Possible to specify scripts for all domains?
gingerbeardman opened this issue · comments
As title.
Found it.
When visiting a page, e.g. https://en.wikipedia.org/wiki/Apple, the Safari extension will inject the following files into the page if they are present in the respective folders selected above:
∙ default.css ∙ default.js
∙ org.css ∙ org.js
∙ wikipedia.org.css ∙ wikipedia.org.js
∙ en.wikipedia.org.css ∙ en.wikipedia.org.jsNote that JS files are injected once the DOM is ready, i.e. once the DOMContentLoaded event has fired.
Curious, mind sharing what you're doing on all domains? 😊
Sure!
I'm replacing a bunch of legacy Safari Extensions — that injected simple scripts to every page with no user interface or options — which stopped working in Safari 12 (High Sierra, Mojave, etc)
// Remove overflow:hidden
// for sites that block scroll on popup
document.body.style.overflow = 'initial !important';
// Show Password on Double Click
function handleDblClick(e){
var pwd = e.target;
var type = pwd.getAttribute('type');
pwd.setAttribute('type', type === 'password' ? 'text': 'password');
}
var passwords = document.querySelectorAll("input[type=password]");
for (var i = 0; i < passwords.length; i++){
passwords[i].addEventListener('dblclick', handleDblClick, true);
}
// NavUpKey, by canisbos
// cmd+alt+up to go up the URL path
String.prototype.reverse = function () {
var rs = '';
for (var i = this.length - 1; i >= 0; i--)
rs += this[i];
return rs;
}
window.addEventListener('keydown', function (e) {
if (e.which === 38 && (e.shiftKey * 1 + e.ctrlKey * 2 + e.altKey * 4 + e.metaKey * 8) === 12) {
var l = window.location;
var rp = l.pathname.reverse();
var rpsi = rp.search(/.\//);
if (rpsi > -1) {
l.assign(l.protocol + '//' + l.host + rp.slice(rpsi+1).reverse());
}
}
}, true);
// Smart Right Click, by Mattijs
// allows right clicking of things that are blocked by other elements
document.addEventListener("mousedown", function(event) {
if (event.which == 3) {
event.stopPropagation();
}
}, true);
document.addEventListener("contextmenu", function(event) {
// show browser default right click only
event.stopPropagation();
// not a trusted context menu event
if (!event.isTrusted || event.constructor.name != "MouseEvent") {
return false;
}
// check if someone would right click this element for a specific reason
function isElementIntention(intention, element) {
var tag = element.nodeName.toLowerCase();
// checks
switch(intention) {
case "ignore":
// not an ignored tag
if (!["body", "html"].includes(tag)) {
return false;
}
break;
case "link":
// not within an "a" tag with a href
if (!element.closest("a[href]")) {
return false;
}
break;
case "text":
// hasn't made a selection
if (window.getSelection().toString().length < 1) {
return false;
}
break;
case "media":
// not a media tag
if (!["img", "video"].includes(tag)) {
return false;
}
switch(tag) {
case "video":
// doesn't have a source
if (element.currentSrc.length < 1) {
return false;
}
break;
case "img":
// doesn't have a source
if (element.currentSrc.length < 1) {
return false;
}
// didn't load
if (element.naturalWidth === 0) {
return false;
}
break;
}
// is not visible
if (getComputedStyle(element).opacity <= 0.1 || getComputedStyle(element).visibility == "hidden") {
return false;
}
break;
}
// validated
return true;
};
// get the most likely reason someone would right click this element
function getElementIntention(element) {
var intention = null;
if (isElementIntention("ignore", element)) {
intention = "ignore";
} else if (isElementIntention("text", element)) {
intention = "text";
} else if (isElementIntention("link", element)) {
intention = "link";
}
if ((!intention || intention == "link") && isElementIntention("media", element)) {
intention = "media";
}
return intention;
};
// if unknown intention - check a bit deeper
var eventTargetIntention = getElementIntention(event.target);
if (!eventTargetIntention || eventTargetIntention == "link") {
// check if pointer intersects with any more likely targets
var timer = new Date();
var intendedTarget = null;
var potentialTargets = document.querySelectorAll("img, video");
for (var i = potentialTargets.length; i--;) {
var potentialTarget = potentialTargets[i];
var bounds = potentialTarget.getBoundingClientRect();
bounds = { l: bounds.left, t: bounds.top, r: bounds.left + bounds.width, b: bounds.top + bounds.height };
// check if it's under the pointer - and not hidden
if (event.clientX >= bounds.l && event.clientX <= bounds.r && event.clientY >= bounds.t && event.clientY <= bounds.b && isElementIntention("media", potentialTarget) && (eventTargetIntention != "link" || event.target.closest("a[href]").contains(potentialTarget))) {
intendedTarget = potentialTarget;
break;
}
// stop if it's taking too long
if ((new Date() - timer) > 10) {
break;
}
}
// if we found the real intended target
if (intendedTarget) {
// enable pointer events for the intended element
var disabled = {
elements: [intendedTarget],
pointerEvents: [{
value: intendedTarget.style.getPropertyValue("pointer-events"),
priority: intendedTarget.style.getPropertyPriority("pointer-events")
}]
}
intendedTarget.style.setProperty("pointer-events", "all", "important");
// go through disabling everything above the intended element so that the right click hits it
var timer = new Date();
var beneath = false;
for (var i = 0; i < 10; i++) {
var currentTarget = (i == 0) ? event.target : document.elementFromPoint(event.clientX, event.clientY);
// check if the loop should continue
if (!currentTarget || disabled.elements.indexOf(currentTarget) !== -1 && currentTarget !== intendedTarget) {
break;
// found intended target
} else if (currentTarget === intendedTarget || isElementIntention("media", currentTarget)) {
if (disabled.elements.length > 1) beneath = true;
break;
}
// push the element and style
disabled.elements.push(currentTarget);
disabled.pointerEvents.push({
value: currentTarget.style.getPropertyValue("pointer-events"),
priority: currentTarget.style.getPropertyPriority("pointer-events")
});
// add "pointer-events: none", to get to the underlying element
currentTarget.style.setProperty("pointer-events", "none", "important");
// stop if it's taking too long
if ((new Date() - timer) > 20) {
break;
}
}
// revert the changes
function revert(disabled) {
for (var i = disabled.pointerEvents.length; d = disabled.pointerEvents[--i];) {
var element = disabled.elements[i];
element.style.setProperty("pointer-events", (d.value ? d.value : ""), d.priority);
if (!element.getAttribute("style")) {
element.removeAttribute("style");
}
}
};
// change right click target
if (beneath) {
setTimeout(revert, 10, disabled);
// notify my other extensions to check the right click event again
intendedTarget.dispatchEvent(new MouseEvent(event.type, event));
// keep it as is
} else {
revert(disabled);
}
// check parent window instead and somehow set "pointer-events: none" to this iframe
} else if (window !== window.top) {
// ..
}
}
}, true);
@gingerbeardman This is super cool! Thanks for sharing
@gingerbeardman Also, sorry to bother you—how did you install these? I assume you put them in a default.js
file? Also, where did you find these, did you write them yourself? Thanks!
@sn0wyfall yes default.js
credits are next to each snippet. Canisbos, Mattijs you'd have to Google to find the source. Probably stack overflow or an old open source safari extension.
The top two short pieces I believe are mine. It's been too long for me to really remember.
My bad, I can't read and I just realized what you the quote meant after reading it.
Thanks for the reply! I was hoping to find a way to replicate StopTheMadness, but I guess that's being too greedy :D. Thanks for posting your script! It'll be really useful.
You could replicate that, but you'd either have to steal the JS/CSS (not good) or rewrite your own versions (good luck).
Yeah, you're right—I think I'll just stick with these scripts for now. Not such a huge deal anyway! It does most of what I would have wanted it to do.
@gingerbeardman Sorry for bothering you. I've been trying to get those JavaScript scripts to work, but have not been successful yet. I tried going here and double clicking on the password, but it didn't show up. I also tried going on an Instagram page and scrolling, but it didn't let me after the log in popup appeared. I tried going on this site and tried to right click on the image, but again no go. It seems like my JS isn't working.
But that's strange, since my CSS works and I put the JS all in a default.js
file, copied exactly as you have it here. Do you have any examples of where it works? I would love to test it out.
@sn0wyfall a few things:
- I just checked and I no longer use all of the JS above, including the right click image script.
- Perhaps Safari has changed JS permissions, the password snippet no longer works for me, sorry.
- the overflow script only works on pages that set it as part of the load, if the page sets it dynamically (like Instagram) then it will not work and would need to be rewritten to work on dynamic pages.
Ideally, there would be a repo of small snippets with test URLs, but we're not there yet.
Got it! Thanks a ton for your insight :)