From 85e87ec717e6c6482ba3eb862d5a69ac1496ce88 Mon Sep 17 00:00:00 2001 From: varun-s22 <93476421+varun-s22@users.noreply.github.com> Date: Wed, 28 Dec 2022 16:45:25 +0530 Subject: [PATCH 1/3] Fixed ReadMe uploads Now the readMe can be easily pushed to github with the new leetcode Here are features which are currently extracted * Problem Title * Problem Link * Problem Statement * Problem Description * Language used by the user * Submission stats --- manifest.json | 8 +- scripts/newleetcode.js | 784 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 790 insertions(+), 2 deletions(-) create mode 100644 scripts/newleetcode.js diff --git a/manifest.json b/manifest.json index 795f250b..a283a2dc 100644 --- a/manifest.json +++ b/manifest.json @@ -23,6 +23,8 @@ "https://api.github.com/*", "https://leetcode.com/*", "https://practice.geeksforgeeks.org/*", + "https://*.leetcode.com/*", + "https://*.leetcode.cn/*", "tabs", "unlimitedStorage", "storage" @@ -32,10 +34,12 @@ "matches": [ "https://leetcode.com/*", "https://github.com/*", - "https://practice.geeksforgeeks.org/*" + "https://practice.geeksforgeeks.org/*", + "https://*.leetcode.com/*", + "https://*.leetcode.cn/*" ], "js": [ - "scripts/leetcode.js", + "scripts/newleetcode.js", "scripts/authorize.js", "scripts/gfg.js" ], diff --git a/scripts/newleetcode.js b/scripts/newleetcode.js new file mode 100644 index 00000000..7d6c2dab --- /dev/null +++ b/scripts/newleetcode.js @@ -0,0 +1,784 @@ +/* Enum for languages supported by LeetCode. */ +const languages = { + Python: '.py', + Python3: '.py', + 'C++': '.cpp', + C: '.c', + Java: '.java', + 'C#': '.cs', + JavaScript: '.js', + Javascript: '.js', + Ruby: '.rb', + Swift: '.swift', + Go: '.go', + Kotlin: '.kt', + Scala: '.scala', + Rust: '.rs', + PHP: '.php', + TypeScript: '.ts', + MySQL: '.sql', + 'MS SQL Server': '.sql', + Oracle: '.sql', +}; + +/* Commit messages */ +const readmeMsg = 'Create README - LeetHub'; +const discussionMsg = 'Prepend discussion post - LeetHub'; +const createNotesMsg = 'Attach NOTES - LeetHub'; +const pattern = /https?:\/\/.*leetcode.com\/problems\/.*\/submissions\/\d*/; + +// problem types +const NORMAL_PROBLEM = 0; +const EXPLORE_SECTION_PROBLEM = 1; + +/* Difficulty of most recenty submitted question */ +let difficulty = ''; + +/* state of upload for progress */ +let uploadState = { uploading: false }; + +/* Util function to check if an element exists */ +function checkElem(elem) { + return elem && elem.length > 0; +} + +/* Get file extension for submission */ +function findLanguage() { + const tag = [ + ...document.getElementsByClassName( + 'ant-select-selection-selected-value', + ), + ...document.getElementsByClassName('Select-value-label'), + ]; + if (tag && tag.length > 0) { + for (let i = 0; i < tag.length; i += 1) { + const elem = tag[i].textContent; + if (elem !== undefined && languages[elem] !== undefined) { + return languages[elem]; // should generate respective file extension + } + } + } + return null; +} + +/* Main function for uploading code to GitHub repo, and callback cb is called if success */ +const upload = ( + token, + hook, + code, + directory, + filename, + sha, + msg, + cb = undefined, +) => { + // To validate user, load user object from GitHub. + const URL = `https://api.github.com/repos/${hook}/contents/${directory}/${filename}`; + + /* Define Payload */ + let data = { + message: msg, + content: code, + sha, + }; + + data = JSON.stringify(data); + + const xhr = new XMLHttpRequest(); + xhr.addEventListener('readystatechange', function () { + if (xhr.readyState === 4) { + if (xhr.status === 200 || xhr.status === 201) { + const updatedSha = JSON.parse(xhr.responseText).content.sha; // get updated SHA. + + chrome.storage.local.get('stats', (data2) => { + let { stats } = data2; + if (stats === null || stats === {} || stats === undefined) { + // create stats object + stats = {}; + stats.solved = 0; + stats.easy = 0; + stats.medium = 0; + stats.hard = 0; + stats.sha = {}; + } + const filePath = directory + filename; + // Only increment solved problems statistics once + // New submission commits twice (README and problem) + if (filename === 'README.md' && sha === null) { + stats.solved += 1; + stats.easy += difficulty === 'Easy' ? 1 : 0; + stats.medium += difficulty === 'Medium' ? 1 : 0; + stats.hard += difficulty === 'Hard' ? 1 : 0; + } + stats.sha[filePath] = updatedSha; // update sha key. + chrome.storage.local.set({ stats }, () => { + console.log( + `Successfully committed ${filename} to github`, + ); + + // if callback is defined, call it + if (cb !== undefined) { + cb(); + } + }); + }); + } + } + }); + xhr.open('PUT', URL, true); + xhr.setRequestHeader('Authorization', `token ${token}`); + xhr.setRequestHeader('Accept', 'application/vnd.github.v3+json'); + xhr.send(data); +}; + +/* Main function for updating code on GitHub Repo */ +/* Currently only used for prepending discussion posts to README */ +/* callback cb is called on success if it is defined */ +const update = ( + token, + hook, + addition, + directory, + msg, + prepend, + cb = undefined, +) => { + const URL = `https://api.github.com/repos/${hook}/contents/${directory}/README.md`; + + /* Read from existing file on GitHub */ + const xhr = new XMLHttpRequest(); + xhr.addEventListener('readystatechange', function () { + if (xhr.readyState === 4) { + if (xhr.status === 200 || xhr.status === 201) { + const response = JSON.parse(xhr.responseText); + const existingContent = decodeURIComponent( + escape(atob(response.content)), + ); + let newContent = ''; + + /* Discussion posts prepended at top of README */ + /* Future implementations may require appending to bottom of file */ + if (prepend) { + newContent = btoa( + unescape(encodeURIComponent(addition + existingContent)), + ); + } + + /* Write file with new content to GitHub */ + upload( + token, + hook, + newContent, + directory, + 'README.md', + response.sha, + msg, + cb, + ); + } + } + }); + xhr.open('GET', URL, true); + xhr.setRequestHeader('Authorization', `token ${token}`); + xhr.setRequestHeader('Accept', 'application/vnd.github.v3+json'); + xhr.send(); +}; + +function uploadGit( + code, + problemName, + fileName, + msg, + action, + prepend = true, + cb = undefined, + _diff = undefined, +) { + // Assign difficulty + if (_diff && _diff !== undefined) { + difficulty = _diff.trim(); + } + + /* Get necessary payload data */ + chrome.storage.local.get('leethub_token', (t) => { + const token = t.leethub_token; + if (token) { + chrome.storage.local.get('mode_type', (m) => { + const mode = m.mode_type; + if (mode === 'commit') { + /* Get hook */ + chrome.storage.local.get('leethub_hook', (h) => { + const hook = h.leethub_hook; + if (hook) { + /* Get SHA, if it exists */ + + /* to get unique key */ + const filePath = problemName + fileName; + chrome.storage.local.get('stats', (s) => { + const { stats } = s; + let sha = null; + + if ( + stats !== undefined && + stats.sha !== undefined && + stats.sha[filePath] !== undefined + ) { + sha = stats.sha[filePath]; + } + + if (action === 'upload') { + /* Upload to git. */ + upload( + token, + hook, + code, + problemName, + fileName, + sha, + msg, + cb, + ); + } else if (action === 'update') { + /* Update on git */ + update( + token, + hook, + code, + problemName, + msg, + prepend, + cb, + ); + } + }); + } + }); + } + }); + } + }); +} + +/* Function for finding and parsing the full code. */ +/* - At first find the submission details url. */ +/* - Then send a request for the details page. */ +/* - Finally, parse the code from the html reponse. */ +/* - Also call the callback if available when upload is success */ +function findCode( + uploadGit, + problemName, + fileName, + msg, + action, + cb = undefined, +) { + /* Get the submission details url from the submission page. */ + var submissionURL; + const e = document.getElementsByClassName('status-column__3SUg'); + if (checkElem(e)) { + // for normal problem submisson + const submissionRef = e[1].innerHTML.split(' ')[1]; + submissionURL = + 'https://leetcode.com' + + submissionRef.split('=')[1].slice(1, -1); + } else { + // for a submission in explore section + const submissionRef = document.getElementById('result-state'); + submissionURL = submissionRef.href; + } + if (submissionURL != undefined) { + /* Request for the submission details page */ + const xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function () { + if (this.readyState == 4 && this.status == 200) { + /* received submission details as html reponse. */ + var doc = new DOMParser().parseFromString( + this.responseText, + 'text/html', + ); + /* the response has a js object called pageData. */ + /* Pagedata has the details data with code about that submission */ + var scripts = doc.getElementsByTagName('script'); + for (var i = 0; i < scripts.length; i++) { + var text = scripts[i].innerText; + if (text.includes('pageData')) { + /* Considering the pageData as text and extract the substring + which has the full code */ + var firstIndex = text.indexOf('submissionCode'); + var lastIndex = text.indexOf('editCodeUrl'); + var slicedText = text.slice(firstIndex, lastIndex); + /* slicedText has code as like as. (submissionCode: 'Details code'). */ + /* So finding the index of first and last single inverted coma. */ + var firstInverted = slicedText.indexOf("'"); + var lastInverted = slicedText.lastIndexOf("'"); + /* Extract only the code */ + var codeUnicoded = slicedText.slice( + firstInverted + 1, + lastInverted, + ); + /* The code has some unicode. Replacing all unicode with actual characters */ + var code = codeUnicoded.replace( + /\\u[\dA-F]{4}/gi, + function (match) { + return String.fromCharCode( + parseInt(match.replace(/\\u/g, ''), 16), + ); + }, + ); + + /* + for a submisssion in explore section we do not get probStat beforehand + so, parse statistics from submisson page + */ + if (!msg) { + slicedText = text.slice( + text.indexOf('runtime'), + text.indexOf('memory'), + ); + const resultRuntime = slicedText.slice( + slicedText.indexOf("'") + 1, + slicedText.lastIndexOf("'"), + ); + slicedText = text.slice( + text.indexOf('memory'), + text.indexOf('total_correct'), + ); + const resultMemory = slicedText.slice( + slicedText.indexOf("'") + 1, + slicedText.lastIndexOf("'"), + ); + msg = `Time: ${resultRuntime}, Memory: ${resultMemory} - LeetHub`; + } + + if (code != null) { + setTimeout(function () { + uploadGit( + btoa(unescape(encodeURIComponent(code))), + problemName, + fileName, + msg, + action, + true, + cb, + ); + }, 2000); + } + } + } + } + }; + + xhttp.open('GET', submissionURL, true); + xhttp.send(); + } +} + +/* Main parser function for the code */ +function parseCode() { + const e = document.getElementsByClassName('CodeMirror-code'); + if (e !== undefined && e.length > 0) { + const elem = e[0]; + let parsedCode = ''; + const textArr = elem.innerText.split('\n'); + for (let i = 1; i < textArr.length; i += 2) { + parsedCode += `${textArr[i]}\n`; + } + return parsedCode; + } + return null; +} + +function convertToSlug(string) { + const a = + 'àáâäæãåāăąçćčđďèéêëēėęěğǵḧîïíīįìłḿñńǹňôöòóœøōõőṕŕřßśšşșťțûüùúūǘůűųẃẍÿýžźż·/_,:;'; + const b = + 'aaaaaaaaaacccddeeeeeeeegghiiiiiilmnnnnoooooooooprrsssssttuuuuuuuuuwxyyzzz------'; + const p = new RegExp(a.split('').join('|'), 'g'); + + return string + .toString() + .toLowerCase() + .replace(/\s+/g, '-') // Replace spaces with - + .replace(p, (c) => b.charAt(a.indexOf(c))) // Replace special characters + .replace(/&/g, '-and-') // Replace & with 'and' + .replace(/[^\w\-]+/g, '') // Remove all non-word characters + .replace(/\-\-+/g, '-') // Replace multiple - with single - + .replace(/^-+/, '') // Trim - from start of text + .replace(/-+$/, ''); // Trim - from end of text +} +function getProblemNameSlug(questionTitle) { + return addLeadingZeros(convertToSlug(questionTitle)); +} + +function addLeadingZeros(title) { + const maxTitlePrefixLength = 4; + var len = title.split('-')[0].length; + if (len < maxTitlePrefixLength) { + return '0'.repeat(4 - len) + title; + } + return title; +} + +/* Parser function for the question and tags */ +function parseQuestion() { + var questionUrl = window.location.href; + questionUrl = questionUrl.split('submissions')[0]; + + let question = document.querySelector('meta[name="description"]'); + if (question) { + question = question.content; + } + + let qArray = question.split(' - '); + question = qArray[1]; + const qbody = question; + + // Problem title. + let qtitle = qArray[0].trim(); + + // Final formatting of the contents of the README for each problem + const markdown = `

${qtitle}


${qbody}`; + return { markdown, title: qtitle }; +} + +/* Parser function for time/space stats */ +function parseStats() { + let probStats = document.getElementsByClassName('data__HC-i'); + if (!checkElem(probStats)) { + probStats = document.getElementsByClassName( + 'text-label-1 dark:text-dark-label-1 ml-2 font-medium', + ); + if (!checkElem(probStats)) { + return null; + } + } + const percentageStats = document.getElementsByClassName( + 'text-white dark:text-dark-white ml-2 rounded-xl px-1.5 font-medium', + ); + let time = probStats[0].textContent; + let timePercentile; + let space; + let spacePercentile; + if (!checkElem(percentageStats)) { + timePercentile = probStats[1].textContent; + space = probStats[2].textContent; + spacePercentile = probStats[3].textContent; + } else { + timePercentile = percentageStats[0].textContent; + space = probStats[1].textContent; + spacePercentile = percentageStats[1].textContent; + } + // Format commit message + return `Time: ${time} (${timePercentile}), Space: ${space} (${spacePercentile}) - LeetHub`; +} + +document.addEventListener('click', (event) => { + const element = event.target; + const oldPath = window.location.pathname; + + /* Act on Post button click */ + /* Complex since "New" button shares many of the same properties as "Post button */ + if ( + element.classList.contains('icon__3Su4') || + element.parentElement.classList.contains('icon__3Su4') || + element.parentElement.classList.contains( + 'btn-content-container__214G', + ) || + element.parentElement.classList.contains('header-right__2UzF') + ) { + setTimeout(function () { + /* Only post if post button was clicked and url changed */ + if ( + oldPath !== window.location.pathname && + oldPath === + window.location.pathname.substring(0, oldPath.length) && + !Number.isNaN(window.location.pathname.charAt(oldPath.length)) + ) { + const date = new Date(); + const currentDate = `${date.getDate()}/${date.getMonth()}/${date.getFullYear()} at ${date.getHours()}:${date.getMinutes()}`; + const addition = `[Discussion Post (created on ${currentDate})](${window.location}) \n`; + const problemName = window.location.pathname.split('/')[2]; // must be true. + + uploadGit( + addition, + problemName, + 'README.md', + discussionMsg, + 'update', + ); + } + }, 1000); + } +}); + +/* function to get the notes if there is any + the note should be opened atleast once for this to work + this is because the dom is populated after data is fetched by opening the note */ +function getNotesIfAny() { + // there are no notes on expore + if (document.URL.startsWith('https://leetcode.com/explore/')) + return ''; + + notes = ''; + if ( + checkElem(document.getElementsByClassName('notewrap__eHkN')) && + checkElem( + document + .getElementsByClassName('notewrap__eHkN')[0] + .getElementsByClassName('CodeMirror-code'), + ) + ) { + notesdiv = document + .getElementsByClassName('notewrap__eHkN')[0] + .getElementsByClassName('CodeMirror-code')[0]; + if (notesdiv) { + for (i = 0; i < notesdiv.childNodes.length; i++) { + if (notesdiv.childNodes[i].childNodes.length == 0) continue; + text = notesdiv.childNodes[i].childNodes[0].innerText; + if (text) { + notes = `${notes}\n${text.trim()}`.trim(); + } + } + } + } + return notes.trim(); +} + +const loader = setInterval(() => { + let code = null; + let probStatement = null; + let probStats = null; + let probType; + let languageDiv = document.querySelector('.leading-4') + ? document.querySelector('.leading-4') + : null; + + const language = languageDiv + ? languages[languageDiv.innerText] + : findLanguage(); + + let currentLink = document.location.href; + let solution = currentLink.match(pattern); + + const successTag = document.getElementsByClassName('success__3Ai7'); + const resultState = document.getElementById('result-state'); + var success = solution ? true : false; + // check success tag for a normal problem + if ( + checkElem(successTag) && + successTag[0].className === 'success__3Ai7' && + successTag[0].innerText.trim() === 'Success' + ) { + console.log(successTag[0]); + success = true; + probType = NORMAL_PROBLEM; + } + + // check success state for a explore section problem + else if ( + resultState && + resultState.className === 'text-success' && + resultState.innerText === 'Accepted' + ) { + success = true; + probType = EXPLORE_SECTION_PROBLEM; + } + if (success) { + probStatement = parseQuestion().markdown; + probStats = parseStats(); + } + + if (probStatement !== null) { + const problemName = getProblemNameSlug(parseQuestion().title); + if (language !== null) { + // start upload indicator here + startUpload(); + chrome.storage.local.get('stats', (s) => { + const { stats } = s; + const filePath = problemName + problemName + language; + let sha = null; + if ( + stats !== undefined && + stats.sha !== undefined && + stats.sha[filePath] !== undefined + ) { + sha = stats.sha[filePath]; + } + + /* Only create README if not already created */ + if (sha === null) { + /* @TODO: Change this setTimeout to Promise */ + uploadGit( + btoa(unescape(encodeURIComponent(probStatement))), + problemName, + 'README.md', + readmeMsg, + 'upload', + ); + } + }); + + /* get the notes and upload it */ + /* only upload notes if there is any */ + notes = getNotesIfAny(); + if (notes.length > 0) { + setTimeout(function () { + if (notes != undefined && notes.length != 0) { + console.log('Create Notes'); + // means we can upload the notes too + uploadGit( + btoa(unescape(encodeURIComponent(notes))), + problemName, + 'NOTES.md', + createNotesMsg, + 'upload', + ); + } + }, 500); + } + + /* Upload code to Git */ + setTimeout(function () { + findCode( + uploadGit, + problemName, + problemName + language, + probStats, + 'upload', + // callback is called when the code upload to git is a success + () => { + if (uploadState['countdown']) + clearTimeout(uploadState['countdown']); + delete uploadState['countdown']; + uploadState.uploading = false; + markUploaded(); + }, + ); // Encode `code` to base64 + }, 1000); + } + } +}, 1000); + +/* Since we dont yet have callbacks/promises that helps to find out if things went bad */ +/* we will start 10 seconds counter and even after that upload is not complete, then we conclude its failed */ +function startUploadCountDown() { + uploadState.uploading = true; + uploadState['countdown'] = setTimeout(() => { + if ((uploadState.uploading = true)) { + // still uploading, then it failed + uploadState.uploading = false; + markUploadFailed(); + } + }, 10000); +} + +/* we will need specific anchor element that is specific to the page you are in Eg. Explore */ +function insertToAnchorElement(elem) { + if (document.URL.startsWith('https://leetcode.com/explore/')) { + // means we are in explore page + action = document.getElementsByClassName('action'); + if ( + checkElem(action) && + checkElem(action[0].getElementsByClassName('row')) && + checkElem( + action[0] + .getElementsByClassName('row')[0] + .getElementsByClassName('col-sm-6'), + ) && + action[0] + .getElementsByClassName('row')[0] + .getElementsByClassName('col-sm-6').length > 1 + ) { + target = action[0] + .getElementsByClassName('row')[0] + .getElementsByClassName('col-sm-6')[1]; + elem.className = 'pull-left'; + if (target.childNodes.length > 0) + target.childNodes[0].prepend(elem); + } + } else { + if (checkElem(document.getElementsByClassName('action__38Xc'))) { + target = document.getElementsByClassName('action__38Xc')[0]; + elem.className = 'runcode-wrapper__8rXm'; + if (target.childNodes.length > 0) + target.childNodes[0].prepend(elem); + } + } +} + +/* start upload will inject a spinner on left side to the "Run Code" button */ +function startUpload() { + try { + elem = document.getElementById('leethub_progress_anchor_element'); + if (!elem) { + elem = document.createElement('span'); + elem.id = 'leethub_progress_anchor_element'; + elem.style = 'margin-right: 20px;padding-top: 2px;'; + } + elem.innerHTML = `
`; + target = insertToAnchorElement(elem); + // start the countdown + startUploadCountDown(); + } catch (error) { + // generic exception handler for time being so that existing feature doesnt break but + // error gets logged + console.log(error); + } +} + +/* This will create a tick mark before "Run Code" button signalling LeetHub has done its job */ +function markUploaded() { + elem = document.getElementById('leethub_progress_elem'); + if (elem) { + elem.className = ''; + style = + 'display: inline-block;transform: rotate(45deg);height:24px;width:12px;border-bottom:7px solid #78b13f;border-right:7px solid #78b13f;'; + elem.style = style; + } +} + +/* This will create a failed tick mark before "Run Code" button signalling that upload failed */ +function markUploadFailed() { + elem = document.getElementById('leethub_progress_elem'); + if (elem) { + elem.className = ''; + style = + 'display: inline-block;transform: rotate(45deg);height:24px;width:12px;border-bottom:7px solid red;border-right:7px solid red;'; + elem.style = style; + } +} + +/* Sync to local storage */ +chrome.storage.local.get('isSync', (data) => { + keys = [ + 'leethub_token', + 'leethub_username', + 'pipe_leethub', + 'stats', + 'leethub_hook', + 'mode_type', + ]; + if (!data || !data.isSync) { + keys.forEach((key) => { + chrome.storage.sync.get(key, (data) => { + chrome.storage.local.set({ [key]: data[key] }); + }); + }); + chrome.storage.local.set({ isSync: true }, (data) => { + console.log('LeetHub Synced to local values'); + }); + } else { + console.log('LeetHub Local storage already synced!'); + } +}); + +// inject the style +injectStyle(); + +/* inject css style required for the upload progress feature */ +function injectStyle() { + const style = document.createElement('style'); + style.textContent = + '.leethub_progress {pointer-events: none;width: 2.0em;height: 2.0em;border: 0.4em solid transparent;border-color: #eee;border-top-color: #3E67EC;border-radius: 50%;animation: loadingspin 1s linear infinite;} @keyframes loadingspin { 100% { transform: rotate(360deg) }}'; + document.head.append(style); +} From 9de65e59d6019ec3c86e7bc9f91093a30280809f Mon Sep 17 00:00:00 2001 From: varun-s22 <93476421+varun-s22@users.noreply.github.com> Date: Thu, 29 Dec 2022 02:27:24 +0530 Subject: [PATCH 2/3] Fixed Code Uploads Now the code can be easily pushed as well The Code and the ReadMe(question statement) can be pushed to github, All the features are extracted. Notes/Discussions are not extracted --- scripts/newleetcode.js | 286 +++++++++-------------------------------- 1 file changed, 61 insertions(+), 225 deletions(-) diff --git a/scripts/newleetcode.js b/scripts/newleetcode.js index 7d6c2dab..8d8b180c 100644 --- a/scripts/newleetcode.js +++ b/scripts/newleetcode.js @@ -36,6 +36,7 @@ let difficulty = ''; /* state of upload for progress */ let uploadState = { uploading: false }; +let elem; /* Util function to check if an element exists */ function checkElem(elem) { @@ -272,120 +273,39 @@ function findCode( action, cb = undefined, ) { - /* Get the submission details url from the submission page. */ - var submissionURL; - const e = document.getElementsByClassName('status-column__3SUg'); - if (checkElem(e)) { - // for normal problem submisson - const submissionRef = e[1].innerHTML.split(' ')[1]; - submissionURL = - 'https://leetcode.com' + - submissionRef.split('=')[1].slice(1, -1); - } else { - // for a submission in explore section - const submissionRef = document.getElementById('result-state'); - submissionURL = submissionRef.href; + let codeUnicoded = null; + /* Extract only the code */ + + let codeDiv = document.querySelector('code'); + if (!codeDiv) { + return null; } - if (submissionURL != undefined) { - /* Request for the submission details page */ - const xhttp = new XMLHttpRequest(); - xhttp.onreadystatechange = function () { - if (this.readyState == 4 && this.status == 200) { - /* received submission details as html reponse. */ - var doc = new DOMParser().parseFromString( - this.responseText, - 'text/html', + codeUnicoded = codeDiv.innerText; + + if (codeUnicoded !== null) { + /* The code has some unicode. Replacing all unicode with actual characters */ + var code = codeUnicoded.replace( + /\\u[\dA-F]{4}/gi, + function (match) { + return String.fromCharCode( + parseInt(match.replace(/\\u/g, ''), 16), ); - /* the response has a js object called pageData. */ - /* Pagedata has the details data with code about that submission */ - var scripts = doc.getElementsByTagName('script'); - for (var i = 0; i < scripts.length; i++) { - var text = scripts[i].innerText; - if (text.includes('pageData')) { - /* Considering the pageData as text and extract the substring - which has the full code */ - var firstIndex = text.indexOf('submissionCode'); - var lastIndex = text.indexOf('editCodeUrl'); - var slicedText = text.slice(firstIndex, lastIndex); - /* slicedText has code as like as. (submissionCode: 'Details code'). */ - /* So finding the index of first and last single inverted coma. */ - var firstInverted = slicedText.indexOf("'"); - var lastInverted = slicedText.lastIndexOf("'"); - /* Extract only the code */ - var codeUnicoded = slicedText.slice( - firstInverted + 1, - lastInverted, - ); - /* The code has some unicode. Replacing all unicode with actual characters */ - var code = codeUnicoded.replace( - /\\u[\dA-F]{4}/gi, - function (match) { - return String.fromCharCode( - parseInt(match.replace(/\\u/g, ''), 16), - ); - }, - ); - - /* - for a submisssion in explore section we do not get probStat beforehand - so, parse statistics from submisson page - */ - if (!msg) { - slicedText = text.slice( - text.indexOf('runtime'), - text.indexOf('memory'), - ); - const resultRuntime = slicedText.slice( - slicedText.indexOf("'") + 1, - slicedText.lastIndexOf("'"), - ); - slicedText = text.slice( - text.indexOf('memory'), - text.indexOf('total_correct'), - ); - const resultMemory = slicedText.slice( - slicedText.indexOf("'") + 1, - slicedText.lastIndexOf("'"), - ); - msg = `Time: ${resultRuntime}, Memory: ${resultMemory} - LeetHub`; - } - - if (code != null) { - setTimeout(function () { - uploadGit( - btoa(unescape(encodeURIComponent(code))), - problemName, - fileName, - msg, - action, - true, - cb, - ); - }, 2000); - } - } - } - } - }; - - xhttp.open('GET', submissionURL, true); - xhttp.send(); + }, + ); } -} - -/* Main parser function for the code */ -function parseCode() { - const e = document.getElementsByClassName('CodeMirror-code'); - if (e !== undefined && e.length > 0) { - const elem = e[0]; - let parsedCode = ''; - const textArr = elem.innerText.split('\n'); - for (let i = 1; i < textArr.length; i += 2) { - parsedCode += `${textArr[i]}\n`; - } - return parsedCode; + if (codeUnicoded !== null && code !== null) { + setTimeout(function () { + uploadGit( + btoa(unescape(encodeURIComponent(code))), + problemName, + fileName, + msg, + action, + true, + cb, + ); + }, 2000); } - return null; } function convertToSlug(string) { @@ -407,16 +327,7 @@ function convertToSlug(string) { .replace(/-+$/, ''); // Trim - from end of text } function getProblemNameSlug(questionTitle) { - return addLeadingZeros(convertToSlug(questionTitle)); -} - -function addLeadingZeros(title) { - const maxTitlePrefixLength = 4; - var len = title.split('-')[0].length; - if (len < maxTitlePrefixLength) { - return '0'.repeat(4 - len) + title; - } - return title; + return convertToSlug(questionTitle); } /* Parser function for the question and tags */ @@ -428,46 +339,35 @@ function parseQuestion() { if (question) { question = question.content; } - let qArray = question.split(' - '); - question = qArray[1]; - const qbody = question; + question = qArray.slice(1).join(' '); // Problem title. let qtitle = qArray[0].trim(); // Final formatting of the contents of the README for each problem - const markdown = `

${qtitle}


${qbody}`; + const markdown = `

${qtitle}


${question}`; return { markdown, title: qtitle }; } /* Parser function for time/space stats */ function parseStats() { - let probStats = document.getElementsByClassName('data__HC-i'); + let probStats = document.getElementsByClassName( + 'text-label-1 dark:text-dark-label-1 ml-2 font-medium', + ); if (!checkElem(probStats)) { - probStats = document.getElementsByClassName( - 'text-label-1 dark:text-dark-label-1 ml-2 font-medium', - ); - if (!checkElem(probStats)) { - return null; - } + return null; } const percentageStats = document.getElementsByClassName( 'text-white dark:text-dark-white ml-2 rounded-xl px-1.5 font-medium', ); let time = probStats[0].textContent; - let timePercentile; - let space; - let spacePercentile; + let space = probStats[1].textContent; if (!checkElem(percentageStats)) { - timePercentile = probStats[1].textContent; - space = probStats[2].textContent; - spacePercentile = probStats[3].textContent; - } else { - timePercentile = percentageStats[0].textContent; - space = probStats[1].textContent; - spacePercentile = percentageStats[1].textContent; + return null; } + let timePercentile = percentageStats[0].textContent; + let spacePercentile = percentageStats[1].textContent; // Format commit message return `Time: ${time} (${timePercentile}), Space: ${space} (${spacePercentile}) - LeetHub`; } @@ -511,44 +411,10 @@ document.addEventListener('click', (event) => { } }); -/* function to get the notes if there is any - the note should be opened atleast once for this to work - this is because the dom is populated after data is fetched by opening the note */ -function getNotesIfAny() { - // there are no notes on expore - if (document.URL.startsWith('https://leetcode.com/explore/')) - return ''; - - notes = ''; - if ( - checkElem(document.getElementsByClassName('notewrap__eHkN')) && - checkElem( - document - .getElementsByClassName('notewrap__eHkN')[0] - .getElementsByClassName('CodeMirror-code'), - ) - ) { - notesdiv = document - .getElementsByClassName('notewrap__eHkN')[0] - .getElementsByClassName('CodeMirror-code')[0]; - if (notesdiv) { - for (i = 0; i < notesdiv.childNodes.length; i++) { - if (notesdiv.childNodes[i].childNodes.length == 0) continue; - text = notesdiv.childNodes[i].childNodes[0].innerText; - if (text) { - notes = `${notes}\n${text.trim()}`.trim(); - } - } - } - } - return notes.trim(); -} - const loader = setInterval(() => { - let code = null; let probStatement = null; let probStats = null; - let probType; + let languageDiv = document.querySelector('.leading-4') ? document.querySelector('.leading-4') : null; @@ -557,37 +423,19 @@ const loader = setInterval(() => { ? languages[languageDiv.innerText] : findLanguage(); + // solution is accepted when the link changes, and a submission is made + // basically on a accepted solution we observe a change in href and so we can say solution is accepted + // on a wrong ans the link remains same + let currentLink = document.location.href; let solution = currentLink.match(pattern); - const successTag = document.getElementsByClassName('success__3Ai7'); - const resultState = document.getElementById('result-state'); var success = solution ? true : false; // check success tag for a normal problem - if ( - checkElem(successTag) && - successTag[0].className === 'success__3Ai7' && - successTag[0].innerText.trim() === 'Success' - ) { - console.log(successTag[0]); - success = true; - probType = NORMAL_PROBLEM; - } - - // check success state for a explore section problem - else if ( - resultState && - resultState.className === 'text-success' && - resultState.innerText === 'Accepted' - ) { - success = true; - probType = EXPLORE_SECTION_PROBLEM; - } if (success) { probStatement = parseQuestion().markdown; probStats = parseStats(); } - if (probStatement !== null) { const problemName = getProblemNameSlug(parseQuestion().title); if (language !== null) { @@ -604,7 +452,6 @@ const loader = setInterval(() => { ) { sha = stats.sha[filePath]; } - /* Only create README if not already created */ if (sha === null) { /* @TODO: Change this setTimeout to Promise */ @@ -618,25 +465,6 @@ const loader = setInterval(() => { } }); - /* get the notes and upload it */ - /* only upload notes if there is any */ - notes = getNotesIfAny(); - if (notes.length > 0) { - setTimeout(function () { - if (notes != undefined && notes.length != 0) { - console.log('Create Notes'); - // means we can upload the notes too - uploadGit( - btoa(unescape(encodeURIComponent(notes))), - problemName, - 'NOTES.md', - createNotesMsg, - 'upload', - ); - } - }, 500); - } - /* Upload code to Git */ setTimeout(function () { findCode( @@ -697,9 +525,18 @@ function insertToAnchorElement(elem) { target.childNodes[0].prepend(elem); } } else { - if (checkElem(document.getElementsByClassName('action__38Xc'))) { - target = document.getElementsByClassName('action__38Xc')[0]; - elem.className = 'runcode-wrapper__8rXm'; + if ( + checkElem( + document.getElementsByClassName( + 'ml-auto flex items-center space-x-4', + ), + ) + ) { + let target = document.getElementsByClassName( + 'ml-auto flex items-center space-x-4', + )[0]; + elem.className = + 'px-3 py-1.5 font-medium items-center whitespace-nowrap transition-all focus:outline-none inline-flex bg-fill-3 dark:bg-dark-fill-3 hover:bg-fill-2 dark:hover:bg-dark-fill-2 text-label-2 dark:text-dark-label-2 rounded-lg'; if (target.childNodes.length > 0) target.childNodes[0].prepend(elem); } @@ -716,7 +553,7 @@ function startUpload() { elem.style = 'margin-right: 20px;padding-top: 2px;'; } elem.innerHTML = `
`; - target = insertToAnchorElement(elem); + insertToAnchorElement(elem); // start the countdown startUploadCountDown(); } catch (error) { @@ -731,7 +568,7 @@ function markUploaded() { elem = document.getElementById('leethub_progress_elem'); if (elem) { elem.className = ''; - style = + let style = 'display: inline-block;transform: rotate(45deg);height:24px;width:12px;border-bottom:7px solid #78b13f;border-right:7px solid #78b13f;'; elem.style = style; } @@ -742,7 +579,7 @@ function markUploadFailed() { elem = document.getElementById('leethub_progress_elem'); if (elem) { elem.className = ''; - style = + let style = 'display: inline-block;transform: rotate(45deg);height:24px;width:12px;border-bottom:7px solid red;border-right:7px solid red;'; elem.style = style; } @@ -772,7 +609,6 @@ chrome.storage.local.get('isSync', (data) => { } }); -// inject the style injectStyle(); /* inject css style required for the upload progress feature */ From 647df5d8dc2e9492f3ffcdc4e2c522ff78737f37 Mon Sep 17 00:00:00 2001 From: varun-s22 <93476421+varun-s22@users.noreply.github.com> Date: Thu, 29 Dec 2022 19:03:23 +0530 Subject: [PATCH 3/3] Fixed repeated uploads Fixed repeatly uploading of files Currently one file/solution can be pushed one time. --- manifest.json | 1 + scripts/newleetcode.js | 283 ++++++++++++++--------------------------- 2 files changed, 100 insertions(+), 184 deletions(-) diff --git a/manifest.json b/manifest.json index a283a2dc..38f76d9a 100644 --- a/manifest.json +++ b/manifest.json @@ -39,6 +39,7 @@ "https://*.leetcode.cn/*" ], "js": [ + "scripts/leetcode.js", "scripts/newleetcode.js", "scripts/authorize.js", "scripts/gfg.js" diff --git a/scripts/newleetcode.js b/scripts/newleetcode.js index 8d8b180c..747ed19d 100644 --- a/scripts/newleetcode.js +++ b/scripts/newleetcode.js @@ -1,5 +1,5 @@ /* Enum for languages supported by LeetCode. */ -const languages = { +const programmingLanguages = { Python: '.py', Python3: '.py', 'C++': '.cpp', @@ -22,48 +22,21 @@ const languages = { }; /* Commit messages */ -const readmeMsg = 'Create README - LeetHub'; -const discussionMsg = 'Prepend discussion post - LeetHub'; -const createNotesMsg = 'Attach NOTES - LeetHub'; +const readMeMsg = 'Create README - LeetHub'; const pattern = /https?:\/\/.*leetcode.com\/problems\/.*\/submissions\/\d*/; -// problem types -const NORMAL_PROBLEM = 0; -const EXPLORE_SECTION_PROBLEM = 1; - -/* Difficulty of most recenty submitted question */ -let difficulty = ''; - /* state of upload for progress */ -let uploadState = { uploading: false }; +let uploadingState = { uploading: false }; let elem; +let questionDict = {}; /* Util function to check if an element exists */ -function checkElem(elem) { +function checkElement(elem) { return elem && elem.length > 0; } -/* Get file extension for submission */ -function findLanguage() { - const tag = [ - ...document.getElementsByClassName( - 'ant-select-selection-selected-value', - ), - ...document.getElementsByClassName('Select-value-label'), - ]; - if (tag && tag.length > 0) { - for (let i = 0; i < tag.length; i += 1) { - const elem = tag[i].textContent; - if (elem !== undefined && languages[elem] !== undefined) { - return languages[elem]; // should generate respective file extension - } - } - } - return null; -} - /* Main function for uploading code to GitHub repo, and callback cb is called if success */ -const upload = ( +const uploadToGit = ( token, hook, code, @@ -107,9 +80,6 @@ const upload = ( // New submission commits twice (README and problem) if (filename === 'README.md' && sha === null) { stats.solved += 1; - stats.easy += difficulty === 'Easy' ? 1 : 0; - stats.medium += difficulty === 'Medium' ? 1 : 0; - stats.hard += difficulty === 'Hard' ? 1 : 0; } stats.sha[filePath] = updatedSha; // update sha key. chrome.storage.local.set({ stats }, () => { @@ -135,7 +105,7 @@ const upload = ( /* Main function for updating code on GitHub Repo */ /* Currently only used for prepending discussion posts to README */ /* callback cb is called on success if it is defined */ -const update = ( +const updateOnGit = ( token, hook, addition, @@ -166,7 +136,7 @@ const update = ( } /* Write file with new content to GitHub */ - upload( + uploadToGit( token, hook, newContent, @@ -185,7 +155,7 @@ const update = ( xhr.send(); }; -function uploadGit( +function uploadOnGit( code, problemName, fileName, @@ -195,11 +165,6 @@ function uploadGit( cb = undefined, _diff = undefined, ) { - // Assign difficulty - if (_diff && _diff !== undefined) { - difficulty = _diff.trim(); - } - /* Get necessary payload data */ chrome.storage.local.get('leethub_token', (t) => { const token = t.leethub_token; @@ -229,7 +194,7 @@ function uploadGit( if (action === 'upload') { /* Upload to git. */ - upload( + uploadToGit( token, hook, code, @@ -241,7 +206,7 @@ function uploadGit( ); } else if (action === 'update') { /* Update on git */ - update( + updateOnGit( token, hook, code, @@ -261,12 +226,12 @@ function uploadGit( } /* Function for finding and parsing the full code. */ -/* - At first find the submission details url. */ -/* - Then send a request for the details page. */ -/* - Finally, parse the code from the html reponse. */ +/* - The solution is available at the same page. */ +/* - Either get the monaco object from the window, or use dom manipulation*/ +/* - Here it gets the code from DOM manipulation */ /* - Also call the callback if available when upload is success */ -function findCode( - uploadGit, +function findSubmittedCode( + uploadOnGit, problemName, fileName, msg, @@ -274,8 +239,8 @@ function findCode( cb = undefined, ) { let codeUnicoded = null; - /* Extract only the code */ + /* Extract only the code */ let codeDiv = document.querySelector('code'); if (!codeDiv) { return null; @@ -295,7 +260,7 @@ function findCode( } if (codeUnicoded !== null && code !== null) { setTimeout(function () { - uploadGit( + uploadOnGit( btoa(unescape(encodeURIComponent(code))), problemName, fileName, @@ -308,7 +273,7 @@ function findCode( } } -function convertToSlug(string) { +function convertToSlugCase(string) { const a = 'àáâäæãåāăąçćčđďèéêëēėęěğǵḧîïíīįìłḿñńǹňôöòóœøōõőṕŕřßśšşșťțûüùúūǘůűųẃẍÿýžźż·/_,:;'; const b = @@ -326,12 +291,12 @@ function convertToSlug(string) { .replace(/^-+/, '') // Trim - from start of text .replace(/-+$/, ''); // Trim - from end of text } -function getProblemNameSlug(questionTitle) { - return convertToSlug(questionTitle); +function getProblemNameSlugCase(questionTitle) { + return convertToSlugCase(questionTitle); } /* Parser function for the question and tags */ -function parseQuestion() { +function getQuestion() { var questionUrl = window.location.href; questionUrl = questionUrl.split('submissions')[0]; @@ -340,7 +305,7 @@ function parseQuestion() { question = question.content; } let qArray = question.split(' - '); - question = qArray.slice(1).join(' '); + question = qArray.slice(1).join(' - '); // Problem title. let qtitle = qArray[0].trim(); @@ -351,11 +316,11 @@ function parseQuestion() { } /* Parser function for time/space stats */ -function parseStats() { +function getStats() { let probStats = document.getElementsByClassName( 'text-label-1 dark:text-dark-label-1 ml-2 font-medium', ); - if (!checkElem(probStats)) { + if (!checkElement(probStats)) { return null; } const percentageStats = document.getElementsByClassName( @@ -363,7 +328,7 @@ function parseStats() { ); let time = probStats[0].textContent; let space = probStats[1].textContent; - if (!checkElem(percentageStats)) { + if (!checkElement(percentageStats)) { return null; } let timePercentile = percentageStats[0].textContent; @@ -372,46 +337,7 @@ function parseStats() { return `Time: ${time} (${timePercentile}), Space: ${space} (${spacePercentile}) - LeetHub`; } -document.addEventListener('click', (event) => { - const element = event.target; - const oldPath = window.location.pathname; - - /* Act on Post button click */ - /* Complex since "New" button shares many of the same properties as "Post button */ - if ( - element.classList.contains('icon__3Su4') || - element.parentElement.classList.contains('icon__3Su4') || - element.parentElement.classList.contains( - 'btn-content-container__214G', - ) || - element.parentElement.classList.contains('header-right__2UzF') - ) { - setTimeout(function () { - /* Only post if post button was clicked and url changed */ - if ( - oldPath !== window.location.pathname && - oldPath === - window.location.pathname.substring(0, oldPath.length) && - !Number.isNaN(window.location.pathname.charAt(oldPath.length)) - ) { - const date = new Date(); - const currentDate = `${date.getDate()}/${date.getMonth()}/${date.getFullYear()} at ${date.getHours()}:${date.getMinutes()}`; - const addition = `[Discussion Post (created on ${currentDate})](${window.location}) \n`; - const problemName = window.location.pathname.split('/')[2]; // must be true. - - uploadGit( - addition, - problemName, - 'README.md', - discussionMsg, - 'update', - ); - } - }, 1000); - } -}); - -const loader = setInterval(() => { +const mainFunction = setInterval(() => { let probStatement = null; let probStats = null; @@ -420,8 +346,8 @@ const loader = setInterval(() => { : null; const language = languageDiv - ? languages[languageDiv.innerText] - : findLanguage(); + ? programmingLanguages[languageDiv.innerText] + : null; // solution is accepted when the link changes, and a submission is made // basically on a accepted solution we observe a change in href and so we can say solution is accepted @@ -433,14 +359,22 @@ const loader = setInterval(() => { var success = solution ? true : false; // check success tag for a normal problem if (success) { - probStatement = parseQuestion().markdown; - probStats = parseStats(); + probStatement = getQuestion().markdown; + probStats = getStats(); } if (probStatement !== null) { - const problemName = getProblemNameSlug(parseQuestion().title); + const problemName = getProblemNameSlugCase(getQuestion().title); + let filePath = problemName + problemName + language; + let readmeKey = language + problemName; + if ( + questionDict[filePath] !== undefined || + questionDict[readmeKey] !== undefined + ) { + return null; + } if (language !== null) { // start upload indicator here - startUpload(); + startUploading(); chrome.storage.local.get('stats', (s) => { const { stats } = s; const filePath = problemName + problemName + language; @@ -455,96 +389,77 @@ const loader = setInterval(() => { /* Only create README if not already created */ if (sha === null) { /* @TODO: Change this setTimeout to Promise */ - uploadGit( - btoa(unescape(encodeURIComponent(probStatement))), - problemName, - 'README.md', - readmeMsg, - 'upload', - ); + if (questionDict[readmeKey] === undefined) { + uploadOnGit( + btoa(unescape(encodeURIComponent(probStatement))), + problemName, + 'README.md', + readMeMsg, + 'upload', + ); + questionDict[readmeKey] = Date.now(); + } } }); - - /* Upload code to Git */ - setTimeout(function () { - findCode( - uploadGit, - problemName, - problemName + language, - probStats, - 'upload', - // callback is called when the code upload to git is a success - () => { - if (uploadState['countdown']) - clearTimeout(uploadState['countdown']); - delete uploadState['countdown']; - uploadState.uploading = false; - markUploaded(); - }, - ); // Encode `code` to base64 - }, 1000); + if (questionDict[filePath] === undefined) { + /* Upload code to Git */ + setTimeout(function () { + findSubmittedCode( + uploadOnGit, + problemName, + problemName + language, + probStats, + 'upload', + // callback is called when the code upload to git is a success + () => { + if (uploadingState['countdown']) + clearTimeout(uploadingState['countdown']); + delete uploadingState['countdown']; + uploadingState.uploading = false; + markTheUploaded(); + }, + ); // Encode `code` to base64 + questionDict[filePath] = Date.now(); + }, 1000); + } } } }, 1000); /* Since we dont yet have callbacks/promises that helps to find out if things went bad */ /* we will start 10 seconds counter and even after that upload is not complete, then we conclude its failed */ -function startUploadCountDown() { - uploadState.uploading = true; - uploadState['countdown'] = setTimeout(() => { - if ((uploadState.uploading = true)) { +function startUploadCountdown() { + uploadingState.uploading = true; + uploadingState['countdown'] = setTimeout(() => { + if ((uploadingState.uploading = true)) { // still uploading, then it failed - uploadState.uploading = false; - markUploadFailed(); + uploadingState.uploading = false; + markUploadFail(); } }, 10000); } /* we will need specific anchor element that is specific to the page you are in Eg. Explore */ -function insertToAnchorElement(elem) { - if (document.URL.startsWith('https://leetcode.com/explore/')) { - // means we are in explore page - action = document.getElementsByClassName('action'); - if ( - checkElem(action) && - checkElem(action[0].getElementsByClassName('row')) && - checkElem( - action[0] - .getElementsByClassName('row')[0] - .getElementsByClassName('col-sm-6'), - ) && - action[0] - .getElementsByClassName('row')[0] - .getElementsByClassName('col-sm-6').length > 1 - ) { - target = action[0] - .getElementsByClassName('row')[0] - .getElementsByClassName('col-sm-6')[1]; - elem.className = 'pull-left'; - if (target.childNodes.length > 0) - target.childNodes[0].prepend(elem); - } - } else { - if ( - checkElem( - document.getElementsByClassName( - 'ml-auto flex items-center space-x-4', - ), - ) - ) { - let target = document.getElementsByClassName( +function insertToAnchor(elem) { + if ( + checkElement( + document.getElementsByClassName( 'ml-auto flex items-center space-x-4', - )[0]; - elem.className = - 'px-3 py-1.5 font-medium items-center whitespace-nowrap transition-all focus:outline-none inline-flex bg-fill-3 dark:bg-dark-fill-3 hover:bg-fill-2 dark:hover:bg-dark-fill-2 text-label-2 dark:text-dark-label-2 rounded-lg'; - if (target.childNodes.length > 0) - target.childNodes[0].prepend(elem); - } + ), + ) + ) { + let target = document.getElementsByClassName( + 'ml-auto flex items-center space-x-4', + )[0]; + elem.className = + 'px-3 py-1.5 font-medium items-center whitespace-nowrap transition-all focus:outline-none inline-flex bg-fill-3 dark:bg-dark-fill-3 hover:bg-fill-2 dark:hover:bg-dark-fill-2 text-label-2 dark:text-dark-label-2 rounded-lg'; + if (target.childNodes.length > 0) + target.childNodes[0].prepend(elem); } } /* start upload will inject a spinner on left side to the "Run Code" button */ -function startUpload() { +function startUploading() { try { elem = document.getElementById('leethub_progress_anchor_element'); if (!elem) { @@ -553,9 +468,9 @@ function startUpload() { elem.style = 'margin-right: 20px;padding-top: 2px;'; } elem.innerHTML = `
`; - insertToAnchorElement(elem); + insertToAnchor(elem); // start the countdown - startUploadCountDown(); + startUploadCountdown(); } catch (error) { // generic exception handler for time being so that existing feature doesnt break but // error gets logged @@ -564,7 +479,7 @@ function startUpload() { } /* This will create a tick mark before "Run Code" button signalling LeetHub has done its job */ -function markUploaded() { +function markTheUploaded() { elem = document.getElementById('leethub_progress_elem'); if (elem) { elem.className = ''; @@ -575,7 +490,7 @@ function markUploaded() { } /* This will create a failed tick mark before "Run Code" button signalling that upload failed */ -function markUploadFailed() { +function markUploadFail() { elem = document.getElementById('leethub_progress_elem'); if (elem) { elem.className = ''; @@ -609,10 +524,10 @@ chrome.storage.local.get('isSync', (data) => { } }); -injectStyle(); +injectCode(); /* inject css style required for the upload progress feature */ -function injectStyle() { +function injectCode() { const style = document.createElement('style'); style.textContent = '.leethub_progress {pointer-events: none;width: 2.0em;height: 2.0em;border: 0.4em solid transparent;border-color: #eee;border-top-color: #3E67EC;border-radius: 50%;animation: loadingspin 1s linear infinite;} @keyframes loadingspin { 100% { transform: rotate(360deg) }}';