Check when WebKit context is available in NW.js
Asked Answered
C

4

12

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?

Craft answered 23/4, 2016 at 22:6 Comment(5)
Is running your node-main function once WebKit loads an option?Laverty
@RahatMahbub I was hoping there is an official (maybe undocumented) way to do this solely through Node, like global event. This would make sense, but if there is none then calling back from html page is the only option, I guess.Craft
@estus There's a way to do it but I don't think it's a particularly neat way. I can add it to my answer if you want.Sedgemoor
@Sedgemoor Sure, that would be great. The answer contains a good introductory NW example but doesn't address the question directly.Craft
@estus I added a timer to it so you can see how long it takes for the nw object to be defined.Sedgemoor
C
1

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.

Craft answered 2/5, 2016 at 16:37 Comment(0)
L
2

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.

Laverty answered 27/4, 2016 at 2:9 Comment(0)
S
2

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');
Sedgemoor answered 29/4, 2016 at 23:43 Comment(1)
Thanks for pollit package, looks like a hack but a good one, performance hit and excessive delay are nearly zero.Craft
F
1

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);
    };
};
Freeboard answered 30/4, 2016 at 9:36 Comment(2)
This solution is wrong. require('nw.gui') is the legacy way to get the nw object. Your also missing the OP's point about contexts, you're not in node context at any point in this answer.Sedgemoor
@Sedgemoor it is an example.Freeboard
C
1

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.

Craft answered 2/5, 2016 at 16:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.