Compare commits

...

10 Commits

Author SHA1 Message Date
Sage Vaillancourt f7276dcb3d Add auto-update support 2023-12-04 11:32:45 -05:00
Sage Vaillancourt de342265b1 Automatically enter folders when file-scrolling 2023-12-04 10:53:33 -05:00
Sage Vaillancourt 46b873e71d Add some varName sanitization. 2023-12-04 10:23:47 -05:00
Sage Vaillancourt ab9f6641f4 More var highlighting 2023-12-04 10:14:33 -05:00
Sage Vaillancourt e71e2fb699 Fixed up scroll-wheel folder de-nesting 2023-12-04 10:04:20 -05:00
Sage Vaillancourt 8cdcfac87d Tweak spacing. 2023-02-07 15:14:06 -05:00
Sage Vaillancourt 62ade8de28 Add scroll hints. 2023-02-07 15:04:56 -05:00
Sage Vaillancourt be9719be8c Missed de-nest in file-scroll.js 2023-02-07 14:08:19 -05:00
Sage Vaillancourt 22079471bc Add scroll-wheel file browsing.
Improve backtick comments (works on non-diff pages)
Other small bits of cleanup.
2023-02-07 14:07:58 -05:00
Sage Vaillancourt bee9f4e2b2 Sort repo search results.
Prioritizes shared project repos and the current user's repos before other users'
2023-02-02 15:57:09 -05:00
8 changed files with 179 additions and 27 deletions

View File

@ -1,4 +1,4 @@
bitbucket-fork-redirect.zip: *.json *.js img/* *.md *.txt all: *.json *.js img/* *.md *.txt
zip -r bitbucket-fork-redirect.zip * -x .git/* -x img/screenshot.png -x .gitignore -x Makefile zip -r bitbucket-fork-redirect.zip * -x .git/* -x img/screenshot.png -x .gitignore -x Makefile
clean: clean:

View File

@ -44,24 +44,24 @@ const keywordMap = {
const bookendedWith = (text, bookend) => text.startsWith(bookend) && text.endsWith(bookend) const bookendedWith = (text, bookend) => text.startsWith(bookend) && text.endsWith(bookend)
const getClass = (text, keywords) => { const getClass = (text, prefix, keywords) => {
if (keywords.has(text)) { if (keywords.has(text)) {
return 'hl-keyword' return prefix + 'keyword'
} }
if (bookendedWith(text, '"')) { if (bookendedWith(text, '"')) {
return 'hl-string' return prefix + 'string'
} }
return 'hl-variable' return prefix + 'variable'
} }
const getKeywords = () => keywordMap[getFileName().replace(/.*\./g, '')] || keywordMap.none const getKeywords = () => keywordMap[getFileName()?.replace(/.*\./g, '')] || keywordMap.none
const commentSpan = innerText => { const commentSpan = (innerText, prefix) => {
const span = document.createElement('span') const span = document.createElement('span')
span.innerText = innerText span.innerText = innerText
span.classList.add('hl-comment') span.classList.add(prefix + 'comment')
return span return span
} }
@ -69,24 +69,29 @@ const colorCodeInComment = (codeStart = '`', codeEnd) => {
codeEnd ??= codeStart codeEnd ??= codeStart
const processed = new Set() const processed = new Set()
const keywords = getKeywords() const keywords = getKeywords()
getClassNameElementsArray('hl-comment').forEach(element => { const comments = [...getClassNameElementsArray('hl-comment'), ...getClassNameElementsArray('cm-comment')]
if (!element.innerText?.startsWith(codeStart) || processed.has(element)) { comments.forEach(element => {
const codeStartIndex = element.innerText?.indexOf(codeStart)
if (codeStartIndex === -1 || processed.has(element)) {
return return
} }
const codeStartElement = commentSpan(codeStart) const prefix = element.classList.contains('hl-comment') ? 'hl-' : 'cm-'
const endOfCodeStart = codeStartIndex + codeStart.length
const codeStartText = element.innerText.substring(0, endOfCodeStart)
const codeStartElement = commentSpan(codeStartText, prefix)
element.innerText = element.innerText.substring(endOfCodeStart, element.innerText.length)
element.replaceWith(codeStartElement, element) element.replaceWith(codeStartElement, element)
element.innerText = element.innerText.replace(codeStart, '')
while (element) { while (element) {
if (!element.classList.contains('hl-comment')) { if (!element.classList.contains('hl-comment') && !element.classList.contains('cm-comment')) {
break break
} }
processed.add(element) processed.add(element)
element.classList.remove('hl-comment') element.classList.remove(`${prefix}comment`)
const text = element.innerText.replaceAll(codeStart, '').replaceAll(codeEnd, '') const text = element.innerText.replaceAll(codeStart, '').replaceAll(codeEnd, '')
element.classList.add(getClass(text, keywords)) element.classList.add(getClass(text, prefix, keywords))
const codeEndIndex = element.innerText.indexOf(codeEnd) const codeEndIndex = element.innerText.indexOf(codeEnd)
if (codeEndIndex > -1) { if (codeEndIndex > -1) {
const codeEndElement = commentSpan(element.innerText.substring(codeEndIndex)) const codeEndElement = commentSpan(element.innerText.substring(codeEndIndex), prefix)
element.innerText = element.innerText.substring(0, codeEndIndex) element.innerText = element.innerText.substring(0, codeEndIndex)
element.replaceWith(element, codeEndElement) element.replaceWith(element, codeEndElement)
break break

View File

@ -1,5 +1,5 @@
const fixCSharpStrings = () => { const fixCSharpStrings = () => {
const hasCSharpFileName = getFileName().endsWith('.cs') const hasCSharpFileName = getFileName()?.endsWith('.cs')
if (!hasCSharpFileName) { if (!hasCSharpFileName) {
return return
} }

79
file-scroll.js Normal file
View File

@ -0,0 +1,79 @@
const getChangeHeader = () => getClassNameElementsArray('change-header')[0]
const getFileElements = () => querySelectorAllArray('.file,.icon-folder-closed')
const getSelectedFile = () => getClassNameElementsArray('file-selected')[0]
const addScrollHints = () => {
getClassNameElementsArray('sages-extra-breadcrumbs').forEach(e => e.parentNode.removeChild(e))
const buildHint = (fileElement, prefix, top) => {
const hint = document.createElement('div', {})
hint.classList.add('sages-extra-breadcrumbs')
if (fileElement.classList.contains('file-viewed')) {
hint.classList.add('sages-extra-breadcrumbs-viewed')
}
hint.innerText = `${prefix} ${decodeURIComponent(fileElement.firstElementChild?.href?.split('#')[1] || 'In Folder...')}`
if (top) {
hint.style.top = top
}
return hint;
}
const selectedFile = getSelectedFile()
const fileElements = getFileElements()
const index = fileElements.indexOf(selectedFile)
const breadcrumbs = Array.from(getChangeHeader().getElementsByTagName('h4'))[0]
console.log({ breadcrumbs })
if (index > 0) {
breadcrumbs.prepend(buildHint(fileElements[index - 1], 'Prev:', '1.4em'))
}
if (index < fileElements.length - 1) {
breadcrumbs.appendChild(buildHint(fileElements[index + 1], 'Next:'))
}
}
const changeFile = (e, noRecurse) => {
const selectedFile = getSelectedFile()
const fileElements = getFileElements()
const up = e.wheelDelta ? e.wheelDelta > 0 : e.deltaY < 0
const index = fileElements.indexOf(selectedFile)
const nextElement =
(up && index > 0) ? fileElements[index - 1] :
(!up && index < (fileElements.length - 1)) ? fileElements[index + 1] :
null
const isFolder = !nextElement?.firstElementChild
const toClick = nextElement?.firstElementChild ?? nextElement
toClick?.focus()
toClick?.click()
if (isFolder) {
changeFile(e)
}
}
let styled = false
addFix(() => {
const header = getChangeHeader()
header.onwheel = changeFile
styled || addStyle(`
.sages-extra-breadcrumbs {
color: rgba(0, 0, 0, 0) !important;
margin-top: -1.6em !important;
/*margin-bottom: -1em !important;*/
float: left !important;
position: absolute;
font-size: 11px;
margin-left: 20%;
}
.diff-meta:hover h4 .sages-extra-breadcrumbs {
color: rgba(0, 0, 0, 0.35) !important;
}
.diff-meta:hover h4 .sages-extra-breadcrumbs-viewed {
color: rgba(0, 0, 0, 0.2) !important;
}
`)
styled = true
})
window.addEventListener('load', () => setTimeout(addScrollHints, NEW_PAGE_DELAY))
addEventListener('hashchange', () => setTimeout(addScrollHints, 200))

View File

@ -1,11 +1,12 @@
{ {
"name": "ADD Git NoFork", "name": "Sage's BitBucket Addon",
"description": "Ensure searches don't include forks", "description": "Ensure searches don't include forks (and other enhancements)",
"version": "1.0.2", "version": "1.0.13",
"manifest_version": 2, "manifest_version": 2,
"browser_specific_settings": { "browser_specific_settings": {
"gecko": { "gecko": {
"id": "svaillancourt@add123.com" "id": "svaillancourt@add123.com",
"update_url": "https://bb-addon.sagev.space/updates.json"
} }
}, },
"background": { "background": {
@ -25,8 +26,11 @@
"matches": ["*://git.add123.com/*"], "matches": ["*://git.add123.com/*"],
"js": [ "js": [
"utils.js", "utils.js",
"csharp-quotes.js", "all-ADD-repos.js",
"backtick-comments.js", "backtick-comments.js",
"csharp-quotes.js",
"file-scroll.js",
"repo-search-sorter.js",
"var-highlighter.js" "var-highlighter.js"
], ],
"run_at": "document_start" "run_at": "document_start"

36
repo-search-sorter.js Normal file
View File

@ -0,0 +1,36 @@
const compareResults = currentUser => (a, b) => {
return ['/projects/', currentUser]
.some(href => a.children[0].href.includes(href)) ? -1 : 1
}
const sortChildren = ol => {
const children = Array.from(ol.children)
children.forEach(child => ol.removeChild(child))
children
.sort(compareResults(getCurrentUser()))
.forEach(child => ol.appendChild(child))
}
/// Re-order the search results to list projects and the current user first
let claim = 0
const sortSearchResult = () => {
const searchBox = Array.from(document.getElementsByTagName('input'))
.filter(input => input.name === 'repository-search')[0]
if (!searchBox) {
return
}
searchBox.onkeyup = e => {
claim++
const myClaim = claim
setTimeout(() => {
if (claim !== myClaim) {
return
}
const ol = getClassNameElementsArray('search-results')[0]?.firstChild
sortChildren(ol)
claim = 0
}, 1000)
}
}
addFix(sortSearchResult)

View File

@ -2,12 +2,25 @@ const NEW_PAGE_DELAY = 1000
const CURRENT_PAGE_DELAY = 300 const CURRENT_PAGE_DELAY = 300
const getClassNameElementsArray = className => const getClassNameElementsArray = className =>
Object.values(document.getElementsByClassName(className)) Array.from(document.getElementsByClassName(className))
// Runs on "complete" load and when https://urlurl.url#this-hash-value changes const querySelectorAllArray = selector =>
Array.from(document.querySelectorAll(selector))
/// Runs on "complete" load and when `https://website.tld#this-hash-value` changes
const addFix = fixFunc => { const addFix = fixFunc => {
window.addEventListener('load', () => setTimeout(fixFunc, NEW_PAGE_DELAY)) window.addEventListener('load', () => setTimeout(fixFunc, NEW_PAGE_DELAY))
addEventListener('hashchange', () => setTimeout(fixFunc, CURRENT_PAGE_DELAY)) addEventListener('hashchange', () => setTimeout(fixFunc, CURRENT_PAGE_DELAY))
} }
const getFileName = () => getClassNameElementsArray('file-breadcrumbs-segment-highlighted').map(e => e.innerText)[0] const getFileName = () => getClassNameElementsArray('file-breadcrumbs-segment-highlighted').map(e => e.innerText)[0]
const getCurrentUser = () => {
return getClassNameElementsArray("user-dropdown-trigger")[0].title.replace(/(.*\()|\)/g, '')
}
const addStyle = css => {
const styleElement = document.createElement('style')
styleElement.innerText = css.replaceAll("\n", " ")
document.head.appendChild(styleElement)
}

View File

@ -1,29 +1,44 @@
const getVarElements = () => [ const getVarElements = () => [
...getClassNameElementsArray('hl-variable'), ...getClassNameElementsArray('hl-variable'),
...getClassNameElementsArray('hl-variable-2'),
...getClassNameElementsArray('hl-def'), ...getClassNameElementsArray('hl-def'),
...getClassNameElementsArray('hl-attribute'),
...getClassNameElementsArray('hl-property'),
...getClassNameElementsArray('hl-tag'),
...getClassNameElementsArray('hl-string'),
...getClassNameElementsArray('hl-string-2'),
...getClassNameElementsArray('hl-type') ...getClassNameElementsArray('hl-type')
] ]
const selectedClass = 'sages-selected-variable'
let currentSelected let currentSelected
const cleanVarName = varName => {
if (varName.startsWith('/') && varName.endsWith('>')) {
varName = varName.substring(1, varName.length - 1)
}
varName = varName.replaceAll('"', "'")
return varName
}
const selectVar = varName => { const selectVar = varName => {
if (!varName) { if (!varName) {
return return
} }
varName = cleanVarName(varName)
// Clear existing colors // Clear existing colors
getClassNameElementsArray(selectedClass) getClassNameElementsArray(selectedClass)
.forEach(e => e.classList.remove(selectedClass)) .forEach(e => e.classList.remove(selectedClass))
// Color vars with matching text // Color vars with matching text
getVarElements() getVarElements()
.filter(e => e.innerText === varName) .filter(e => cleanVarName(e.innerText) === varName)
.forEach(e => e.classList.add(selectedClass)) .forEach(e => e.classList.add(selectedClass))
currentSelected = varName currentSelected = varName
} }
const selectedClass = 'sages-selected-variable'
const getTagMatching = (name, matcher) => { const getTagMatching = (name, matcher) => {
const elements = Object.values(document.getElementsByTagName(name)).filter(matcher) const elements = Object.values(document.getElementsByTagName(name)).filter(matcher)
return elements.length ? elements[0] : null return elements.length ? elements[0] : null