I want to get GreaseMonkey to process a button click. The HTML for the button is generated in a perl CGI which is accessed by GM_xmlhttprequest. The javascript to handle the click is in my user script.
Here is the user script. It prepends a div to the top of a webpage and populates that div with what comes from my CGI via AJAX.
// ==UserScript==
// #name button test
// #namespace http://www.webmonkey.com
// #description test that I can intercept a button and process click with AJAX
// #include http*
// #version 1
// #grant GM_xmlhttpRequest
// ==/UserScript==
function processButton() {
alert("got to processButton");
}
var myDiv;
var details = GM_xmlhttpRequest({
method: 'GET',
url:"http://localhost/cgi-bin/buttonTest",
onload: function (response) {
myDiv = document.createElement('div');
myDiv.id = 'mydiv';
myDiv.style.border = '4px solid #000';
myDiv.innerHTML = response.responseText;
document.body.insertBefore(myDiv, document.body.firstChild);
}
});
Here's my CGI.
#!/usr/bin/perl -w
print "Content-type:text/html\n\n";
print qq|<button onclick="processButton()">Click here</button>| ;
When I load a web page I get a new div with the HTML button code in it as I expect. When I click the button nothing happens. No alert. I created an HTML example to make sure I wasn't doing something really stupid. The example works fine.
<html>
<head>
<script>
function processButton() {
alert("got to processButton");
}
</script>
</head>
<body>
<button onclick="processButton()">Click here</button>
</body>
</html>
There's a console error message:
ReferenceError: processButton is not defined
What am I missing?
If I declare the processButton function in the HTML code that includes the button declaration and then put this in my userScript then my code works:
unsafeWindow.processButton = function() {
alert("hijacked processButton");
}
In other words, I'm now able to hijack the button from GM.
Related
(Edit: the question have been rewritten to better isolate the issue.)
I'am writing a single D3js script.js which could be reused "as it" in both web browser for client side and nodejs for server side uses. I made some good progress :
a single d3js script.js indeed create basic svg shapes on both client and server sides. On web browser displaying the viz, on server side outputing the wanted map.svg file.
I have a more complex d3js map making code: client side it works like a charm (link)
but! the nodejs call fails without error message. The xhr d3.json() seems to be hanging up waiting and not firing up its callback:
mapIt.node.js (server side, fails)
Pass variables and run me:
$ WIDTH=800 ITEM="world-1e3" node script.node.js
Content of script.node.js:
var jsdom = require('jsdom'); // npm install jsdom
var fs = require('fs'); // natively in nodejs.
jsdom.env(
"<html><body></body></html>", // CREATE DOM HOOK
[ './d3.v3.min.js', // load assets into window environment (!)
'./topojson.v1.min.js',
'./queue.v1.min.js',
'./script.js' ],
function (err, window) {
/* ***************************************************************** */
/* Check availability of loaded assets in window scope. ************ */
console.log(typeof window.mapIt); // expect: 'function', because exist in script.js !
console.log(typeof window.doesntExist); // expect: 'undefined', because doesn't exist anywhere.
// if all as expected, should work !
/* ***************************************************************** */
/* COLLECT ENV.VARIABLES ******************************************* */
var width = process.env.WIDTH,
target= process.env.ITEM;
/* ***************************************************************** */
/* D3js FUNCTION *************************************************** */
var url = "http://rugger-demast.codio.io/output/"+target+"/administrative.topo.json";
console.log(url);
console.warn(window.document.URL);
var mapIt = function(width, target){
console.log("mapIt(): start");
var svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', width/960*500);
var projection = d3.geo.mercator()
.scale(100)
.translate([width / 2, height/2]);
var path = d3.geo.path()
.projection(projection);
var url = "http://rugger-demast.codio.io/output/"+target+"/administrative.topo.json";
/* FROM HERE SOMETHING FAILS ********************** */
d3.json(url, function (error, json) { // from here: NOT fired on server side :(
if (error) return console.warn(error);
d3.select("svg").append("g").attr("log","d3.json");
svg.append('path')
.datum(topojson.feature(json, json.objects.admin_0))
.attr('d', path)
.attr('class', 'L0');
});
};
window.mapIt(width,target);
/* ***************************************************************** */
/* SVG PRINT ******************************************************* */
// better svg header:
var svgheader = '<?xml version="1.0" encoding="utf-8"?>\n'
+'<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n';
// printing + timer, to be sure all is ready when printing file:
setTimeout(
fs.writeFileSync('map.svg', svgheader + window.d3.select("body").html()) ,
30000
);
}
);
map.svg (incomplete)
Since d3.json callback is not fired, I get the incomplete map.svg such:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="800" height="416.6666666666667"></svg>
Given map.svg's content, the script first work as expected, setting the svg width and height attributes, then the d3.json() doesn't work, the call back is never fired up. There is no error message back. But given where the script stop, it seems the query is hanging up waiting.
Notes:
The same exact d3js script works client side (link).
Interestingly, console.warn(window.document.URL) returns file:///data/yug/projects_active/map/script.node.js (purely local) while the d3.json() xhr request is on http://bl.ocks.org/hugolpz/raw/1c34e14d8b7dd457f802/administrative.topo.json.
Question
How to get the request working ? or How to run nodejs script with so the query is allowed ?
Testing
To try out the script (Github gist):
git clone https://gist.github.com/9bdc50271afc49d33e35.git ./map
cd ./map; npm install
WIDTH=800 ITEM="world-1e3" node script.node.js
Help: D3js>API>Requests
Replace :
setTimeout(
fs.writeFileSync('map.svg', svgheader + window.d3.select("body").html()) ,
10000
);
by
setTimeout(
function(){ fs.writeFileSync('map.svg', svgheader + window.d3.select("body").html()) } ,
10000
);
I would like to click on this link using Greasemonkey:
<a class="bbbx-button bbbx-button-grey" id="vote-button" data-bbbx-trigger="module.vote.submit" data-bbbx-id="51526" data-bbbx-score="10" data-bbbx-vote-type="up"><img src="//du3rc6beq6sv9.cloudfront.net/release1406643004647-5-1-2-1/assets/skins/default/images/vote/heart.png"> vote! <small id="vote-count">( 14 )</small></a>
I tried this code:
// ==UserScript==
// #name test
// #namespace test
// #include *
// #version 1
// #require http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js
// #grant GM_addStyle
// ==/UserScript==
console.log("Start");
window.setTimeout(start, 7000);
function start()
{
$("a#vote-button")[0].click();
console.log("End clicktime");
}
But unfortunately I can't find the problem.
You don't have jQuery loaded in your GM script.
Add this:
// #require http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js
Feel free to change the version.
Have a look at this question, it also provides some solutions for websites that already have jQuery loaded: How can I use jQuery in Greasemonkey?
Thanks to some awesome answers in other threads, I learned how to:
load jQuery and jQuery-UI in a bookmarklet
load a CSS into a bookmarklet using jQuery
create a DIV in a bookmarklet using jQuery
create jQuery UI dialog (external link)
and I could manage to combine all four things into a single script which works both as a BookMarklet and as a GreaseMonkey script, which is absolutely awesome.
However, it doesn't work on all sites. Neither the bookmarklet nor the GreaseMonkey script work on Wikipedia.
I tried it on other sites and it works: amazon.com, msn.com, yahoo.com, news.google.com, stackoverflow.com and it works well.
Is it possible to make it work on wikipedia.org?
Also there is a minor issue: each second time I use the bookmarklet, the title of the dialog window doesnt show ('Basic dialog'). I wonder why, although it doesn't really matter - it's not important at all.
Here is the script:
javascript:(function () {
function getScript(url, success) {
var script = document.createElement('script');
script.src = url;
var head = document.getElementsByTagName('head')[0];
var completed = false;
script.onload = script.onreadystatechange = function () {
if (!completed && (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete')) {
completed = true;
success();
script.onload = script.onreadystatechange = null;
head.removeChild(script);
}
};
head.appendChild(script);
}
getScript("https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.js", function () {
getScript("https://ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.js", function () {
var myStylesLocation = "https://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css";
$('<link rel="stylesheet" type="text/css" href="'+myStylesLocation+'" >').appendTo("head");
alert("It works");
var myMessage = "This is the default dialog which is useful for displaying information.";
$("<div id='dialog'; title='Basic dialog'; style='border:2px solid black; background-color:lightblue; font-size:80%'; <p>" + myMessage + "</p></div>").appendTo("body");
$( "#dialog" ).dialog();
});
});
})();
I've written a simple Greasemonkey script, and I'm trying to create a "config" page for this script (like the one that is used for Google Chrome extensions. ) Would there be any way to create a config page for a userscript, like the "options" pages for Google Chrome extensions? There isn't any way to include an .html page as part of a Greasemonkey script (as far as I know), so I'm looking for other options.
// ==UserScript==
// #name Redirector
// #namespace http://use.i.E.your.homepage/
// #version 0.1
// #description enter something useful
// #match http://*/*
// #copyright 2012+, You
// #run-at document-start
// ==/UserScript==
redirectToPage("http://www.youtube.com/", "http://www.google.com");
function redirectToPage(page1, page2){
if(window.location.href.indexOf(page1) != -1){
window.location.href = page2;
}
}
There are a few libraries which provide config pages for userscripts:
1) GM_config
2) MonkeyConfig
3) GM_registerMenuCommand Submenu JS Module
The usage varies per library, but typically you grant the permissions they need such as GM_getValue and GM_setValue, and require the library via the #require directive, e.g.:
// ==UserScript==
// #name My Userscript
// #description An example userscript with a config page
// #version 0.0.1
// #require https://www.example.com/lib/myconfig.js
// #grant GM_getValue
// #grant GM_setValue
// #grant GM_addStyle
// #grant GM_registerMenuCommand
// ==/UserScript==
const config = new MyConfig({ ... })
You then register a menu command which opens the config page/dialog, e.g.:
GM_registerMenuCommand('Configure My Userscript!', () => {
config.open()
})
In the case of MonkeyConfig, it can register the command for you:
const config = new MonkeyConfig({
title: 'Configure My Userscript!',
menuCommand: true,
// ...
})
For advanced uses, the configurator may allow listeners to be registered for the close/cancel/save events, as well as providing control over the CSS, and other options. Detailed instructions can be found on the GM_config wiki and the MonkeyConfig homepage.
If you are using it for chrome, then it isn't Greasemonkey but Tampermonkey.
You may consider using GM_getResourceText, paste your html to pastebin.com (or similar) and add the link as one of #resource to the metadata block.
At least, I know it works to Greasemonkey.
For example:
// #resource configHtml http://pastebin.com/raw.php?i=2RjKfwJQ
// ... some DOM node that you will append to the current page
node.innerHTML = GM_getResourceText("configHtml");
This is much needed but for now combination of 2 approaches should work.
1) For personal use I just have a bunch of variables at the top of the script. The problem here is that if anyone else uses my script an update ends up erasing his preferences.
2) Have a configuration page on your website. While this works wonderfully websites get deleted all the time. There is no good reason for a script to depend on a website to work.
If you do both those things the user can edit the preferences in the script when the scripts website vanishes.
Here is an example where undesired functionality is // commented out.
http://go-here.nl/gm/wikipedia-clean-up.php
Good luck and enjoy
Use a parameter to the page you're already including, and if that's set then clear the whole document:
http://page.my.script.runs.on/?configPage=true
if(getUrlParameter("configPage") === "true") {
$(document).empty
}
this userscript don't use any external libs. with modification you can make good UI.
// ==UserScript==
// #name UI development
// #namespace http://tampermonkey.net/
// #version 0.1
// #description try to take over the world!
// #author ManojBhakarPCM
// #match https://www.google.com/
// #grant none
// ==/UserScript==
(function() {
'use strict';
function ui_create(contentss){
var div = document.createElement('div');
//var contentss = "<h1>TESTsssss</h1>";
div.innerHTML = '<div id="mbpcm_modal" style="position: fixed;z-index: 1;left: 0;top: 0;width: 100%;height: 100%;overflow: auto;display:none;"><div id="mbpcm_content" style="background-color: pink;margin: 15% auto;padding: 20px;border: 1px solid black;width: 20%;box-shadow:20px 20px 30px 2px gray;border-radius:7px;"><span id="mbpcm_close" style="float: right;font-size:25px;cursor: pointer;">×</span>'+ contentss +'</div></div>';
document.body.appendChild(div);
var modal = document.getElementById("mbpcm_modal");
var close = document.getElementById("mbpcm_close");
close.onclick = function(){modal.style.display = "none";}
}
function ui_add_opener(parent,classs,stylee){
var btn = document.createElement("button");
btn.setAttribute("class",classs);
btn.setAttribute("style",stylee);
btn.setAttribute("id","mbpcm_ui_opener");
btn.innerHTML = "Settings";
parent.appendChild(btn);
var modal = document.getElementById("mbpcm_modal");
var btnn = document.getElementById("mbpcm_ui_opener");
btnn.onclick = function(){modal.style.display = "block";}
}
function ui_addElement(el,cls,styl,val,innerHtml){
var modal = document.getElementById("mbpcm_content");
var e = document.createElement(el);
e.setAttribute("class",cls);
e.setAttribute("style",styl);
e.setAttribute("value",val);
e.innerHTML = innerHtml;
modal.appendChild(e);
return e;
}
ui_create("<h1>Title</h1><span>discription</span><br>");
ui_add_opener(document.getElementById("gbw"),"RNmpXc","nothing");
ui_addElement("button","modal","none","none","TestButton").onclick = function(){alert("hellow ji ");}
ui_addElement("br","","","","");
ui_addElement("button","none","none","none","TestButton").onclick = function(){alert("How are you");}
ui_addElement("button","none","none","none","TestButton").onclick = function(){alert("Kaise ho");}
})();
I am trying to get get zombie.js to activate a link that uses javascript. The page I am testing it on is:
<html>
<body>
<div id="test123">
START_TEXT
</div>
GO<br/>
<script type="text/javascript">
go = function() {
var el = document.getElementById("test123");
el.innerHTML = "CHANGED";
}
</script>
</body>
</html>
The Script I am using is:
var zombie = require("zombie");
var browser = new zombie.Browser;
browser.visit( "http://localhost:8000/testpage.html",
function() {
browser.clickLink("GO", function(e, browser, status) {
var temp = browser.text("div#test123");
console.log("content:", temp);
});
});
I get the error message:
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Cannot load resource: javascript:go()
at History._resource (/home/julian/temp/node_modules/zombie/lib/zombie/history.coffee:75:15)
at History._pageChanged (/home/julian/temp/node_modules/zombie/lib/zombie/history.coffee:60:21)
at History._assign (/home/julian/temp/node_modules/zombie/lib/zombie/history.coffee:213:19)
at Object.location (/home/julian/temp/node_modules/zombie/lib/zombie/history.coffee:51:24)
at Object.click (/home/julian/temp/node_modules/zombie/lib/zombie/jsdom_patches.coffee:31:59)
at Object.dispatchEvent (/home/julian/temp/node_modules/zombie/node_modules/jsdom/lib/jsdom/level2/html.js:480:47)
at /home/julian/temp/node_modules/zombie/lib/zombie/eventloop.coffee:130:16
at EventLoop.perform (/home/julian/temp/node_modules/zombie/lib/zombie/eventloop.coffee:121:7)
at EventLoop.dispatch (/home/julian/temp/node_modules/zombie/lib/zombie/eventloop.coffee:129:19)
at Browser.dispatchEvent (/home/julian/temp/node_modules/zombie/lib/zombie/browser.coffee:220:30)
When I use
browser.evaluate("go()")
it works.
What am I missing?
Zombie.js doesn't handle links with "javascript:" on href (at the time of this writing).
I fixed it by adding 3 lines to the source code. Look for /node_modules/zombie/lib/zombie/history.coffee and add the 3 lines commented with FIX (beware that in .coffee you must respect indentation, ie. use 2 spaces):
# Location uses this to move to a new URL.
_assign: (url)->
url = #_resolve(url)
# FIX: support for javascript: protocol href
if url.indexOf("javascript:")==0
#_browser.evaluate(url.substr("javascript:".length))
return
was = #_stack[#_index]?.url # before we destroy stack
#_stack = #_stack[0..#_index]
#_stack[++#_index] = new Entry(this, url)
#_pageChanged was
I probably should fork zombie.js on github.com and put this into a Pull Request, but until then you are welcome to do use this snippet, or make that pull request before me.
Well it doesn't seem to understand javascript:code hrefs. Perhaps you can get the url, remove the javascript:-section and evaluate it?
Disclaimer: I haven't used zombie.js myself yet.
The zombie.js API says it takes a CSS selector or the link text as the first parameter for browser.clickLink(). So your code should work.
But try adding an id to the link, if you have control over the page, and using a CSS selector
browser.clickLink('#thelink', function(e, browser, status) {
var temp = browser.text("div#test123");
console.log("content:", temp);
});