-
-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #52 from N1ck/fix-extension-injection
- Loading branch information
Showing
6 changed files
with
126 additions
and
37 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
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,26 @@ | ||
// Lovingly copied from https://github.com/refined-github/refined-github/blob/main/source/helpers/caller-id.ts | ||
import hashString from './hash-string.js' | ||
|
||
/** | ||
Get unique ID by using the line:column of the call (or its parents) as seed. Every call from the same place will return the same ID, as long as the index is set to the parents that matters to you. | ||
@param ancestor Which call in the stack should be used as key. 0 means the exact line where getCallerID is called. Defaults to 1 because it's usually used inside a helper. | ||
*/ | ||
export default function getCallerID(ancestor = 1) { | ||
/* +1 because the first line comes from this function */ | ||
return hashString(getStackLine(new Error('Get stack').stack, ancestor + 1)) | ||
} | ||
|
||
export function getStackLine(stack, line) { | ||
return ( | ||
stack | ||
// Remove non-stacktrace line from array (missing in Firefox) #6032 | ||
.replace('Error: Get stack\n', '') | ||
.split('\n') | ||
.at(line) ?? warn(stack, line) | ||
) | ||
} | ||
|
||
function warn(stack, line) { | ||
console.warn('The stack doesn’t have the line', {line, stack}) | ||
return Math.random().toString(16) | ||
} |
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 @@ | ||
// Lovingly copied from https://github.com/refined-github/refined-github/blob/main/source/helpers/hash-string.ts | ||
export default function hashString(string) { | ||
let hash = 0 | ||
|
||
for (const character of string) { | ||
hash = (hash << 5) - hash + character.codePointAt(0) | ||
} | ||
|
||
return String(Math.trunc(hash)) | ||
} |
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,54 @@ | ||
// Lovingly copied from https://github.com/refined-github/refined-github/blob/main/source/helpers/selector-observer.tsx | ||
// eslint-disable-next-line no-unused-vars | ||
import React from 'dom-chef' | ||
import {css} from 'code-tag' | ||
import onetime from 'onetime' | ||
|
||
import getCallerID from './caller-id.js' | ||
|
||
const animation = 'rgh-selector-observer' | ||
const getListener = (seenMark, selector, callback) => | ||
function (event) { | ||
const target = event.target | ||
// The target can match a selector even if the animation actually happened on a ::before pseudo-element, so it needs an explicit exclusion here | ||
if (target.classList.contains(seenMark) || !target.matches(selector)) { | ||
return | ||
} | ||
|
||
// Removes this specific selector’s animation once it was seen | ||
target.classList.add(seenMark) | ||
|
||
callback(target) | ||
} | ||
|
||
const registerAnimation = onetime(() => { | ||
document.head.append(<style>{`@keyframes ${animation} {}`}</style>) | ||
}) | ||
|
||
export default function observe(selectors, listener, {signal} = {}) { | ||
if (signal?.aborted) { | ||
return | ||
} | ||
|
||
const selector = String(selectors) // Array#toString() creates a comma-separated string | ||
const seenMark = 'rgh-seen-' + getCallerID() | ||
|
||
registerAnimation() | ||
|
||
const rule = document.createElement('style') | ||
|
||
rule.textContent = css` | ||
:where(${String(selector)}):not(.${seenMark}) { | ||
animation: 1ms ${animation}; | ||
} | ||
` | ||
document.body.prepend(rule) | ||
signal?.addEventListener('abort', () => { | ||
rule.remove() | ||
}) | ||
window.addEventListener( | ||
'animationstart', | ||
getListener(seenMark, selector, listener), | ||
{signal} | ||
) | ||
} |
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