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();
});
});
})();
Related
I am building my first chrome extension, but it doesn't work. Google chrome can read the manifest and icon logo, but it doesn't work after that. It is supposed to have a click function (javascript) to different background images. So when you click on them it will change the background of any page.
javascript file
function click(e){
chrome.tabs.executeScript(null,
{code:"document.body.style.backgroundImage='url(" + image[e.target.id]
+"'"});
window.close();
}
document.addEventListener('DOMContentLoaded', function () {
var divs = document.querySelectionALL('div');
for (var i = 0; i < divs.length; i++){
divs[i].addEventListener('click', click);
}
});
var images = {
file1: 'file1.jpg',
file2:'file2.jpg',
}
When executed in Node context (node-main),
setTimeout(function () {
console.log(nw);
}, 20);
throws
nw is not defined
because WebKit context is not ready (right from the start window is unavailable in NW.js <= 0.12, window.nw in NW.js >= 0.13). And
setTimeout(function () {
console.log(nw);
}, 200);
works just fine but setTimeout looks like a hack, setting it to safe delay value may cause undesirable lag.
How can the availability of WebKit context and nw be checked from Node context? Is there a reasonable way, like an event that could be handled?
The following achieves the same thing but does it the other way around.
In your html file:
<body onload="process.mainModule.exports.init()">
In your node-main JS file:
exports.init = function() {
console.log(nw);
}
Here, init function is only called when Webkit context/DOM is available.
You could use pollit :) ...
var pit = require("pollit");
foo = function(data) {
console.log(nw);
};
pit.nw('nw', foo);
I've tested it and it works for me :). This modularizes the solution that I give near the end of this.
The nw object does not exist until webkit is up and running ie the browser
window has been created. This happens after Node starts up which is why you're
getting this error. To use the nw api you either create events that can be
listened to or call global functions the former being preferable. The following code will demonstrate both and should give you a good idea of how Node and WebKit are interfacing with each other.
This example creates a Window, opens devtools and allows you to toggle the
screen. It also displays the mouse location in the console. It also demonstrates how to send events using the DOM ie body.onclick() and attaching events from within Node ie we're going to catch minimize events and write them to the console.
For this to work you need to be using the SDK version of NW. This is my package.json
{
"name": "hello",
"node-main": "index.js",
"main": "index.html",
"window": {
"toolbar": true,
"width": 800,
"height": 600
},
"dependencies" : {
"robotjs" : "*",
"markdown" : "*"
}
}
The two files you need are index.html
<!DOCTYPE html>
<html>
<head>
<script>
var win = nw.Window.get();
global.win = win;
global.console = console;
global.main(nw);
global.mouse();
var markdown = require('markdown').markdown;
document.write(markdown.toHTML("-->Click between the arrows to toggle full screen<---"));
</script>
</head>
<body onclick="global.mouse();">
</body>
</html>
and index.js.
var robot = require("robotjs");
global.mouse = function() {
var mouse = robot.getMousePos();
console.log("Mouse is at x:" + mouse.x + " y:" + mouse.y);
global.win.toggleFullscreen();
}
global.main = function(nw_passed_in) {
global.win.showDevTools();
console.log("Starting main");
console.log(nw_passed_in);
console.log(nw);
global.win.on('minimize', function() {
console.log('n: Window is minimized from Node');
});
}
When running this I used
nwjs --enable-logging --remote-debugging-port=1729 ./
You can then open up the browser using
http://localhost:1729/
for debugging if needed.
If you want to do something as soon as the nw object exists you can poll it. I'd use eventEmitter, if you don't want to use event emitter you can just as easily wrap this in a function and call it recursively. The following will display how many milliseconds it took before the nw object was setup. On my system this ranged between 43 - 48 milliseconds. Using a recursive function was no different. If you add this to the code above you'll see everything logged to the console.
var start = new Date().getTime();
var events = require('events');
var e = new events.EventEmitter();
var stop = 0;
e.on('foo', function() {
if(typeof nw === 'undefined') {
setTimeout(function () {
e.emit('is_nw_defined');
}, 1);
}
else {
if(stop === 0) {
stop = new Date().getTime();
}
setTimeout(function () {
console.log(stop - start);
console.log(nw);
e.emit('is_nw_defined');
}, 2000);
}
});
e.emit('is_nw_defined');
Solution 1:
You can use onload, see reference.
main.js:
var gui = require("nw.gui"),
win = gui.Window.get();
onload = function() {
console.log("loaded");
console.log(win.nw);
};
index.html:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="main.js"></script>
</head>
<body></body>
</html>
package.json:
{
"name": "Freebox",
"main": "index.html"
}
Solution 2:
(To prevent issue, but it is not necessary).
var gui = require("nw.gui"),
win = gui.Window.get();
onload = function() {
console.log("loaded");
var a = function () {
if (!win.nw) return setTimeout(a, 10);
console.log(win.nw);
};
};
The solution I've initially come up looks like
app-node.js
process.once('webkit', () => {
console.log(nw);
});
app.html
<html>
<head>
<script>
global.process.emit('webkit');
</script>
...
I would be glad to know that there is already an event to listen, so cross-platform client scripts could omit NW-related code.
I'm using the BrowserWindow to display an app and I would like to force the external links to be opened in the default browser. Is that even possible or I have to approach this differently?
I came up with this, after checking the solution from the previous answer.
mainWindow.webContents.on('new-window', function(e, url) {
e.preventDefault();
require('electron').shell.openExternal(url);
});
According to the electron spec, new-window is fired when external links are clicked.
NOTE: Requires that you use target="_blank" on your anchor tags.
new-window is now deprecated in favor of setWindowOpenHandler in Electron 12 (see https://github.com/electron/electron/pull/24517).
So a more up to date answer would be:
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
shell.openExternal(url);
return { action: 'deny' };
});
Improved from the accepted answer ;
the link must be target="_blank" ;
add in background.js(or anywhere you created your window) :
window.webContents.on('new-window', function(e, url) {
// make sure local urls stay in electron perimeter
if('file://' === url.substr(0, 'file://'.length)) {
return;
}
// and open every other protocols on the browser
e.preventDefault();
shell.openExternal(url);
});
Note : To ensure this behavior across all application windows, this code should be run after each window creation.
If you're not using target="_blank" in your anchor elements, this might work for you:
const shell = require('electron').shell;
$(document).on('click', 'a[href^="http"]', function(event) {
event.preventDefault();
shell.openExternal(this.href);
});
I haven't tested this but I assume this is should work:
1) Get WebContents of the your BrowserWindow
var wc = browserWindow.webContents;
2) Register for will-navigate of WebContent and intercept navigation/link clicks:
wc.on('will-navigate', function(e, url) {
/* If url isn't the actual page */
if(url != wc.getURL()) {
e.preventDefault();
openBrowser(url);
}
}
3) Implement openBrowser using child_process. An example for Linux desktops:
var openBrowser(url) {
require('child_process').exec('xdg-open ' + url);
}
let me know if this works for you!
For anybody coming by.
My use case:
I was using SimpleMDE in my app and it's preview mode was opening links in the same window. I wanted all links to open in the default OS browser. I put this snippet, based on the other answers, inside my main.js file. It calls it after it creates the new BrowserWindow instance. My instance is called mainWindow
let wc = mainWindow.webContents
wc.on('will-navigate', function (e, url) {
if (url != wc.getURL()) {
e.preventDefault()
electron.shell.openExternal(url)
}
})
Check whether the requested url is an external link. If yes then use shell.openExternal.
mainWindow.webContents.on('will-navigate', function(e, reqUrl) {
let getHost = url=>require('url').parse(url).host;
let reqHost = getHost(reqUrl);
let isExternal = reqHost && reqHost != getHost(wc.getURL());
if(isExternal) {
e.preventDefault();
electron.shell.openExternal(reqUrl);
}
}
Put this in renderer side js file. It'll open http, https links in user's default browser.
No JQuery attached! no target="_blank" required!
let shell = require('electron').shell
document.addEventListener('click', function (event) {
if (event.target.tagName === 'A' && event.target.href.startsWith('http')) {
event.preventDefault()
shell.openExternal(event.target.href)
}
})
For Electron 5, this is what worked for me:
In main.js (where you create your browser window), include 'shell' in your main require statement (usually at the top of the file), e.g.:
// Modules to control application life and create native browser window
const {
BrowserWindow,
shell
} = require('electron');
Inside the createWindow() function, after mainWindow = new BrowserWindow({ ... }), add these lines:
mainWindow.webContents.on('new-window', function(e, url) {
e.preventDefault();
shell.openExternal(url);
});
I solved the problem by the following step
Add shell on const {app, BrowserWindow} = require('electron')
const {app, BrowserWindow, shell} = require('electron')
Set nativeWindowOpen is true
function createWindow () {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 1350,
height: 880,
webPreferences: {
nativeWindowOpen: true,
preload: path.join(__dirname, 'preload.js')
},
icon: path.join(__dirname, './img/icon.icns')
})
Add the following listener code
mainWindow.webContents.on('will-navigate', function(e, reqUrl) {
let getHost = url=>require('url').parse(url).host;
let reqHost = getHost(reqUrl);
let isExternal = reqHost && reqHost !== getHost(wc.getURL());
if(isExternal) {
e.preventDefault();
shell.openExternal(reqUrl, {});
}
})
reference https://stackoverflow.com/a/42570770/7458156 by cuixiping
I tend to use these lines in external .js script:
let ele = document.createElement("a");
let url = "https://google.com";
ele.setAttribute("href", url);
ele.setAttribute("onclick", "require('electron').shell.openExternal('" + url + "')");
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.
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);
});