diff --git a/Makefile b/Makefile index 2bd591f..ad9ac9c 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -old-reddit-redirect.zip: *.json *.js img/* *.md *.txt - zip -r old-reddit-redirect.zip * -x .git/* -x img/screenshot.png -x .gitignore -x Makefile +bitbucket-fork-redirect.zip: *.json *.js img/* *.md *.txt + zip -r bitbucket-fork-redirect.zip * -x .git/* -x img/screenshot.png -x .gitignore -x Makefile clean: rm *.zip diff --git a/background.js b/background.js index abb306e..5c2efac 100644 --- a/background.js +++ b/background.js @@ -1,36 +1,14 @@ -const oldReddit = "https://old.reddit.com"; -const excludedPaths = [ - "/poll", - "/rpan", - "/settings", - "/topics", - "/community-points", -]; +chrome.webRequest.onBeforeRequest.addListener(details => { + const url = new URL(details.url) -chrome.webRequest.onBeforeRequest.addListener( - function (details) { - const url = new URL(details.url); + if (url.hostname !== "git.add123.com") return + if (!url.pathname.endsWith("plugins/servlet/search")) return + if (details.url.includes("fork:") || details.url.includes("fork%3A")) return - if (url.hostname === "old.reddit.com") return; - - for (const path of excludedPaths) { - if (url.pathname.indexOf(path) === 0) return; - } - - if (url.pathname.indexOf("/gallery") === 0) { - return { redirectUrl: oldReddit + '/comments' + url.pathname.slice("/gallery".length) }; - } - - return { redirectUrl: oldReddit + url.pathname + url.search + url.hash }; + return { redirectUrl: `${details.url}%20fork%3Afalse` } }, { - urls: [ - "*://reddit.com/*", - "*://www.reddit.com/*", - "*://np.reddit.com/*", - "*://amp.reddit.com/*", - "*://i.reddit.com/*", - ], + urls: [ "https://git.add123.com/*" ], types: [ "main_frame", "sub_frame", @@ -43,4 +21,4 @@ chrome.webRequest.onBeforeRequest.addListener( ], }, ["blocking"] -); +) diff --git a/backtick-comments.js b/backtick-comments.js new file mode 100644 index 0000000..2c66728 --- /dev/null +++ b/backtick-comments.js @@ -0,0 +1,107 @@ +const jsKeywords = new Set([ + "abstract", "arguments", "await*", "boolean", "break", "byte", "case", + "catch", "char", "class*", "const", "continue", "debugger", "default", + "delete", "do", "double", "else", "enum*", "eval", "export*", "extends*", + "false", "final", "finally", "float", "for", "function", "goto", "if", + "implements", "import*", "in", "instanceof", "int", "interface", "let*", + "long", "native", "new", "null", "package", "private", "protected", "public", + "return", "short", "static", "super*", "switch", "synchronized", "this", + "throw", "throws", "transient", "true", "try", "typeof", "var", "void", + "volatile", "while", "with", "yield" +]) + +const csKeywords = new Set([ + "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", + "checked", "class", "const", "continue", "decimal", "default", "delegate", + "do", "double", "else", "enum", "event", "explicit", "extern", "false", + "finally", "fixed", "float", "for", "foreach", "goto", "if", "implicit", + "in", "int", "interface", "internal", "is", "lock", "long", "namespace", + "new", "null", "object", "operator", "out", "override", "params", "private", + "protected", "public", "readonly", "ref", "return", "sbyte", "sealed", + "short", "sizeof", "stackalloc", "static", "string", "struct", "switch", + "this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", + "unsafe", "ushort", "using", "virtual", "void", "volatile", "while", +]) + +const javaKeywords = new Set([ + "abstract", "continue", "for", "new", "switch", "assert", "default", + "goto", "package", "synchronized", "boolean", "do", "if", "private", + "this", "break", "double", "implements", "protected", "throw", "byte", + "else", "import", "public", "throws", "case", "enum", "instanceof", + "return", "transient", "catch", "extends", "int", "short", "try", "char", + "final", "interface", "static", "void", "class", "finally", "long", + "strictfp", "volatile", "const", "float", "native", "super", "while", +]) + +const keywordMap = { + js: jsKeywords, + mjs: jsKeywords, + java: javaKeywords, + cs: csKeywords, + none: new Set() +} + + +const bookendedWith = (text, bookend) => text.startsWith(bookend) && text.endsWith(bookend) + +const getClass = (text, keywords) => { + if (keywords.has(text)) { + return 'hl-keyword' + } + + if (bookendedWith(text, '"')) { + return 'hl-string' + } + + return 'hl-variable' +} + +const getKeywords = () => keywordMap[getFileName().replace(/.*\./g, '')] || keywordMap.none + +const commentSpan = innerText => { + const span = document.createElement('span') + span.innerText = innerText + span.classList.add('hl-comment') + return span +} + +const colorCodeInComment = (codeStart = '`', codeEnd) => { + codeEnd ??= codeStart + const processed = new Set() + const keywords = getKeywords() + getClassNameElementsArray('hl-comment').forEach(element => { + if (!element.innerText?.startsWith(codeStart) || processed.has(element)) { + return + } + const codeStartElement = commentSpan(codeStart) + element.replaceWith(codeStartElement, element) + element.innerText = element.innerText.replace(codeStart, '') + while (element) { + if (!element.classList.contains('hl-comment')) { + break + } + processed.add(element) + element.classList.remove('hl-comment') + const text = element.innerText.replaceAll(codeStart, '').replaceAll(codeEnd, '') + element.classList.add(getClass(text, keywords)) + const codeEndIndex = element.innerText.indexOf(codeEnd) + if (codeEndIndex > -1) { + const codeEndElement = commentSpan(element.innerText.substring(codeEndIndex)) + element.innerText = element.innerText.substring(0, codeEndIndex) + element.replaceWith(element, codeEndElement) + break + } + element = element.nextElementSibling + } + }) +} + +const commentCodeFix = () => { + colorCodeInComment('`', '`') + colorCodeInComment('', '') + colorCodeInComment('
', '
') + colorCodeInComment('', '') +} + + +addFix(commentCodeFix) diff --git a/csharp-quotes.js b/csharp-quotes.js new file mode 100644 index 0000000..506dbe2 --- /dev/null +++ b/csharp-quotes.js @@ -0,0 +1,17 @@ +const fixCSharpStrings = () => { + const hasCSharpFileName = getFileName().endsWith('.cs') + if (!hasCSharpFileName) { + return + } + getClassNameElementsArray('hl-string') + .filter(e => e.innerText.includes('@"') || e.innerText.includes('@$""')) + .forEach(e => { + const s = e.innerHTML + const firstQuote = s.indexOf('"') + 1 + const spanned = s.substring(firstQuote, s.length - 1).replaceAll('""', '""') + e.innerHTML = s.substring(0, firstQuote) + spanned + '"' + }) +} + + +addFix(fixCSharpStrings) diff --git a/img/icon128.png b/img/icon128.png index e999360..a4e1bfa 100644 Binary files a/img/icon128.png and b/img/icon128.png differ diff --git a/img/icon48.png b/img/icon48.png index b1452a8..7c7baeb 100644 Binary files a/img/icon48.png and b/img/icon48.png differ diff --git a/manifest.json b/manifest.json index 75273ea..a4a37f4 100644 --- a/manifest.json +++ b/manifest.json @@ -1,27 +1,41 @@ { - "name": "Old Reddit Redirect", - "description": "Ensure Reddit always loads the old design", - "version": "1.6.1", + "name": "ADD Git NoFork", + "description": "Ensure searches don't include forks", + "version": "1.0.2", "manifest_version": 2, - "background": { "scripts": ["background.js"] }, + "browser_specific_settings": { + "gecko": { + "id": "svaillancourt@add123.com" + } + }, + "background": { + "scripts": ["background.js"] + }, "icons": { "48": "img/icon48.png", "128": "img/icon128.png" }, "content_scripts": [ { - "matches": ["*://old.reddit.com/*"], + "matches": ["*://git.add123.com/*"], "css": ["styles.css"], "run_at": "document_start" + }, + { + "matches": ["*://git.add123.com/*"], + "js": [ + "utils.js", + "csharp-quotes.js", + "backtick-comments.js", + "var-highlighter.js" + ], + "run_at": "document_start" } ], "permissions": [ + "activeTab", "webRequest", "webRequestBlocking", - "*://reddit.com/*", - "*://www.reddit.com/*", - "*://np.reddit.com/*", - "*://amp.reddit.com/*", - "*://i.reddit.com/*" + "*://git.add123.com/*" ] } diff --git a/utils.js b/utils.js new file mode 100644 index 0000000..37315db --- /dev/null +++ b/utils.js @@ -0,0 +1,13 @@ +const NEW_PAGE_DELAY = 1000 +const CURRENT_PAGE_DELAY = 300 + +const getClassNameElementsArray = className => + Object.values(document.getElementsByClassName(className)) + +// Runs on "complete" load and when https://urlurl.url#this-hash-value changes +const addFix = fixFunc => { + window.addEventListener('load', () => setTimeout(fixFunc, NEW_PAGE_DELAY)) + addEventListener('hashchange', () => setTimeout(fixFunc, CURRENT_PAGE_DELAY)) +} + +const getFileName = () => getClassNameElementsArray('file-breadcrumbs-segment-highlighted').map(e => e.innerText)[0] diff --git a/var-highlighter.js b/var-highlighter.js new file mode 100644 index 0000000..e5138e6 --- /dev/null +++ b/var-highlighter.js @@ -0,0 +1,89 @@ +const getVarElements = () => [ + ...getClassNameElementsArray('hl-variable'), + ...getClassNameElementsArray('hl-def'), + ...getClassNameElementsArray('hl-type') +] + +let currentSelected + +const selectVar = varName => { + if (!varName) { + return + } + // Clear existing colors + getClassNameElementsArray(selectedClass) + .forEach(e => e.classList.remove(selectedClass)) + + // Color vars with matching text + getVarElements() + .filter(e => e.innerText === varName) + .forEach(e => e.classList.add(selectedClass)) + + currentSelected = varName +} + +const selectedClass = 'sages-selected-variable' + +const getTagMatching = (name, matcher) => { + const elements = Object.values(document.getElementsByTagName(name)).filter(matcher) + return elements.length ? elements[0] : null +} + +const addVariableClickers = skipExpandos => { + const getSearchButton = () => getTagMatching( + 'button', e => e.dataset?.testid === 'search-action-button') + + const getSearchBox = () => getTagMatching('input', e => e.name === 'changes-search-input') + + if (!skipExpandos) { + getClassNameElementsArray('expand-context-button').forEach(e => { + const currentOnClick = e.onclick + e.onclick = (...args) => { + currentOnClick && currentOnClick(...args) + setTimeout(() => { + selectVar(currentSelected) + addVariableClickers(true) + }, CURRENT_PAGE_DELAY) + } + }) + } + + getVarElements() + .forEach(e => { + e.onclick = () => selectVar(e.innerText) + e.ondblclick = () => { + let searchBox = getSearchBox() + + if (!searchBox) { + getSearchButton()?.click() + searchBox = getSearchBox() + } + + searchBox.value = e.innerText + searchBox.dispatchEvent(new window.Event('change', { bubbles: true })) + } + }) + +} + + +addFix(() => { + const styleSheet = document.createElement('style') + styleSheet.innerText = ` + .${selectedClass} { + background: rgb(255, 253, 170) !important; + border-radius: 3px; + padding: 2px !important; + margin: -2px !important; + /* + padding-left: 2px !important; + padding-right: 2px !important; + margin-left: -2px !important; + margin-right: -2px !important; + */ + }` + + document.head.appendChild(styleSheet) + addVariableClickers() + selectVar(currentSelected) +})