Greasy Fork is available in English.

LeetCode Assistant

【使用前先看介绍/有问题可反馈】力扣助手 (LeetCode Assistant):为力扣页面增加辅助功能。

  1. // ==UserScript==
  2. // @name LeetCode Assistant
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0.9
  5. // @description 【使用前先看介绍/有问题可反馈】力扣助手 (LeetCode Assistant):为力扣页面增加辅助功能。
  6. // @author cc
  7. // @require https://cdn.bootcss.com/jquery/3.4.1/jquery.js
  8. // @require https://greatest.deepsurf.us/scripts/422854-bubble-message.js
  9. // @require https://greatest.deepsurf.us/scripts/432416-statement-parser.js
  10. // @match https://leetcode.cn/problems/*
  11. // @match https://leetcode.cn/problemset/*
  12. // @match https://leetcode.cn/company/*/*
  13. // @match https://leetcode.cn/problem-list/*/*
  14. // @grant GM_setValue
  15. // @grant GM_getValue
  16. // ==/UserScript==
  17. // noinspection JSUnresolvedFunction
  18.  
  19. (function() {
  20. const __VERSION__ = '1.0.8';
  21. let executing = false;
  22. const bm = new BubbleMessage();
  23. bm.config.width = 400;
  24.  
  25. const config = {
  26. recommendVisible: false,
  27. autoAdjustView: true,
  28. __hideAnsweredQuestion: false,
  29. __hideCollectionAnsweredQuestion: false,
  30. __supportLanguage: ['Java', 'C++', 'Python3', 'JavaScript'],
  31. };
  32.  
  33. const Basic = {
  34. updateData: function(obj) {
  35. let data = GM_getValue('data');
  36. if (!obj) {
  37. // 初始化调用
  38. if (!data) {
  39. // 未初始化
  40. data = {};
  41. Object.assign(data, config);
  42. GM_setValue('data', data);
  43. } else {
  44. // 已初始化,检查是否存在更新脚本后未添加的值
  45. let isModified = false;
  46. for (let key in config) {
  47. if (data[key] === undefined) {
  48. isModified = true;
  49. data[key] = config[key];
  50. }
  51. }
  52. // 双下划綫开头的属性删除掉,因为不需要保存
  53. for (let key in data) {
  54. if (key.startsWith('__')) {
  55. isModified = true;
  56. delete data[key];
  57. }
  58. }
  59. if (isModified)
  60. GM_setValue('data', data);
  61. Object.assign(config, data);
  62. }
  63. } else {
  64. // 更新调用
  65. Object.assign(config, obj);
  66. Object.assign(data, config);
  67. GM_setValue('data', data);
  68. }
  69. },
  70. listenHistoryState: function() {
  71. const _historyWrap = function(type) {
  72. const orig = history[type];
  73. const e = new Event(type);
  74. return function() {
  75. const rv = orig.apply(this, arguments);
  76. e.arguments = arguments;
  77. window.dispatchEvent(e);
  78. return rv;
  79. };
  80. };
  81. history.pushState = _historyWrap('pushState');
  82. window.addEventListener('pushState', () => {
  83. if (!executing) {
  84. executing = true;
  85. main();
  86. }
  87. });
  88. },
  89. observeChildList: function(node, callback) {
  90. let observer = new MutationObserver(function(mutations) {
  91. mutations.forEach((mutation) => {
  92. if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
  93. callback([...mutation.addedNodes]);
  94. }
  95. });
  96. });
  97. observer.observe(node, { childList: true });
  98. },
  99. executeUtil: function(task, cond, args, thisArg, timeout) {
  100. args = args || [];
  101. timeout = timeout || 250;
  102. if (cond()) {
  103. task.apply(thisArg, args);
  104. } else {
  105. setTimeout(() => {
  106. Basic.executeUtil(task, cond, args, thisArg, timeout);
  107. }, timeout);
  108. }
  109. }
  110. };
  111.  
  112. const Switch = {
  113. setSwitch: function(container, id_, onchange, text, defaultChecked) {
  114. if (defaultChecked === undefined)
  115. defaultChecked = true;
  116. container.style = 'display: inline-flex; align-items: center; margin-left: 10px;';
  117. let switchCheckbox = document.createElement('input');
  118. switchCheckbox.type = 'checkbox';
  119. switchCheckbox.checked = defaultChecked;
  120. switchCheckbox.setAttribute('id', id_);
  121. switchCheckbox.addEventListener('change', onchange);
  122. let switchLabel = document.createElement('label');
  123. switchLabel.setAttribute('for', id_);
  124. switchLabel.innerText = text;
  125. switchLabel.style.marginLeft = '5px';
  126. switchLabel.setAttribute('style', 'margin-left: 5px; cursor: default;')
  127. container.appendChild(switchCheckbox);
  128. container.appendChild(switchLabel);
  129. },
  130. switchVisible: function(nodes, visible, defaultDisplay) {
  131. defaultDisplay = defaultDisplay || '';
  132. if (visible) {
  133. nodes.forEach(node => node.style.display = defaultDisplay);
  134. } else {
  135. nodes.forEach(node => node.style.display = 'none');
  136. }
  137. },
  138. switchRecommendVisible: function() {
  139. let nodes = [];
  140. let target = document.querySelector('.border-divider-border-2');
  141. while (target) {
  142. nodes.push(target);
  143. target = target.previousElementSibling;
  144. }
  145. let sidebar = document.querySelector('.col-span-4:nth-child(2)');
  146. target = sidebar.querySelector('.space-y-4:nth-child(2)');
  147. while (target) {
  148. nodes.push(target);
  149. target = target.nextElementSibling;
  150. }
  151. Switch.switchVisible(nodes, config.recommendVisible);
  152. Basic.observeChildList(sidebar, (nodes) => {
  153. Switch.switchVisible(nodes, config.recommendVisible);
  154. });
  155. },
  156. switchAnsweredQuestionVisible: function() {
  157. let rowGroup = document.querySelector('[role=rowgroup]');
  158. let nodes = [...rowGroup.querySelectorAll('[role=row]')];
  159. let matchPage = location.href.match(/\?page=(\d+)/);
  160. if (!matchPage || parseInt(matchPage[1]) === 1)
  161. nodes = nodes.slice(1);
  162. nodes = nodes.filter(node => node.querySelector('svg.text-green-s'));
  163. Switch.switchVisible(nodes, !config.__hideAnsweredQuestion, 'flex');
  164. },
  165. switchCollectionAnsweredQuestionVisible: function() {
  166. let nodes = [...document.querySelectorAll('.ant-table-tbody>tr')];
  167. nodes = nodes.filter(node => {
  168. let svg = node.querySelector('svg');
  169. return svg.getAttribute('color').includes('success');
  170. });
  171. Switch.switchVisible(nodes, !config.__hideCollectionAnsweredQuestion);
  172. }
  173. };
  174.  
  175. const Insert = {
  176. base: {
  177. insertStyle: function() {
  178. if (document.getElementById('leetcode-assistant-style'))
  179. return;
  180. let style = document.createElement('style');
  181. style.setAttribute('id', 'leetcode-assistant-style');
  182. style.innerText = `
  183. .leetcode-assistant-copy-example-button {
  184. border: 1px solid;
  185. border-radius: 2px;
  186. cursor: pointer;
  187. padding: 1px 4px;
  188. font-size: 0.8em;
  189. margin-top: 5px;
  190. width: fit-content;
  191. }
  192. .leetcode-assistant-highlight-accept-submission {
  193. font-weight: bold;
  194. }`;
  195. document.body.appendChild(style);
  196. },
  197. insertTextarea: function() {
  198. let textarea = document.createElement('textarea');
  199. textarea.setAttribute('id', 'leetcode-assistant-textarea');
  200. textarea.setAttribute('style', 'width: 0; height: 0;')
  201. document.body.appendChild(textarea);
  202. }
  203. },
  204. copy: {
  205. insertCopyStructCode: function() {
  206. const id_ = 'leetcode-assistant-copy-struct-button';
  207. if (document.getElementById(id_)) {
  208. executing = false;
  209. return;
  210. }
  211. let buttonContainer = document.querySelector('[class^=first-section-container]');
  212. let ref = buttonContainer.querySelector('button:nth-child(2)');
  213. let button = document.createElement('button');
  214. button.setAttribute('id', id_);
  215. button.className = ref.className;
  216. let span = document.createElement('span');
  217. span.className = ref.lastElementChild.className;
  218. span.innerText = '复制结构';
  219. button.appendChild(span);
  220. button.addEventListener('click', Copy.copyClassStruct);
  221. buttonContainer.appendChild(button);
  222. executing = false;
  223. },
  224. insertCopySubmissionCode: function() {
  225. let tbody = document.querySelector('.ant-table-tbody');
  226. let trs = [...tbody.querySelectorAll('tr')];
  227. let processTr = (tr) => {
  228. let qid = tr.dataset.rowKey;
  229. Basic.executeUtil((tr) => {
  230. let cell = tr.querySelector(':nth-child(4)');
  231. cell.title = '点击复制代码';
  232. cell.style = 'cursor: pointer; color: #007aff';
  233. cell.addEventListener('click', function() {
  234. XHR.requestCode(qid);
  235. });
  236. cell.setAttribute('data-set-copy', 'true');
  237. }, () => {
  238. let cell = tr.querySelector(':nth-child(4)');
  239. return cell && cell.dataset.setCopy !== 'true';
  240. }, [tr]);
  241. }
  242. trs.forEach(processTr);
  243. Fun.highlightBestAcceptSubmission();
  244. Basic.observeChildList(tbody, (nodes) => {
  245. let node = nodes[0];
  246. if (node.tagName === 'TR') {
  247. processTr(node);
  248. Fun.highlightBestAcceptSubmission();
  249. }
  250. });
  251. executing = false;
  252. },
  253. insertCopyExampleInput: function() {
  254. // 检查是否添加 "复制示例代码" 按钮
  255. let content = document.querySelector('[data-key=description-content] [class^=content] .notranslate');
  256. if (content.dataset.addedCopyExampleInputButton === 'true')
  257. return;
  258. // 对每个 example 添加复制按钮
  259. let examples = [...content.querySelectorAll('pre')];
  260. for (let example of examples) {
  261. let btn = document.createElement('div');
  262. btn.innerText = '复制示例输入';
  263. btn.className = 'leetcode-assistant-copy-example-button';
  264. btn.addEventListener('click', () => {
  265. Copy.copyExampleInput(example);
  266. });
  267. example.appendChild(btn);
  268. }
  269. content.setAttribute('data-added-copy-example-input-button', 'true');
  270. executing = false;
  271. },
  272. insertCopyTestInput: function() {
  273. function addCopyTestInputForInputInfo(inputInfo) {
  274. inputInfo = inputInfo || document.querySelector('[class^=result-container] [class*=ValueContainer]');
  275. if (inputInfo && inputInfo.dataset.setCopy !== 'true') {
  276. inputInfo.addEventListener('click', function() {
  277. // 检查是否支持语言
  278. let lang = Get.getLanguage();
  279. if (!config.__supportLanguage.includes(lang)) {
  280. bm.message({
  281. type: 'warning',
  282. message: '目前不支持该语言的测试输入代码复制',
  283. duration: 1500,
  284. });
  285. executing = false;
  286. return;
  287. }
  288. // 主要代码
  289. let sp = new StatementParser(lang);
  290. let expressions = this.innerText.trim().split('\n');
  291. let declares = sp.getDeclaresFromCode(Get.getCode());
  292. let statements = sp.getStatementsFromDeclaresAndExpressions(declares, expressions);
  293. Copy.copy(statements);
  294. });
  295. inputInfo.setAttribute('data-set-copy', 'true');
  296. }
  297. }
  298. let submissions = document.querySelector('[class^=submissions]');
  299. submissions.addEventListener('DOMNodeInserted', function(event) {
  300. if (event.target.className.startsWith('container') || event.target.className.includes('Container')) {
  301. Basic.executeUtil((container) => {
  302. let inputInfo = container.querySelector('[class*=ValueContainer]');
  303. addCopyTestInputForInputInfo(inputInfo);
  304. }, () => {
  305. return event.target.querySelector('[class*=ValueContainer]');
  306. }, [event.target]);
  307. }
  308. });
  309. addCopyTestInputForInputInfo();
  310. executing = false;
  311. },
  312. },
  313. switch: {
  314. insertRecommendVisibleSwitch: function() {
  315. const id_ = 'leetcode-assistant-recommend-visible-switch';
  316. if (document.getElementById(id_)) {
  317. executing = false;
  318. return;
  319. }
  320. let container = document.querySelector('.relative.space-x-5').nextElementSibling;
  321. let onchange = function() {
  322. Basic.updateData({ recommendVisible: !this.checked });
  323. Switch.switchRecommendVisible();
  324. };
  325. let text = '简洁模式';
  326. Switch.setSwitch(container, id_, onchange, text);
  327. executing = false;
  328. },
  329. insertHideAnsweredQuestionSwitch: function() {
  330. const id_ = 'leetcode-assistant-hide-answered-question-switch';
  331. if (document.getElementById(id_)) {
  332. executing = false;
  333. return;
  334. }
  335. let container = document.createElement('div');
  336. document.querySelector('.relative.space-x-5').parentElement.appendChild(container);
  337. let onchange = function() {
  338. config.__hideAnsweredQuestion = !config.__hideAnsweredQuestion;
  339. Switch.switchAnsweredQuestionVisible();
  340. };
  341. let text = '隐藏已解决';
  342. Switch.setSwitch(container, id_, onchange, text, false);
  343. Basic.executeUtil(() => {
  344. let btns = [...document.querySelectorAll('[role=navigation] button')];
  345. btns.forEach(btn => {
  346. btn.addEventListener("click", function() {
  347. document.getElementById(id_).checked = false;
  348. config.__hideAnsweredQuestion = false;
  349. Switch.switchAnsweredQuestionVisible();
  350. return true;
  351. });
  352. });
  353. }, () => {
  354. let btns = [...document.querySelectorAll('[role=navigation] button')];
  355. return btns.length > 0;
  356. });
  357. executing = false;
  358. },
  359. insertHideCollectionAnsweredQuestionSwitch: function() {
  360. const id_ = 'leetcode-assistant-hide-collection-answered-question-switch';
  361. if (document.getElementById(id_)) {
  362. executing = false;
  363. return;
  364. }
  365. let container = document.createElement('div');
  366. document.querySelector('#lc-header>nav>ul:first-child').appendChild(container);
  367. let onchange = function() {
  368. config.__hideCollectionAnsweredQuestion = !config.__hideCollectionAnsweredQuestion;
  369. Switch.switchCollectionAnsweredQuestionVisible();
  370. };
  371. let text = '隐藏已解决';
  372. Switch.setSwitch(container, id_, onchange, text, false);
  373. Basic.executeUtil(() => {
  374. let btns = [...document.querySelectorAll('.ant-table-pagination li>*')];
  375. btns = btns.filter(btn => btn.tagName === 'BUTTON' || btn.tagName === 'A');
  376. btns.forEach(btn => {
  377. btn.addEventListener('click', function() {
  378. document.getElementById(id_).checked = false;
  379. config.__hideCollectionAnsweredQuestion = false;
  380. Switch.switchCollectionAnsweredQuestionVisible();
  381. return true;
  382. });
  383. });
  384. }, () => {
  385. let btns = [...document.querySelectorAll('.ant-pagination-item')];
  386. return btns.length > 0;
  387. });
  388. executing = false;
  389. },
  390. insertAutoAdjustViewSwitch: function() {
  391. const id_ = 'leetcode-assistant-auto-adjust-view-switch';
  392. if (document.getElementById(id_)) {
  393. executing = false;
  394. return;
  395. }
  396. let container = document.querySelector('[data-status] nav > ul');
  397. let onchange = function() {
  398. Basic.updateData({ autoAdjustView: this.checked });
  399. };
  400. let text = '自动调节视图';
  401. Switch.setSwitch(container, id_, onchange, text);
  402. executing = false;
  403. }
  404. }
  405. };
  406.  
  407. const Copy = {
  408. copy: function(value) {
  409. let textarea = document.getElementById('leetcode-assistant-textarea');
  410. textarea.value = value;
  411. textarea.setAttribute('value', value);
  412. textarea.select();
  413. document.execCommand('copy');
  414. bm.message({
  415. type: 'success',
  416. message: '复制成功',
  417. duration: 1500,
  418. });
  419. },
  420. copyClassStruct: function() {
  421. // 检查语言是否支持
  422. let lang = Get.getLanguage();
  423. if (!config.__supportLanguage.includes(lang)) {
  424. bm.message({
  425. type: 'warning',
  426. message: '目前不支持该语言的结构类代码复制',
  427. duration: 1500,
  428. });
  429. executing = false;
  430. return;
  431. }
  432. // 主要代码
  433. let sp = new StatementParser(lang);
  434. let classStructCode = sp.getClassStructFromCode(Get.getCode());
  435. if (!classStructCode) {
  436. bm.message({
  437. type: 'warning',
  438. message: '结构类代码不存在',
  439. duration: 1500,
  440. });
  441. return;
  442. }
  443. Copy.copy(classStructCode);
  444. },
  445. copyExampleInput: function(example) {
  446. // 检查语言是否支持
  447. let lang = Get.getLanguage();
  448. if (!config.__supportLanguage.includes(lang)) {
  449. bm.message({
  450. type: 'warning',
  451. message: '目前不支持该语言的示例输入代码复制',
  452. duration: 1500,
  453. });
  454. executing = false;
  455. return;
  456. }
  457. let sp = new StatementParser(lang);
  458. // 获取 declares
  459. let declares = sp.getDeclaresFromCode(Get.getCode());
  460. // 获取 expressions
  461. let strong = example.querySelector('strong');
  462. let inputText = "";
  463. if (strong && strong.nextSibling) {
  464. let inputTextElement = strong.nextSibling;
  465. while ((inputTextElement instanceof Text) || !['STRONG', 'B'].includes(inputTextElement.tagName)) {
  466. if (inputTextElement instanceof Text) {
  467. inputText += inputTextElement.wholeText;
  468. } else {
  469. inputText += inputTextElement.innerText;
  470. }
  471. inputTextElement = inputTextElement.nextSibling;
  472. }
  473. } else {
  474. inputText = example.innerText.replace(/\n/g, '').match(/输入:(.+)输出:/)[1];
  475. }
  476. let expressions = inputText.trim().replace(/,$/, '');
  477. if (inputText.replace(/".+?"/g, '').includes(',')) {
  478. // 无视字符串后存在逗号分隔符,说明有多个输入
  479. expressions = expressions.split(/,\s+/);
  480. } else {
  481. // 单个输入
  482. expressions = [expressions];
  483. }
  484. // 生成语句并复制
  485. Copy.copy(sp.getStatementsFromDeclaresAndExpressions(declares, expressions));
  486. },
  487. };
  488.  
  489. const XHR = {
  490. requestCode: function(qid) {
  491. let query = `
  492. query mySubmissionDetail($id: ID!) {
  493. submissionDetail(submissionId: $id) {
  494. id
  495. code
  496. runtime
  497. memory
  498. rawMemory
  499. statusDisplay
  500. timestamp
  501. lang
  502. passedTestCaseCnt
  503. totalTestCaseCnt
  504. sourceUrl
  505. question {
  506. titleSlug
  507. title
  508. translatedTitle
  509. questionId
  510. __typename
  511. }
  512. ... on GeneralSubmissionNode {
  513. outputDetail {
  514. codeOutput
  515. expectedOutput
  516. input
  517. compileError
  518. runtimeError
  519. lastTestcase
  520. __typename
  521. }
  522. __typename
  523. }
  524. submissionComment {
  525. comment
  526. flagType
  527. __typename
  528. }
  529. __typename
  530. }
  531. }`;
  532. $.ajax({
  533. url: 'https://leetcode.cn/graphql/',
  534. method: 'POST',
  535. contentType: 'application/json',
  536. data: JSON.stringify({
  537. operationName: 'mySubmissionDetail',
  538. query: query,
  539. variables: {
  540. id: qid
  541. },
  542. }),
  543. }).then(res => {
  544. Copy.copy(res.data.submissionDetail.code);
  545. });
  546. }
  547. };
  548.  
  549. const Get = {
  550. getLanguage: function() {
  551. return document.getElementById('lang-select').innerText;
  552. },
  553. getCode: function() {
  554. return document.querySelector('[name=code]').value;
  555. }
  556. };
  557.  
  558. const Fun = {
  559. adjustViewScale: function(left, right) {
  560. if (!config.autoAdjustView) {
  561. executing = false;
  562. return;
  563. }
  564. let splitLine = document.querySelector('[data-is-collapsed]');
  565. let leftPart = splitLine.previousElementSibling;
  566. let rightPart = splitLine.nextElementSibling;
  567. let leftPartFlex = leftPart.style.flex.match(/\d+\.\d+/)[0];
  568. let rightPartFlex = rightPart.style.flex.match(/\d+\.\d+/)[0];
  569. leftPart.style.flex = leftPart.style.flex.replace(leftPartFlex, `${left}`);
  570. rightPart.style.flex = rightPart.style.flex.replace(rightPartFlex, `${right}`);
  571. executing = false;
  572. },
  573. highlightBestAcceptSubmission: function() {
  574. let highlightClassName = 'leetcode-assistant-highlight-accept-submission';
  575. let items = [...document.querySelectorAll('tr[data-row-key]')];
  576. let acItems = items.filter(item => item.querySelector('a[class^=ac]'));
  577. if (acItems.length === 0)
  578. return;
  579. let matchTimeMem = acItems.map(item => item.innerText.match(/(\d+)\sms.+?(\d+\.?\d)\sMB/).slice(1, 3));
  580. let timeList = matchTimeMem.map(res => parseInt(res[0]));
  581. let memList = matchTimeMem.map(res => parseFloat(res[1]));
  582. let targetIndex = 0;
  583. for (let i = 0; i < items.length; i++) {
  584. if (timeList[i] < timeList[targetIndex] || (timeList[i] === timeList[targetIndex] && memList[i] < memList[targetIndex])) {
  585. targetIndex = i;
  586. }
  587. }
  588. let lastTarget = document.querySelector(`.${highlightClassName}`);
  589. if (lastTarget)
  590. lastTarget.classList.remove(highlightClassName);
  591. acItems[targetIndex].classList.add(highlightClassName);
  592. }
  593. };
  594.  
  595. function main() {
  596. console.log(`LeetCode Assistant version ${__VERSION__}`);
  597. if (location.href.match(/\/problems\/[a-zA-Z0-9\-]+\//)) { // /problems/*
  598. Basic.executeUtil(() => {
  599. Insert.copy.insertCopyStructCode();
  600. Insert.switch.insertAutoAdjustViewSwitch();
  601. }, () => {
  602. return document.querySelector('[class^=first-section-container]');
  603. });
  604. if (location.href.match(/\/problems\/[a-zA-Z0-9\-]+\/$/)) { // 题目描述
  605. Fun.adjustViewScale(0.618, 0.382);
  606. Basic.executeUtil(Insert.copy.insertCopyExampleInput, () => {
  607. let codeDOM = document.querySelector('.editor-scrollable');
  608. let content = document.querySelector('[data-key=description-content] [class^=content] .notranslate');
  609. return codeDOM && content && content.querySelector('pre');
  610. });
  611. } else if (location.href.includes('/solution/')) { // 题解
  612. Fun.adjustViewScale(0.382, 0.618);
  613. } else if (location.href.includes('/submissions/')) { // 提交记录
  614. Basic.executeUtil(() => {
  615. Insert.copy.insertCopySubmissionCode();
  616. Insert.copy.insertCopyTestInput();
  617. }, () => {
  618. return document.querySelector('.ant-table-thead');
  619. });
  620. }
  621. } else if (location.href.startsWith('https://leetcode.cn/problemset/')) { // 首页
  622. Insert.switch.insertRecommendVisibleSwitch();
  623. Switch.switchRecommendVisible();
  624. Basic.executeUtil(() => {
  625. Insert.switch.insertHideAnsweredQuestionSwitch();
  626. Switch.switchAnsweredQuestionVisible();
  627. }, () => {
  628. let navigation = document.querySelector('[role=navigation]');
  629. return navigation && navigation.innerText.length > 0;
  630. });
  631. } else if (location.href.startsWith('https://leetcode.cn/problem-list/') || location.href.startsWith('https://leetcode.cn/company/')) { // 集合类型问题列表页
  632. Insert.switch.insertHideCollectionAnsweredQuestionSwitch();
  633. Basic.executeUtil(() => {
  634. Insert.switch.insertHideCollectionAnsweredQuestionSwitch();
  635. Switch.switchCollectionAnsweredQuestionVisible();
  636. }, () => {
  637. let navigation = document.querySelector('#lc-header>nav>ul');
  638. return navigation && navigation.childElementCount > 0;
  639. });
  640. } else {
  641. executing = false;
  642. }
  643. }
  644.  
  645. window.addEventListener('load', () => {
  646. Basic.updateData();
  647. Insert.base.insertStyle();
  648. Insert.base.insertTextarea();
  649. Basic.listenHistoryState();
  650. main();
  651. });
  652. })();