Reputation: 426
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>...
Upvotes: 2
Views: 1970
Reputation: 93473
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 @require
ing 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.
Upvotes: 1