diff --git a/manifest.json b/manifest.json index 795f250b..38f76d9a 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,13 @@ "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..747ed19d --- /dev/null +++ b/scripts/newleetcode.js @@ -0,0 +1,535 @@ +/* Enum for languages supported by LeetCode. */ +const programmingLanguages = { + 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 pattern = /https?:\/\/.*leetcode.com\/problems\/.*\/submissions\/\d*/; + +/* state of upload for progress */ +let uploadingState = { uploading: false }; +let elem; +let questionDict = {}; + +/* Util function to check if an element exists */ +function checkElement(elem) { + return elem && elem.length > 0; +} + +/* Main function for uploading code to GitHub repo, and callback cb is called if success */ +const uploadToGit = ( + 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.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 updateOnGit = ( + 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 */ + uploadToGit( + 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 uploadOnGit( + code, + problemName, + fileName, + msg, + action, + prepend = true, + cb = undefined, + _diff = undefined, +) { + /* 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. */ + uploadToGit( + token, + hook, + code, + problemName, + fileName, + sha, + msg, + cb, + ); + } else if (action === 'update') { + /* Update on git */ + updateOnGit( + token, + hook, + code, + problemName, + msg, + prepend, + cb, + ); + } + }); + } + }); + } + }); + } + }); +} + +/* Function for finding and parsing the full code. */ +/* - 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 findSubmittedCode( + uploadOnGit, + problemName, + fileName, + msg, + action, + cb = undefined, +) { + let codeUnicoded = null; + + /* Extract only the code */ + let codeDiv = document.querySelector('code'); + if (!codeDiv) { + return null; + } + 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), + ); + }, + ); + } + if (codeUnicoded !== null && code !== null) { + setTimeout(function () { + uploadOnGit( + btoa(unescape(encodeURIComponent(code))), + problemName, + fileName, + msg, + action, + true, + cb, + ); + }, 2000); + } +} + +function convertToSlugCase(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 getProblemNameSlugCase(questionTitle) { + return convertToSlugCase(questionTitle); +} + +/* Parser function for the question and tags */ +function getQuestion() { + 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.slice(1).join(' - '); + + // Problem title. + let qtitle = qArray[0].trim(); + + // Final formatting of the contents of the README for each problem + const markdown = `