I would like to write a greasemonkey script that given an xpath returns all of the output of that xpath executed on the current page in a .txt file with one result per row.
How do I do this?
EDIT: Its ok if the output is not written to a file. I just want to have it displayed.
Here is an example that appends a list of all href links to the body of the html. You can pretty it up with style, and make it hidden, floating, etc.
// ==UserScript==
// #name test
// #namespace johnweldon.com
// #description test
// #include *
// ==/UserScript==
(function() {
try {
var xpath = "//a[#href]"; // get all links
var res = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
var str = "<div><ul>";
for ( var i = 0; i < res.snapshotLength; i++) {
str = str + "\n<li>" + res.snapshotItem(i);
}
str += "</ul></div>";
var ev = document.createElement("div"); // parent element for our display
ev.innerHTML = str; //quick and dirty
document.body.appendChild(ev);
}
catch (e) {
alert(e.message);
}
}())
Related
Been struggling to get this script to work. It's meant to batch export notes out of Apple Notes. Script is below.
// set things up
var app = Application.currentApplication();
app.includeStandardAdditions = true;
var notesApp = Application('Notes');
notesApp.includeStandardAdditions = true;
// choose which notes
var notes = notesApp.notes;
var whichNotes = app.chooseFromList(notes.name(), { withPrompt: "Which Notes?", multipleSelectionsAllowed: true });
if (whichNotes) {
// choose save location
var saveWhere = app.chooseFolder().toString();
if (saveWhere) {
// loop through all notes
for(var i=0; i<notes.length; i++) {
// is this note one to be exported?
if (whichNotes.indexOf(notes[i].name()) > -1) {
// save file as html
var filename = saveWhere+"/"+notes[i].name()+".html";
var file = app.openForAccess(Path(filename), { writePermission: true });
app.setEof(file, { to: 0 });
app.write(notes[i].body(), {to: file});
app.closeAccess(file);
}
}
}
}
A bunch of other people have used it with no problems.
I have the same problem with the same script on 10.15.7. The issue is raised on notes.name().
I assume this is related to either too many notes (it used to work, but I created a lot of notes since), or some special char in the note title. But I did not managed to fix it with my notes.
I copied my version below.
(notice the replace to build a valid file name if your note title contain "/".)
// set things up
var app = Application.currentApplication();
app.includeStandardAdditions = true;
var notesApp = Application('Notes');
notesApp.includeStandardAdditions = true;
// choose which notes
var notes = notesApp.notes;
this.console.log("before notes.name()")
var whichNotes = app.chooseFromList(notes.name(), { withPrompt: "Which Notes?", multipleSelectionsAllowed: true });
this.console.log("After notes.name()")
this.console.log("Let's do it") // view / show log / message tab
if (whichNotes) {
// choose save location
var saveWhere = app.chooseFolder().toString();
if (saveWhere) {
this.console.log("note count:"+notes.length)
// loop through all notes
for(var i=0; i<notes.length; i++) {
// is this note one to be exported?
if (whichNotes.indexOf(notes[i].name()) > -1) {
// save file as html
var notename = notes[i].name().replace(/\//gi,'-')
this.console.log("next:"+notename) // view / show log / message tab
var filename = saveWhere+"/"+ notename +".html";
var file = app.openForAccess(Path(filename), { writePermission: true });
app.setEof(file, { to: 0 });
app.write(notes[i].body(), {to: file});
app.closeAccess(file);
}
}
}
}
At the end of wikipedia pages, there are reference links. I am trying to not display reference list, and list is toggled when clicked on heading of reference links, like 'Further Reading', 'Notes', 'References' etc. Every reference list has class name 'reflist'. Problem I am having is, when I try to find div with reflist class after heading,
if (div.className == 'reflist') statement does not work. What could be going wrong?
I wrote following code. Problem is when I click 'Further reading', 'reflist' of 'References' is toggled. Clicking 'References' also toggle the same 'relist'. By the way I am not Professional Programmer...
if (document.getElementsByClassName("reflist")) {
let x = document.getElementsByClassName("reflist");
for (let i = 0; i < x.length; i++) {
x[i].style.display = "none";
}
}
function toggle(element) {
element.style.display = element.style.display == "none" ? "block" : "none";
}
// furtherReading is <span> element.
var furtherReading = document.getElementById("Further_reading");
// parentElement is <h2> element
furtherReading.parentElement.style.background = '#f8f9fa';
furtherReading.parentNode.style.borderBottom = 'none';
furtherReading.style.cursor = "pointer";
var references = document.getElementById("References");
references.parentElement.style.background = '#f8f9fa';
references.parentElement.style.borderBottom = 'none';
references.style.cursor = "pointer";
if (furtherReading) {
/* There may be other 'style' or 'div' elements before 'div' having className 'reflist' */
let a = furtherReading.parentElement.nextElementSibling;
while (a) {
if (a.className == 'reflist') {
break;
} else
a = a.nextElementSibling;
}
furtherReading.addEventListener("click", toggle.bind(null, a));
}
if (references) {
let b = references.parentElement.nextElementSibling;
while (b) {
if (b.className == 'reflist') {
break;
} else
b = b.nextElementSibling;
}
references.addEventListener("click", toggle.bind(null, b));
}
Try the following code, test with https://en.wikipedia.org/wiki/Perast
// ==UserScript==
// #name Toggle Wikipedia Reference
// #namespace http://tampermonkey.net/
// #version 0.1
// #description Toggle Wikipedia Reference
// #author Ynjxsjmh
// #match *://*.wikipedia.org/wiki/*
// #grant none
// ==/UserScript==
(function() {
'use strict';
var reflist = document.getElementsByClassName("reflist")[0];
var reference = document.getElementById("References");
console.log("Toggle Wikipedia Reference");
reflist.style.display = "none";
console.log(reference);
reference.addEventListener("click", toggleReference.bind(null, reflist));
function toggleReference(element) {
element.style.display = element.style.display == "none" ? "block" : "none";
}
})();
The problem is that you should get the div manually since nobody help you loop through the div.
// ==UserScript==
// #id rtskincruff
// #name Right Gmail
// #version 1.0
// #namespace OK
// #author Exaskryz
// #description
// #include https://mail.google.com/*
// #run-at window-load
// ==/UserScript==
//I tried for the #run-at document-end and document-idle
var colored = setInterval(function () {coloring()}, 10000);
function coloring() {
console.log("ping");
var name = document.getElementsByClassName("gb_l gb_n");
console.log("got name");
console.log(name.length);
if (name[0].innerHTML = "+Exaskryz") {
name[0].style.color = "#FF0000";
name[0].style.fontSize = "xx-large";
name[0].innerHTML = "WRONG ACCOUNT TO SEND EMAIL FROM";
//var backg = document.getElementsByClassName("AO");
//backg[0].style.backgroundColor = "#FF0000";
} else if (name[0].innerHTML = "WRONG ACCOUNT TO SEND EMAIL FROM") {
clearInterval(colored);
alert("pun");
}
The script is being launched 5 times and I can't figure out why or how to stop the other instances. The other instances seem to never be able to find any element with a class name (gb_l gb_n) and report back to me a name.length of 0. It's also really weird because it will only ever get up to the part where it logs the name.length and never does any other console.log UNLESS it did get a value for the variable name.
Here's the full script with the full mess of trying to figure out how to stop this script from running every 10 seconds (originally had it set at 1 second):
// ==UserScript==
// #id rtskincruff
// #name Right Gmail
// #version 1.0
// #namespace OK
// #author Exaskryz
// #description
// #include https://mail.google.com/*
// #run-at document-idle
// ==/UserScript==
var colored = setInterval(function () {coloring()}, 10000);
function coloring() {
console.log("ping");
var name = document.getElementsByClassName("gb_l gb_n");
console.log("got name");
console.log(name.length);
if (name[0].innerHTML = "+Exaskryz") {
name[0].style.color = "#FF0000";
name[0].style.fontSize = "xx-large";
name[0].innerHTML = "WRONG ACCOUNT TO SEND EMAIL FROM";
//var backg = document.getElementsByClassName("AO");
//backg[0].style.backgroundColor = "#FF0000";
} else if (name[0].innerHTML = "WRONG ACCOUNT TO SEND EMAIL FROM") {
clearInterval(colored);
alert("pun");
}
console.log("passed first if");
if (name.length>0) {
if (name[0].innerHTML = "+Exaskryz") {
name[0].style.color = "#FF0000";
name[0].style.fontSize = "xx-large";
name[0].innerHTML = "WRONG ACCOUNT TO SEND EMAIL FROM";
//var backg = document.getElementsByClassName("AO");
//backg[0].style.backgroundColor = "#FF0000";
alert("bun");
clearInterval(colored);
alert("done");
}
}
console.log("passed second if");
var nong = document.getElementsByClassName("gb_K gb_ka gb_n gb_ga");
console.log("got nong");
alert(nong.length);
if (nong[0].innerHTML = "CorrectAccount#gmail.com") {
alert("hun");
clearInterval(colored);
alert("done");
}
}
This is a shot in the dark, but it may be possible that your script is also running on any iframes present at mail.google.com. To stop the script from running on iframes, there's two options.
In the meta data block of the script, you can set #noframes to true.
// #noframes true
Or, you can check at the beginning of the script whether or not the script's window is in fact the 'top most' window.
if (window != window.top) { return; }
I'm pretty new to CasperJS, but isn't there a way to open a URL and execute CasperJS commands in for loops? For example, this code doesn't work as I expected it to:
casper.then(function() {
var counter = 2013;
for (i = counter; i < 2014; i++) {
var file_name = "./Draws/wimbledon_draw_" + counter + ".json";
// getting some local json files
var json = require(file_name);
var first_round = json["1"];
for (var key in first_round) {
var name = first_round[key].player_1.replace(/\s+/g, '-');
var normal_url = "http://www.atpworldtour.com/Tennis/Players/" + name;
// the casper command below only executes AFTER the for loop is done
casper.thenOpen(normal_url, function() {
this.echo(normal_url);
});
}
}
});
Instead of Casper is calling thenOpen on each new URL per iteration, it gets only called AFTER the for loop executes. Casper thenOpen then gets called with the last value normal_url is set to. Is there no Casper command to have it work each iteration within the for loop?
Follow up: How do we make casper thenOpen return a value on the current iteration of the for loop?
Say for example, I needed a return value on that thenOpen (maybe if the HTTP status is 404 I need to evaluate another URL so I want to return false). Is this possible to do?
Editing casper.thenOpen call above:
var status;
// thenOpen() only executes after the console.log statement directly below
casper.thenOpen(normal_url, function() {
status = this.status(false)['currentHTTPStatus'];
if (status == 200) {
return true;
} else {
return false;
}
});
console.log(status); // This prints UNDEFINED the same number of times as iterations.
If you need to get context then use the example here:
https://groups.google.com/forum/#!topic/casperjs/n_zXlxiPMtk
I used the IIFE (immediately-invoked-function-expression) option.
Eg:
for(var i in links) {
var link = links[i];
(function(index) {
var link = links[index]
var filename = link.replace(/#/, '');
filename = filename.replace(/\//g, '-') + '.png';
casper.echo('Attempting to capture: '+link);
casper.thenOpen(vars.domain + link).waitForSelector('.title h1', function () {
this.capture(filename);
});
})(i);
}
links could be an array of objects and therefore your index is a reference to a group of properties if need be...
var links = [{'page':'some-page.html', 'filename':'page-page.png'}, {...}]
As Fanch and Darren Cook stated, you could use an IIFE to fix the url value inside of the thenOpen step.
An alternative would be to use getCurrentUrl to check the url. So change the line
this.echo(normal_url);
to
this.echo(this.getCurrentUrl());
The problem is that normal_url references the last value that was set but not the current value because it is executed later. This does not happen with casper.thenOpen(normal_url, function(){...});, because the current reference is passed to the function. You just see the wrong url, but the correct url is actually opened.
Regarding your updated question:
All then* and wait* functions in the casperjs API are step functions. The function that you pass into them will be scheduled and executed later (triggered by casper.run()). You shouldn't use variables outside of steps. Just add further steps inside of the thenOpen call. They will be scheduled in the correct order. Also you cannot return anything from thenOpen.
var somethingDone = false;
var status;
casper.thenOpen(normal_url, function() {
status = this.status(false)['currentHTTPStatus'];
if (status != 200) {
this.thenOpen(alternativeURL, function(){
// do something
somethingDone = true;
});
}
});
casper.then(function(){
console.log("status: " + status);
if (somethingDone) {
// something has been done
somethingDone = false;
}
});
In this example this.thenOpen will be scheduled after casper.thenOpen and somethingDone will be true inside casper.then because it comes after it.
There are some things that you need to fix:
You don't use your counter i: you probably mean "./Draws/wimbledon_draw_" + i + ".json" not "./Draws/wimbledon_draw_" + counter + ".json"
You cannot require a JSON string. Interestingly, you can require a JSON file. I still would use fs.read to read the file and parse the JSON inside it (JSON.parse).
Regarding your question...
You didn't schedule any commands. Just add steps (then* or wait*) behind or inside of thenOpen.
I'm trying to add an event for all elements with "p" tag.
But instead of adding an event script colors all links in red
<script>
//create links
var code = ""
for (i=0;i<10;i++){
code += "<p><a href='#'>Link " + i + "</a></p>"
}
document.getElementById('links').innerHTML = code;
//add Events
for(i=0;i<document.getElementsByTagName("p").length;i++){
document.getElementsByTagName("p")[i].onmouseover = document.getElementsByTagName("p")[i].childNodes[0].style.color="green"
document.getElementsByTagName("p")[i].onmouseout = document.getElementsByTagName("p")[i].childNodes[0].style.color="red"
}
}
</script>
There is My code
Event handlers need to be functions. So you need something like this:
document.getElementsByTagName("p")[i].onmouseover = function() {
// You don't want to use i in a function in a loop since i will
// be different by the time the function gets called
// this is document.getElementsByTagName("p")[i]
this.childNodes[0].style.color="green"
}
You should probably also create the nodeList for the <p> tags outside of the loop so you're not traversing the DOM each time.
var paras = document.getElementsByTagName('p');
for(i=0;i<paras.length;i++){
paras[i].onmouseover = function() { /* */ };
paras[i].onmouseout = function() { /* */ };
}