I frequent a forum that has a horrible way of ignoring users. If you place someone on ignore, it almost makes that users presence more prevalent.
So I wrote this to hide them completely:
// ==UserScript==
// #name Freddie
// #namespace http://tampermonkey.net/
// #version 0.1
// #description hide annoying forum users
// #author You
// #match http://www.scout.com/college/kansas/forums/*
// #grant none
// ==/UserScript==
/* jshint -W097 */
'use strict';
function checkForDiv() { // crappy workaround function to wait for AJAX content
if (!document.getElementById("wrapper")) {
setTimeout(checkForDiv, 300);
} else {
checkNames();
}
}
function checkNames() {
var mybannedList = ["Pyros", "GOHawksGators", "th30r3o"]; // add usernames here
var nms = document.body.querySelectorAll('a.authName'), i = 0, len = nms.length;
for (i; i < len; i++) {
if (mybannedList.indexOf(nms[i].innerHTML) != -1) {
nms[i].parentNode.parentNode.style.display = "none";
}
}
}
checkForDiv();
But when you go to the page with the ignored users they still appear, upon refreshing, the script runs and they disappear.
Please good sirs, what do I do?
The site uses AJAX for navigation so the page address changes without reloading, that's why Tampermonkey doesn't inject your script when you navigate from another page on that site.
The simplest solution would be to include the entire site: // #match http://www.scout.com/*
There are other more advanced methods of detecting page transitions based on MutationObserver or some DOM event or property change that occurs on navigation.
Also beware of #grant none with jQuery loaded via #require: it breaks sites that also load jQuery unless you use jQuery.noConflict. The simplest solution is to remove that line as you don't need to access the web page variables.
P.S. There's a known timer-based wrapper: waitForKeyElements.
Related
In our company, we are using the firefox greasemonkey addon to fill in login credentials on a webpage to open it and login automatically.
This is what the script looks like:
// ==UserScript==
// #name logon
// #namespace https://www.example.com
// #include https://www.example.com
// #version 1
// #grant none
// #require http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js
// ==/UserScript==
$(document).ready(function() {
$('input[name=username]').val('*****');
$('input[name=passcode]').val('*****');
$('input[name=send]').click();
});
Since upgrading to Firefox version 70, the script does not enter the login credentials anymore.
I am under the impression that this is due to the page loading to slowly and the script executing to fast.
I would therefore like to insert a timeout in this script. So that it will only execute after 5 seconds after opening the page.
I have tried several examples found online, but none seems to work.
Help would be greatly appreciated!
Thanks
If your theory about why it does not work is correct, the code below should work. Observe some other changes such as:
JQuery is not needed for trivial things like this. Or any things. Just don't. It adds a dependency on your code for little benefit.
You should not store password in plaintext script, but if you do at least put it in variable so that it's obvious to non-programmers where to change it.
Using someinput.form.submit() also submits the form, and removes one input on which existence you have to rely on (the script still works if the submit button is renamed).
// ==UserScript==
// #name logon
// #namespace https://www.example.com
// #include https://www.example.com
// #version 1
// #grant none
// #rut-at document-start
// ==/UserScript==
const TIMEOUT = 2000;
const NAME = "***";
const PASSWORD = "***";
document.addEventListener("load", function() {
setTimeout(function() {
document.querySelector("input[name=username]").value = NAME;
document.querySelector("input[name=passcode]").value = PASSWORD;
document.querySelector("input[name=passcode]").form.submit();
}, TIMEOUT);
});
But your original script may also have broken to other things than what you think, such as:
Input fields renamed. Use dev tools to inspect them and see if there's any name, class name or ID.
Framework is now used that does not read the values from fields, but only updates it's internal values when you type in them
Look in dev console to errors. Next time when asking, also demonstrate what have you tried and how it failed, otherwise you will not learn as much as you could have.
Trying to create a Greasemonkey script to auto-fill some forms, and I want the script to go to another URL after the form is submitted.
// ==UserScript==
// #name usersaime
// #description fills data form for user and pass
// #include https://tramites.saime.gob.ve/*
// #version 1.1
// #grant none
// ==/UserScript==
document.getElementById('LoginForm_username').value = "user";
document.getElementById('LoginForm_password').value = "1234";
setTimeout(function() {
document.getElementById('login_button').click();
}, 2000);
window.addEventListener('load', function(event) {
// all resources finished loading
window.location = 'https://tramites.saime.gob.ve/index/example/example';
});
The window.location is not working at all. I also tried window.location.href and window.location.replace, and a setTimeout function to the window.location. Nothing works.
There are no errors on the console.
Tried:
Firefox 59 + Greasemonkey 4.3
Chrome 66 + Tampermonkey 4.5
The login page/form is https://tramites.saime.gob.ve/index.php?r=site/login.
And on successful login it goes to https://tramites.saime.gob.ve/index.php?r=tramite/tramite/ -- which I want to redirect.
Simple, immediate answer:
The reason window.location is not working for you is because:
The window.location call is made inside a window load event handler.
That target page is one of those were the load event fires almost immediately after the DOMContentLoaded event.
By default userscripts trigger at DOMContentLoaded, but in this case, the load event has already fired by the time your script runs.
The real answer:
There are many problems with that question code:
It's not tracking and accounting for the state of the process. The script fires on many (kinds of) pages and must behave differently depending on state.
The page(s) is/are extensively AJAX-driven. The code is not properly accounting for that.
Load event is not particularly useful in this kind of scenario.
The aforementioned, script-firing versus load, race condition.
Hard-coding login credentials in a script like that means that you will get pwned, it's just a question of when and how much damage.
You need to track and differentiate between at least 3 different states, using a combination of the pages' URLs and/or key nodes on the pages and/or state variables that the script stores/passes.
The following userscript illustrates the process, although I can't test it on the site and the recommended authentication framework is omitted for simplicity. :
// ==UserScript==
// #name _Login and then redirect an AJAX-driven page
// #include https://tramites.saime.gob.ve/*
// #require https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// #require https://gist.github.com/raw/2625891/waitForKeyElements.js
// #grant GM_addStyle
// #grant GM.getValue
// ==/UserScript==
//- The #grant directives are needed to restore the proper sandbox.
//--- Different pages require different actions
if (location.search.includes ("site/login") ) { // initial login page
//-- Wait for page to initialize
waitForKeyElements ("#login_button:visible", loginWhenReady);
}
else if (location.search.includes ("tramite/tramite") ) { // successful login page
//-- Just redirect
location.assign ("https://tramites.saime.gob.ve/index/example/example");
}
else {
//-- All other pages, no action needed.
}
function loginWhenReady (jNode) {
//-- Demo purposes only! Use framework or password manager instead!
$("#LoginForm_username").val ("user");
$("#LoginForm_password").val ("1234");
clickNode (jNode); // Login button passed in by WFKE
}
function clickNode (jNode) {
var clickEvent = document.createEvent ('MouseEvents');
clickEvent.initEvent ('click', true, true);
jNode[0].dispatchEvent (clickEvent);
}
Possible quick and easy solution:
If-and-only-if (IIF) the post login page is always
https://tramites.saime.gob.ve/index.php?r=tramite/tramite/.
AND IIF that page is used for nothing else that you care about...
Then the following userscript may suffice for your needs:
// ==UserScript==
// #name _Quick and dirty post login redirect
// #include https://tramites.saime.gob.ve/index.php?r=tramite/tramite*
// #grant none
// #run-at document-start
// ==/UserScript==
location.replace ("https://tramites.saime.gob.ve/index/example/example");
In my script for Greasemonkey/Tampermonkey is working perfectly in Google Chrome but in Firefox this:
waitForKeyElements ("table#ID-rowTable tr td span._GAmD", replaceAffid);
is not waiting for AJAX loaded content.
Here is the relevant part of my script:
// ==UserScript==
// #name ChangeProvider
// #description Change Provider Name
// #include https://analytics.google.com/analytics/web/*
// #version 1
// #grant GM_xmlhttpRequest
// ==/UserScript==
<snip>...
waitForKeyElements ("table#ID-rowTable tr td span._GAmD", replaceAffid);
<snip>...
function waitForKeyElements (
selectorTxt, /* Required: The jQuery selector string that
specifies the desired element(s).
*/
actionFunction, /* Required: The code to run when elements are
found. It is passed a jNode to the matched
element.
*/
bWaitOnce, /* Optional: If false, will continue to scan for
new elements even after the first match is
found.
*/
iframeSelector /* Optional: If set, identifies the iframe to
search.
*/
) {
var targetNodes, btargetsFound;
if (typeof iframeSelector == "undefined")
targetNodes = $(selectorTxt);
else
targetNodes = $(iframeSelector).contents ()
.find (selectorTxt);
<snip>...
The full code is at pastebin.com.
The problem(s) is/are:
waitForKeyElements() requires jQuery.
Your script must either provide jQuery (recommended), or use #grant none mode and be running on a page that already uses jQuery (a brittle way to do things, AKA "time-bomb code").
Tampermonkey has a bug and possible security weakness whereby it doesn't always
sandbox properly. This means that the script can (sometimes) see the page's jQuery, even when #grant is not none. This allowed the script to run in Chrome (for now) but is a very dicey thing to depend on.
Firefox properly sandboxes the scope when you use #grant GM_ ... so the script cannot see the page's jQuery.
If you had looked at Firefox's Browser Console, you would have seen error messages pointing you to the problem.
The solution is: Don't use waitForKeyElements without #requireing jQuery!
In fact, you should require both libraries, as shown in this answer, as it (A) Runs faster, (B) Only fetches the code once, when you install/edit the userscript, and (C) makes for cleaner, easier to grok code.
So, your entire script would become something like:
// ==UserScript==
// #name GoogleAnalytics Change Provider
// #description Change Provider Name
// #include https://analytics.google.com/analytics/web/*
// #version 1
// #require http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
// #require https://gist.github.com/raw/2625891/waitForKeyElements.js
// #grant GM_xmlhttpRequest
// ==/UserScript==
var providers = new Array ();
GM_xmlhttpRequest ( {
method: "GET",
url: "http://localhost:3000/api/t2_provider_list",
onload: function (response) {
var provider_list = JSON.parse (response.responseText);
for (i = 0; i < provider_list.length; i++) {
providers[provider_list[i].analytics_prefix] = provider_list[i].provider_name;
}
waitForKeyElements ("table#ID-rowTable tr td span._GAmD", replaceAffid);
}
} );
/*--- replaceAffid (): Match the fields with a given pattern
and replace with the provider name and affid
*/
function replaceAffid () {
console.log (providers);
var regExp = /([a-z,A-Z,0-9]+)---([a-z,A-Z,0-9,_,-]+)/g;
var spans = document.evaluate ("//span[contains(#class, '_GAmD') and not(contains(#class, '_GAe'))]/text()", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
console.log (spans);
for (var i = 0; i < spans.snapshotLength; i++) {
match = regExp.exec (spans.snapshotItem (i).textContent);
if (match != null) {
if (typeof providers[match[1]] === undefined) {
// do nothing
} else {
spans.snapshotItem (i).textContent = "Provider: " + providers[match[1]] + " \n\r Affid: " + match[2];
}
}
}
}
Finally, it looks like you pasted in an old version of waitForKeyElements.
Since May of 2012, that function has had this text near the top:
IMPORTANT: This function requires your script to have loaded jQuery.
If you grabbed your copy of the function from one of my old answers, I apologize. I just updated it to avoid a repeat of this confusion.
I am currently using the following script in Tampermonkey in Google Chrome:
// ==UserScript==
// #name Youtube opt in Ads per channel
// #namespace schippi
// #include http://www.youtube.com/watch*
// #version 1
// ==/UserScript==
var u = window.location.href;
if (u.search("user=") == -1) {
var cont = document.getElementById("watch7-user-header").innerHTML;
var user=cont.replace(/.+\/user\//i,'').replace(/\?(?:.|\s)*/m,'');
window.location.href = u+"&user="+user;
}
It seems to work perfectly in Firefox with Greasemonkey but in Google Chrome, it seems that it only works on the first click on a YouTube video.
More specifically, if I click on a YouTube video:
youtube.com/watch?v=MijmeoH9LT4,
it redirects me to:
youtube.com/watch?v=MijmeoH9LT4&user=Computerphile
However, if I click on a video from the related videos vertical bar, it doesn't seem to do any further redirection.
Alas, there is still no really "neat" way to do this in Chrome. (Firefox has more options.)
Your best bet is just to poll location.search; see below.
Your other options in Chrome, currently, are not recommended -- but here they are for reference:
Hack into the history.pushState function. This gives faster notice of page changes, BUT fires before you can run your code, so it will still need a timer. Plus, it brings in cross-scope problems, in a userscript environment.
Use Mutation Observers to monitor changes to the <title> tag. this may work okay, but may fire after you want it to, causing delays and pronounced "flicker". Also may not work on poorly designed pages (YouTube is okay).
Also note that the replace() statements, from the question, will blow up the URL and 404 the script in several cases. Use DOM methods to get the user (see below).
Polling code (Simple, robust, cross-browser):
// ==UserScript==
// #name Youtube opt in Ads per channel
// #namespace schippi
// #include http://www.youtube.com/watch*
// #version 1
// #grant GM_addStyle
// ==/UserScript==
/*- The #grant directive is needed to work around a design change
introduced in GM 1.0. It restores the sandbox.
*/
var elemCheckTimer = null;
var pageURLCheckTimer = setInterval (
function () {
if (this.lastQueryStr !== location.search) {
this.lastQueryStr = location.search;
gmMain ();
}
}
, 111 //-- Nine times a second. Plenty fast w/o bogging page
);
function gmMain () {
if ( ! /user=/.test (window.location.href) ) {
elemCheckTimer = setInterval (checkUserAndRelocate, 24);
}
}
function checkUserAndRelocate () {
var elem = document.querySelector (
"#watch7-user-header a[href*='/user/']"
);
if (elem) {
clearInterval (elemCheckTimer);
var user = elem.href.match (/\/user\/(\w+)\W?/);
if (user && user.length > 1) {
location.replace (location.href + "&user=" + user[1]);
}
}
}
I have the following greasemonkey script currently:
// ==UserScript==
// #name test_script
// #namespace my.example.com
// #include http://example.com/*
// #require http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js
// #version 1
// #grant none
// ==/UserScript==
(function(window, undefined) {
// normalized window
var w;
if (unsafeWindow != "undefined"){
w = unsafeWindow
} else {
w = window;
}
// You can inject almost any javascript library here.
// Just pass the w as the window reference,
// e.g. jquery.min.js embedding:
// (function(a,b){function ci(a) ... a.jQuery=a.$=d})(w);
// do not run in frames
if (w.self != w.top){
return;
}
// additional url check.
// Google Chrome do not treat #match as intended sometimes.
if (/http:\/\/example.com\//.test(w.location.href)){
var link = $('span.link').text();
if (link) {
location.replace(link);
}
}
})(window);
Once I open example.com page, it looks for the URL here and redirects me. It works well, but redirection happens only when all images, style sheets etc. are loaded. How should I fix that? Looks like it should happen before.
The code you've shown, will fire before images are loaded, but not necessarily before CSS.
From Mozilla's DOMContentLoaded doc (and verified in Gecko, Webkit and IE):
Stylesheet loads block script execution, so if you have a <script> after a <link rel="stylesheet" ...>, the page will not finish parsing - and DOMContentLoaded will not fire - until the stylesheet is loaded.
Obviously, if the CSS and/or images are small or cached, they might also appear to load before the script fires.
You might be able to work around this using #run-at document-start. See this answer, and similar, for example.