-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Feature] Custom domain support (#1)
* made each provider a function so it always gives a new object instead of a reference, added name property, added domains property, removed domain property, added canSelfHost property * getGitProvider now tests only the host of the url instead of the whole string. Removed switch statement for thesting domains in favor of a for loop which will allow custom domains. added new function addGitProvider which adds a copy of the config for given provider and new custom domain * added custom providers library * added loading of custom providers * change async/await calls with with promise.then * added detect property to provider.selectors object * added flag isCustom to all custom provider objects for use in filtering * added mozilla webextension polyfill so storage works across browsers * wip: custom domains * added wildcard for content scripts, so that popup can talk to current page * added eslint exception for parameter reassignment in custom-providers.js * added isCustom property to each provider, so it is consistent in all provider objects * Closes #82 * Closes #76 * Added previous selectors as fallback for older instances * Added ability to opt in to custom websites instead of the extension running on all of them by default. Only chromium browsers supported. Firefox does not allow requesting permissions from background scripts --------- Co-authored-by: Michael Goodman <bulgedition@gmail.com>
- Loading branch information
1 parent
71ff6c0
commit e153f99
Showing
24 changed files
with
2,466 additions
and
603 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import Browser from 'webextension-polyfill'; | ||
|
||
Browser.runtime.onMessage.addListener((message) => { | ||
if (message.event === 'request-access') { | ||
const perm = { | ||
permissions: ['activeTab'], | ||
origins: [`*://${message.data.host}/*`], | ||
}; | ||
|
||
Browser.permissions.request(perm).then((granted) => { | ||
if (!granted) { | ||
return; | ||
} | ||
|
||
// run the script now | ||
Browser.scripting.executeScript({ | ||
files: ['./main.js'], | ||
target: { | ||
tabId: message.data.tabId, | ||
}, | ||
}); | ||
|
||
// register content script for future | ||
return Browser.scripting.registerContentScripts([ | ||
{ | ||
id: 'github-material-icons', | ||
js: ['./main.js'], | ||
css: ['./injected-styles.css'], | ||
matches: [`*://${message.data.host}/*`], | ||
runAt: 'document_start', | ||
}, | ||
]); | ||
}); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import Browser from 'webextension-polyfill'; | ||
|
||
export const getCustomProviders = () => | ||
Browser.storage.sync.get('customProviders').then((data) => data.customProviders || {}); | ||
export const addCustomProvider = (name, handler) => | ||
getCustomProviders().then((customProviders) => { | ||
customProviders[name] = handler; | ||
|
||
return Browser.storage.sync.set({ customProviders }); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,49 @@ | ||
import Browser from 'webextension-polyfill'; | ||
import { getGitProvider } from './providers'; | ||
import { observePage, replaceAllIcons } from './lib/replace-icons'; | ||
import { initIconSizes } from './lib/icon-sizes'; | ||
import { getConfig, onConfigChange } from './lib/userConfig'; | ||
|
||
initIconSizes(); | ||
const { href } = window.location; | ||
const gitProvider = getGitProvider(href); | ||
|
||
Promise.all([ | ||
getConfig('iconPack'), | ||
getConfig('extEnabled'), | ||
getConfig('extEnabled', 'default'), | ||
]).then(([iconPack, extEnabled, globalExtEnabled]) => { | ||
if (!globalExtEnabled || !extEnabled || !gitProvider) return; | ||
observePage(gitProvider, iconPack); | ||
onConfigChange('iconPack', (newIconPack) => replaceAllIcons(gitProvider, newIconPack)); | ||
function init() { | ||
initIconSizes(); | ||
|
||
const { href } = window.location; | ||
|
||
getGitProvider(href).then((gitProvider) => { | ||
Promise.all([ | ||
getConfig('iconPack'), | ||
getConfig('extEnabled'), | ||
getConfig('extEnabled', 'default'), | ||
]).then(([iconPack, extEnabled, globalExtEnabled]) => { | ||
if (!globalExtEnabled || !extEnabled || !gitProvider) return; | ||
observePage(gitProvider, iconPack); | ||
onConfigChange('iconPack', (newIconPack) => replaceAllIcons(gitProvider, newIconPack)); | ||
}); | ||
}); | ||
} | ||
|
||
const handlers = { | ||
init, | ||
|
||
guessProvider(possibilities) { | ||
for (const [name, selector] of Object.entries(possibilities)) { | ||
if (document.querySelector(selector)) { | ||
return name; | ||
} | ||
} | ||
|
||
return null; | ||
}, | ||
}; | ||
|
||
Browser.runtime.onMessage.addListener((message, sender, response) => { | ||
if (!handlers[message.cmd]) { | ||
return response(null); | ||
} | ||
|
||
const result = handlers[message.cmd].apply(null, message.args || []); | ||
|
||
return response(result); | ||
}); | ||
|
||
init(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,57 +1,72 @@ | ||
/** The name of the class used to hide the pseudo element `:before` on Azure */ | ||
const HIDE_PSEUDO_CLASS = 'material-icons-exension-hide-pseudo'; | ||
|
||
const azureConfig = { | ||
domain: 'dev.azure.com', | ||
selectors: { | ||
row: 'table.bolt-table tbody > a', | ||
filename: 'table.bolt-table tbody > a > td[aria-colindex="1"] span.text-ellipsis', | ||
icon: 'td[aria-colindex="1"] span.icon-margin', | ||
}, | ||
getIsLightTheme: () => | ||
document.defaultView.getComputedStyle(document.body).getPropertyValue('color') === | ||
'rgba(0, 0, 0, 0.9)', // TODO: There is probably a better way to determine whether Azure is in light mode | ||
getIsDirectory: ({ icon }) => icon.classList.contains('repos-folder-icon'), | ||
getIsSubmodule: () => false, // There appears to be no way to tell if a folder is a submodule | ||
getIsSymlink: ({ icon }) => icon.classList.contains('ms-Icon--PageArrowRight'), | ||
replaceIcon: (svgEl, newSVG) => { | ||
newSVG.style.display = 'inline-flex'; | ||
newSVG.style.height = '1rem'; | ||
newSVG.style.width = '1rem'; | ||
export default function azure() { | ||
return { | ||
name: 'azure', | ||
domains: [ | ||
{ | ||
host: 'dev.azure.com', | ||
test: /^dev\.azure\.com$/, | ||
}, | ||
{ | ||
host: 'visualstudio.com', | ||
test: /.*\.visualstudio\.com$/, | ||
}, | ||
], | ||
selectors: { | ||
row: 'table.bolt-table tbody tr.bolt-table-row, table.bolt-table tbody > a', | ||
filename: | ||
'td.bolt-table-cell[data-column-index="0"] .bolt-table-link .text-ellipsis, table.bolt-table tbody > a > td[aria-colindex="1"] span.text-ellipsis', | ||
icon: 'td.bolt-table-cell[data-column-index="0"] span.icon-margin, td[aria-colindex="1"] span.icon-margin', | ||
// Element by which to detect if the tested domain is azure. | ||
detect: 'body > input[type=hidden][name=__RequestVerificationToken]', | ||
}, | ||
canSelfHost: false, | ||
isCustom: false, | ||
getIsLightTheme: () => | ||
document.defaultView.getComputedStyle(document.body).getPropertyValue('color') === | ||
'rgba(0, 0, 0, 0.9)', // TODO: There is probably a better way to determine whether Azure is in light mode | ||
getIsDirectory: ({ icon }) => icon.classList.contains('repos-folder-icon'), | ||
getIsSubmodule: () => false, // There appears to be no way to tell if a folder is a submodule | ||
getIsSymlink: ({ icon }) => icon.classList.contains('ms-Icon--PageArrowRight'), | ||
replaceIcon: (svgEl, newSVG) => { | ||
newSVG.style.display = 'inline-flex'; | ||
newSVG.style.height = '1rem'; | ||
newSVG.style.width = '1rem'; | ||
|
||
if (!svgEl.classList.contains(HIDE_PSEUDO_CLASS)) { | ||
svgEl.classList.add(HIDE_PSEUDO_CLASS); | ||
} | ||
|
||
// Instead of replacing the child icon, add the new icon as a child, | ||
// otherwise Azure DevOps crashes when you navigate through the repository | ||
if (svgEl.hasChildNodes()) { | ||
svgEl.replaceChild(newSVG, svgEl.firstChild); | ||
} else { | ||
svgEl.appendChild(newSVG); | ||
} | ||
}, | ||
onAdd: (row, callback) => { | ||
// Mutation observer is required for azure to work properly because the rows are not removed | ||
// from the page when navigating through the repository. Without this the page will render | ||
// fine initially but any subsequent changes will reult in inaccurate icons. | ||
const mutationCallback = (mutationsList) => { | ||
// Check whether the mutation was made by this extension | ||
// this is determined by whether there is an image node added to the dom | ||
const isExtensionMutation = mutationsList.some((mutation) => | ||
Array.from(mutation.addedNodes).some((node) => node.nodeName === 'IMG') | ||
); | ||
if (!svgEl.classList.contains(HIDE_PSEUDO_CLASS)) { | ||
svgEl.classList.add(HIDE_PSEUDO_CLASS); | ||
} | ||
|
||
// If the mutation was not caused by the extension, run the icon replacement | ||
// otherwise there will be an infinite loop | ||
if (!isExtensionMutation) { | ||
callback(); | ||
// Instead of replacing the child icon, add the new icon as a child, | ||
// otherwise Azure DevOps crashes when you navigate through the repository | ||
if (svgEl.hasChildNodes()) { | ||
svgEl.replaceChild(newSVG, svgEl.firstChild); | ||
} else { | ||
svgEl.appendChild(newSVG); | ||
} | ||
}; | ||
}, | ||
onAdd: (row, callback) => { | ||
// Mutation observer is required for azure to work properly because the rows are not removed | ||
// from the page when navigating through the repository. Without this the page will render | ||
// fine initially but any subsequent changes will reult in inaccurate icons. | ||
const mutationCallback = (mutationsList) => { | ||
// Check whether the mutation was made by this extension | ||
// this is determined by whether there is an image node added to the dom | ||
const isExtensionMutation = mutationsList.some((mutation) => | ||
Array.from(mutation.addedNodes).some((node) => node.nodeName === 'IMG') | ||
); | ||
|
||
const observer = new MutationObserver(mutationCallback); | ||
observer.observe(row, { attributes: true, childList: true, subtree: true }); | ||
}, | ||
}; | ||
// If the mutation was not caused by the extension, run the icon replacement | ||
// otherwise there will be an infinite loop | ||
if (!isExtensionMutation) { | ||
callback(); | ||
} | ||
}; | ||
|
||
export default azureConfig; | ||
const observer = new MutationObserver(mutationCallback); | ||
observer.observe(row, { attributes: true, childList: true, subtree: true }); | ||
}, | ||
}; | ||
} |
Oops, something went wrong.