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.1
  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.1";
  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. status = "<span style='color: red;'>Нет соединения</span>";
  520. } else if (chosen_answer == correct) {
  521. status = "<span style='color: green;'>Верно</span>";
  522. new_answer_f = false;
  523. } else if (incorrect.includes(chosen_answer)) {
  524. status = "<span style='color: red;'>Неверно</span>";
  525. new_answer_f = false;
  526. }
  527. GM_setValue('new_answer_f', prev_new_answer_f || new_answer_f);
  528. var pboxes = document.getElementsByTagName('p');
  529. const display_answer = config.get('display_answer');
  530. for (i = 0; i < pboxes.length; i++) {
  531. pbox = pboxes[i];
  532. if (pbox.textContent.includes("Вопрос:")) {
  533. pbox.innerHTML = "<i>(Вариант <input onfocus='this.select();' id='variant' value='" + hash + (display_answer == true ? (" " + chosen_answer) : "") + "' readonly>)</i>";
  534. if (config.get('server') != '') {
  535. pbox.innerHTML += "<br>Статус: " + status;
  536. }
  537. pbox.innerHTML += "<br>Вопрос:";
  538. break;
  539. }
  540. }
  541. var question_num = undefined;
  542. for (i = 0; i < pboxes.length; i++) {
  543. pbox = pboxes[i];
  544. if (pbox.textContent.includes("Текущий вопрос: ")) {
  545. question_num = pbox.textContent.slice(variant.indexOf("Текущий вопрос: ") + 16).trim();
  546. break;
  547. }
  548. }
  549. if (hash !== undefined) {
  550. var tests = GM_getValue('tests', new Object());
  551. if (tests[testID] === undefined) {
  552. tests[testID] = new Object();
  553. }
  554. tests[testID][hash] = [question_num, chosen_answer, answers.length];
  555. GM_setValue('tests', tests);
  556. }
  557. }
  558.  
  559. function auto_answer() {
  560. var answer, sorry_val, correct_element, incorrect_element;
  561. const auto_answer = config.get('auto_answer');
  562. if (correct != undefined) {
  563. switch (type) {
  564. case 'radio': {
  565. for (answer in answers) {
  566. if (answers[answer].sorry_value == correct) {
  567. if (!answers[answer].sorry_colored) {
  568. correct_element = answers[answer].parentNode;
  569. sorry_val = answers[answer].sorry_value;
  570. correct_element.innerHTML = "<span style='color: green;'>" + correct_element.innerHTML + "</span>";
  571. answers[answer] = correct_element.getElementsByTagName('input')[0];
  572. answers[answer].sorry_value = sorry_val;
  573. answers[answer].sorry_colored = true;
  574. }
  575. answers[answer].click();
  576. }
  577. }
  578. } break;
  579. case 'checkbox': {
  580. for (answer in answers) {
  581. if (correct.includes(answers[answer].sorry_value)) {
  582. if (!answers[answer].sorry_colored) {
  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. answers[answer].click();
  591. }
  592. }
  593. } break;
  594. case 'text': {
  595. var corr = correct.slice(1, correct.length - 1).split('][');
  596. for (i = 0; (i < answers.leght) && (i < corr.length); i++) {
  597. answers[i].placeholder = corr[i];
  598. }
  599. } break;
  600. }
  601. } else if (auto_answer == labels.auto_answer_random) {
  602. switch (type) {
  603. case 'radio': {
  604. var possible_answers = [];
  605. for (answer in answers) {
  606. if (incorrect.includes(answers[answer].sorry_value) == false) {
  607. possible_answers.push(answer);
  608. } else {
  609. if (!answers[answer].sorry_colored) {
  610. incorrect_element = answers[answer].parentNode;
  611. sorry_val = answers[answer].sorry_value;
  612. incorrect_element.innerHTML = "<span style='color: red;'>" + incorrect_element.innerHTML + "</span>";
  613. answers[answer] = incorrect_element.getElementsByTagName('input')[0];
  614. answers[answer].sorry_value = sorry_val;
  615. answers[answer].sorry_colored = true;
  616. }
  617. }
  618. }
  619. var chosen_answer;
  620. chosen_answer = Math.floor(Math.random() * possible_answers.length);
  621. answers[possible_answers[chosen_answer]].click();
  622. } break;
  623. case 'checkbox': {
  624. var combs = comb(charset.slice(0, answers.length), incorrect);
  625. var pick = combs[Math.floor(Math.random() * combs.length)];
  626. for (i = 0; i < answers.length; i++) {
  627. if(pick.includes(answers[i].sorry_value)) {
  628. answers[i].click();
  629. }
  630. }
  631. } break;
  632. }
  633. } else if (auto_answer == labels.auto_answer_first) {
  634. Object.values(sorted_objects_value)[0].click();
  635. }
  636. }
  637.  
  638. function parse_server_data(server_data) {
  639. version = server_data.version;
  640. if (version !== VERSION && version !== undefined) {
  641. console.warn("Sorryops is outdated");
  642. server_data = {};
  643. GM_openInTab("https://greasyfork.org/en/scripts/481036-sorryops");
  644. config.set('auto_continue', false);
  645. }
  646. if (server_data.hasOwnProperty('correct')) {
  647. correct = server_data.correct;
  648. if (correct === undefined) {
  649. correct = undefined;
  650. } else if (correct.hasOwnProperty(hash)) {
  651. correct = correct[hash];
  652. } else {
  653. correct = undefined;
  654. }
  655. } else {
  656. correct = undefined;
  657. }
  658. if (server_data.hasOwnProperty('incorrect')) {
  659. incorrect = server_data.incorrect;
  660. if (incorrect === undefined) {
  661. incorrect = [];
  662. } else if (incorrect.hasOwnProperty(hash)) {
  663. incorrect = incorrect[hash];
  664. } else {
  665. incorrect = [];
  666. }
  667. } else {
  668. incorrect = [];
  669. }
  670. }
  671.  
  672. function auto_continue() {
  673. var old_time, cur_time;
  674. if (config.get('auto_continue')) {
  675. old_time = config.get('auto_continue_time');
  676. cur_time = Date.now();
  677. if (cur_time - old_time > 60 * 60 * 1000) {
  678. config.set('auto_continue', false);
  679. } else {
  680. press_continue_btn();
  681. }
  682. }
  683. }
  684.  
  685. function auto_restart() {
  686. var old_time, cur_time;
  687. if (config.get('auto_restart')) {
  688. old_time = config.get('auto_restart_time');
  689. cur_time = Date.now();
  690. if (cur_time - old_time > 60 * 60 * 1000) {
  691. config.set('auto_restart', false);
  692. } else {
  693. press_continue_btn();
  694. }
  695. }
  696. }
  697.  
  698. /* End Functions */
  699.  
  700. /* Handlers */
  701.  
  702. function test_form_handler() {
  703. var i, key, answer, sorry_val;
  704. var complicated_hash_f = false;
  705. var boxes = [];
  706. var sorted_objects;
  707. var objects_hash = new Object();
  708. var objects_value = new Object();
  709. var form = document.getElementById('testform-answer');
  710. var manual_form = document.getElementById('testform-answer-0');
  711. if (form != null) {
  712. boxes = form.getElementsByTagName('input');
  713. } else if (manual_form != null) {
  714. i = 1;
  715. while (manual_form != null) {
  716. boxes.push(manual_form);
  717. manual_form = document.getElementById('testform-answer-' + i++);
  718. }
  719. }
  720. type = boxes[0].type;
  721. for (i = 0; i < boxes.length; i++) {
  722. if (boxes[i].parentNode.innerHTML.includes("<img")) {
  723. complicated_hash_f = true;
  724. break;
  725. }
  726. }
  727. switch (type) {
  728. case 'checkbox':
  729. case 'radio': {
  730. for (i = 0; i < boxes.length; i++) {
  731. boxes[i].hash = hashCode(complicated_hash_f ? boxes[i].parentNode.innerHTML : boxes[i].parentNode.innerText, true);
  732. objects_hash[boxes[i].hash] = boxes[i];
  733. objects_value[boxes[i].value] = boxes[i];
  734. }
  735. const sorted_objects_hash = Object.keys(objects_hash).sort().reduce(
  736. (obj, key) => {
  737. obj[key] = objects_hash[key];
  738. return obj;
  739. }, {}
  740. );
  741. sorted_objects_value = Object.keys(objects_value).sort().reduce(
  742. (obj, key) => {
  743. obj[key] = objects_value[key];
  744. return obj;
  745. }, {}
  746. );
  747. i = 0;
  748. sorted_objects = sorted_objects_hash;
  749. for (key in sorted_objects) {
  750. sorted_objects[key].parentNode.remove();
  751. form.appendChild(sorted_objects[key].parentNode);
  752. }
  753. calculate_variant_hash();
  754. for (key in sorted_objects) {
  755. sorted_objects[key].sorry_value = charset[i++];
  756. var span = document.createElement('span');
  757. var disp_val;
  758. switch (config.get('display_values')) {
  759. case labels.display_values_ori:
  760. disp_val = sorted_objects[key].value;
  761. break;
  762. case labels.display_values_sorry:
  763. disp_val = sorted_objects[key].sorry_value;
  764. break;
  765. case labels.display_values_both:
  766. disp_val = sorted_objects[key].value + ":" + sorted_objects[key].sorry_value;
  767. break;
  768. }
  769. span.innerHTML = disp_val + ") ";
  770. sorted_objects[key].parentNode.insertBefore(span, sorted_objects[key]);
  771. answers.push(sorted_objects[key]);
  772. }
  773. if (config.get('display_values') == labels.display_values_ori) {
  774. for (key in sorted_objects_value) {
  775. sorted_objects_value[key].parentNode.remove();
  776. form.appendChild(sorted_objects_value[key].parentNode);
  777. }
  778. }
  779. } break;
  780. case 'text': {
  781. answers = boxes;
  782. calculate_variant_hash();
  783. } break;
  784. }
  785. auto_answer();
  786. update_variant();
  787. for (i = 0; i < answers.length; i++) {
  788. answers[i].addEventListener('change', update_variant);
  789. }
  790. }
  791.  
  792. function result_page_handler() {
  793. var i;
  794. var correct_num = variant.slice(variant.indexOf("Число верных ответов: ") + 22);
  795. var all_num = variant.slice(variant.indexOf("Число неверных ответов: ") + 24);
  796. correct_num = correct_num.slice(0, correct_num.indexOf("\n")).trim();
  797. all_num = all_num.slice(0, all_num.indexOf("\n")).trim();
  798. all_num = (parseInt(all_num) + parseInt(correct_num)).toString();
  799. var test = GM_getValue('tests', new Object())[testID];
  800. if (test === undefined) {
  801. return;
  802. }
  803. var printer = "";
  804. var sorted_test = [];
  805. for (var hash in test) {
  806. sorted_test.push([hash].concat(test[hash]));
  807. }
  808. sorted_test.sort((a, b) => {return a[1] - b[1]});
  809. for (i = 0; i < sorted_test.length; i++) {
  810. printer += (config.get('append_question_number') ? (sorted_test[i][1] + ") ") : "") + sorted_test[i][0] + " " + sorted_test[i][2] + "\n";
  811. }
  812. printer += correct_num;
  813. if (config.get('copy_answers')) {
  814. GM_setClipboard(printer);
  815. }
  816. if (config.get('accumulator_enable')) {
  817. var acc = GM_getValue('accumulated_answers', "");
  818. if (acc != "") {
  819. acc += "\n\n";
  820. }
  821. var prefix = testID;
  822. if (prefix != "") {
  823. acc += prefix + "\n";
  824. }
  825. acc += printer;
  826. GM_setValue('accumulated_answers', acc);
  827. printer = acc;
  828. }
  829. 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>";
  830. var pboxes = document.getElementsByTagName('p');
  831. for (i = 0; i < pboxes.length; i++) {
  832. var pbox = pboxes[i];
  833. if (pbox.textContent.includes("Попытка ")) {
  834. pbox.outerHTML += printer;
  835. break;
  836. }
  837. }
  838. set_to_clear(testID, () => {
  839. if (GM_getValue('new_answer_f')) {
  840. send_to_server({
  841. type: "test_results",
  842. uid: config.get('user_id'),
  843. id: testID,
  844. answers: sorted_test,
  845. correct: correct_num,
  846. all: all_num,
  847. });
  848. }
  849. GM_setValue('fetched_data', {});
  850. GM_setValue('new_answer_f', false);
  851. });
  852. }
  853.  
  854. /* End Handlers */
  855.  
  856. function main() {
  857. variant = document.getElementById('w0').parentNode.textContent;
  858. prev_new_answer_f = !!GM_getValue('new_answer_f');
  859. if (variant.includes("Вопрос:")) {
  860. fetch_from_server(testID, (server_response) => {
  861. DB_cleaner();
  862. parse_server_data(server_response);
  863. test_form_handler();
  864. if (config.get('wait_server_response')) {
  865. window.setInterval(() => {fetch_from_server(testID, (server_response) => {
  866. if (version === undefined) {
  867. parse_server_data(server_response);
  868. update_variant();
  869. if (version === VERSION) {
  870. press_continue_btn();
  871. auto_continue();
  872. }
  873. }
  874. })}, 3000, true);
  875. } else {
  876. auto_continue();
  877. }
  878. auto_continue();
  879. });
  880. } else if (variant.includes("Результат прохождения теста:")) {
  881. result_page_handler();
  882. auto_restart();
  883. }
  884. }