![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1682517/EPAM_LOGO_Full_Color_RGB.png)
ANDRIY DACENKO
SOFTWARE ENGINEER
September 9, 2015
NW.JS OVERVIEW DESKTOP APPS USING JS
THREE PLATFORMS ONE SOLUTION
JS TALK #20 KYIV
![](https://upload.wikimedia.org/wikipedia/commons/thumb/3/35/Tux.svg/512px-Tux.svg.png)
![](http://a2.mzstatic.com/us/r30/Purple6/v4/28/e1/6e/28e16ed4-df5d-1676-a7aa-f2be85f140e4/ProductPageIcon.png)
![](https://upload.wikimedia.org/wikipedia/commons/thumb/5/5f/Windows_logo_-_2012.svg/2000px-Windows_logo_-_2012.svg.png)
PLATFORMS
DEVELOPERS
DEVELOPERS
DEVELOPERS
![](http://bamlife.ru/wp-content/uploads/2014/09/QT.jpg)
![](https://developer.ibm.com/bluemix/wp-content/uploads/sites/20/2015/05/xcode-logo.png)
![](https://upload.wikimedia.org/wikipedia/commons/thumb/e/e4/Visual_Studio_2013_Logo.svg/2000px-Visual_Studio_2013_Logo.svg.png)
TOOLS
C#
C++
OBJECTIVE-C
WHAT IF I TOLD YOU
YOU CAN MAKE DESKTOP
APPS WITH HTML, CSS
AND JAVASCRIPT?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1646060/nw.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1654342/Screenshot_from_2015-08-20_12_33_01.png)
Oct 30, 2011 – Aug 15, 2015
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1654345/Screenshot_from_2015-08-20_12_34_33.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1654355/Screenshot_from_2015-08-20_12_38_33.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1654359/Screenshot_from_2015-08-20_12_40_53.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1652157/Chrome-May-Add-Support-for-Mozilla-s-Asm-js-the-Native-Client-Competitor.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1652159/Chromium_11_Logo.png)
JS
// package.json
{
"name": "simple",
"version": "1.0.0",
"main": "index.html",
"window": {
"toolbar": false
}
}
<!-- index.html -->
<html>
<head>
<title>Node WebKit App</title>
</head>
<body>
<h1>Desktop App!</h1>
</body>
</html>
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1654310/Screen_Shot_2015-08-20_at_12.16.26_PM.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1654313/Screenshot_from_2015-08-20_12_18_03.png)
# Run application
nw /path/to/app/folder
// Load native UI library
var gui = require('nw.gui');
// Print arguments
console.log(gui.App.argv);
// Quit current app
gui.App.quit();
// Get the JSON object of the manifest file
gui.App.manifest
// Get the application's data
// path in user's directory
gui.App.dataPath
// Lots more ...
APP
// menu.js
var gui = require('nw.gui');
// Create an empty menu
var menu = new gui.Menu();
var MenuItem = gui.MenuItem;
// Add some items
menu.append(new MenuItem({label: 'Item A'}));
menu.append(new MenuItem({label: 'Item B'}));
menu.append(new MenuItem({type: 'separator'}));
menu.append(new MenuItem({label: 'Item C'}));
// Remove one item
menu.removeAt(1);
// Popup as context menu
menu.popup(10, 10);
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1654310/Screen_Shot_2015-08-20_at_12.16.26_PM.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1656094/Screen_Shot_2015-08-21_at_1.07.17_AM.png)
CONTEXT MENU
var gui = require('nw.gui');
// Create a tray icon
var tray = new gui.Tray({
title: 'Tray', icon: 'img/icon.png'
});
// Give it a menu
var menu = new gui.Menu();
menu.append(new gui.MenuItem({
type: 'checkbox', label: 'box1'
}));
tray.menu = menu;
// Remove the tray
tray.remove();
tray = null;
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1665449/Screen_Shot_2015-08-25_at_12.49.48_PM.png)
TRAY
var options = {
icon: "app.png",
body: "Here is the notification text"
};
var notification = new Notification("Notification title", options);
notification.onclick = function() {
console.log('notification clicked');
}
notification.onshow = function() {
// auto close after 1 second
setTimeout(function() {
notification.close();
}, 1000);
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1665469/Screen_Shot_2015-08-25_at_12.59.59_PM.png)
NOTIFICATIONS
var gui = require('nw.gui');
// Open URL with default browser.
gui.Shell.openExternal(
'https://github.com/nwjs/nw.js'
);
var path = process.cwd() + '/shell/test.txt';
// Open a text file with default text editor.
gui.Shell.openItem(path);
// Open a file in file explorer.
gui.Shell.showItemInFolder(path);
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1665534/Screen_Shot_2015-08-25_at_1.59.54_PM.png)
SHELL
// Load native UI library
var gui = require('nw.gui');
// We can not create a clipboard,
// we have to receive the system clipboard
var clipboard = gui.Clipboard.get();
// Read from clipboard
var text = clipboard.get('text');
console.log(text);
// Or write something
clipboard.set('I love node-webkit :)', 'text');
// And clear it!
clipboard.clear();
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1664436/Screen_Shot_2015-08-25_at_2.32.56_AM.png)
CLIPBOARD
var gui = require('nw.gui');
var option = {
key : "Ctrl+Shift+A",
active : function() {
console.log("Global desktop keyboard shortcut: " + this.key + " active.");
},
failed : function(msg) { console.log(msg); }
};
var shortcut = new gui.Shortcut(option);
// Register global desktop shortcut, which can work without focus.
gui.App.registerGlobalHotKey(shortcut);
shortcut.on('active', function() {
console.log("Global desktop keyboard shortcut: " + this.key + " active.");
});
shortcut.on('failed', function(msg) { console.log(msg); });
// Unregister the global desktop shortcut.
gui.App.unregisterGlobalHotKey(shortcut);
SHORTCUT
// camFeed.js
function init()
{
navigator.webkitGetUserMedia({video:true}, onSuccess, onFail);
}
function onSuccess(stream)
{
var camFeed = document.getElementById('camFeed')
camFeed.src = webkitURL.createObjectURL(stream);
}
function onFail()
{
alert('could not connect stream');
}
<video id="camFeed" width="320" height="240" autoplay></video>
VIDEO
gui.Screen.Init();
gui.Screen.chooseDesktopMedia(
["window", "screen"], function(streamId) {
var vid_constraint = {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: streamId,
maxWidth: 1920,
maxHeight: 1080,
minFrameRate: 1,
maxFrameRate: 5
},
optional: []
};
navigator.webkitGetUserMedia({
audio: false,
video: vid_constraint
},
function(stream) {
document.getElementById('video_1').src = URL.createObjectURL(stream);
stream.onended = function() { console.log("Ended"); };
},
function(error) {
console.log('failure', error);
});
});
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1665518/Screen_Shot_2015-08-25_at_1.41.53_PM.png)
SCREEN SHARING
// JS way
gui.Window.open('noframe/index.html', { toolbar : false, frame : false });
<body style="-webkit-app-region: drag">
<h1>No frame</h1>
<button onclick="window.close()">Close</button>
</body>
// package.json
{
"window": {
"frame": false,
"toolbar": false
}
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1664539/Screen_Shot_2015-08-25_at_3.39.06_AM.png)
FRAMELESS
var gui = require('nw.gui');
gui.Window.get().enterKioskMode();
gui.Window.get().leaveKioskMode();
gui.App.quit();
// package.json
{
"window": {
"kiosk": true
}
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1665552/Screen_Shot_2015-08-25_at_2.09.28_PM.png)
KIOSK
<iframe src="http://google.com"
nwdisable
nwfaketop
nwUserAgent="Safari">
</iframe>
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1665521/Screen_Shot_2015-08-25_at_1.51.37_PM.png)
IFRAME
// setting value of file input element
var f = new File('/path/to/file', 'name');
var files = new FileList();
files.append(f);
document.getElementById('input0').files = files;
chooseFile('#fileDialog');
// emulate click on file inputs
function chooseFile(name) {
var chooser = document.querySelector(name);
chooser.addEventListener("change", function(evt) {
console.log(this.value);
console.log(this.files);
}, false);
chooser.click(); // <= here
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1664388/Screen_Shot_2015-08-25_at_2.18.35_AM.png)
<input type="file" webkitdirectory />
<input type="file" nwdirectory />
<input type="file" nwsaveas="filename.txt" />
<input type="file" nwworkingdir="/home/path/" />
CHANGES TO DOM
var gui = require('nw.gui');
var Datastore = require('nedb');
var path = require('path');
var persons = new Datastore({
filename: path.join(gui.App.dataPath, 'persons.db')
});
- Web SQL Database
- IndexedDB
- PouchDB
- NeDB
- StoreDB
- Application Cache
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1665502/database_1_512.png)
WORK WITH DATA
var gulp = require('gulp');
gulp.task('reload', function () {
for (var module_name in global.require.cache)
delete global.require.cache[module_name]
if (location) location.reload();
});
gulp.watch('**/*', ['reload']);
$ npm i nw-dev --save-dev
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1665426/live_reload_icon.jpg)
<script src="node_modules/nw-dev/lib/dev.js"></script>
$ npm i gulp --save-dev
LIVERELOAD
// package.json
{
"window": {
"toolbar": true
}
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1664244/Screen_Shot_2015-08-25_at_12.59.12_AM.png)
require('nw.gui')
.Window.get()
.showDevTools()
# Remote Debugging
# open http://localhost:9222/ to visit the debugger remotely
nw --remote-debugging-port=9222
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1664264/Screen_Shot_2015-08-25_at_1.10.52_AM.png)
DEBUGGING
$ npm install -g nw-gyp
$ nw-gyp configure --target=0.12.2
$ nw-gyp build
// binding.cc
#include <node.h>
#include <v8.h>
using namespace v8;
void Method(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
args.GetReturnValue().Set(String::NewFromUtf8(isolate, "world"));
}
void init(Handle<Object> target) {
NODE_SET_METHOD(target, "hello", Method);
}
NODE_MODULE(binding, init);
// binding.gyp
{
"targets": [
{
"target_name": "binding",
"sources": [ "binding.cc" ]
}
]
}
// test.js
var assert = require('assert');
var binding = require('./build/Release/binding');
assert.equal('world', binding.hello());
console.log('binding.hello() =', binding.hello());
BUILD NATIVE MODULES
# nwjs 0.12.0-rc1+
nwjc source.js binary.bin
require('nw.gui')
.Window.get()
.evalNWBin(null, 'binary.bin');
mytest(2);
# nwjs < 0.12.0-rc1
nwsnapshot --extra_code source.js snapshot.bin
// package.json
"snapshot" : "snapshot.bin"
// source.js
function mytest(a) {
document.write(a + 42);
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1665491/One-Does-Not-Simply.jpg)
ONE DOES NOT SIMPLY
HIDE HIS SOURCE
PTOTECT CODE WITH SNAPSHOT
PACKAGE YOUR APP
# Zip your application into .nw file
# zipped file would be at parent directory
zip -r ../${PWD##*/}.nw *
# Windows: Making an executable file out of a .nw file.
copy /b nw.exe+hello-world.nw hello-world.exe
# Linux: Making an executable file out of a .nw file.
cat /usr/bin/nw hello-world.nw > hello-world && chmod +x hello-world
# Max OSX: Making an executable file.
# Create folder `HelloWorld.app/Contents/Resources/hello-world.nw`
# and put all contents of your code in there
var NwBuilder = require('nw-builder');
var nw = new NwBuilder({
files: './app/**/**', // use the glob format
platforms: ['osx32', 'osx64', 'win32', 'win64'],
macIcns: 'icons/app.icns'
});
// Build returns a promise
nw.build().then(function () {
console.log('all done!');
}).catch(function (error) {
console.error(error);
});
// package.json
{
"window": {
"icon": "app.png"
}
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1665398/Screen_Shot_2015-08-25_at_12.17.53_PM.png)
BUILD
MADE USING NW.JS
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1665403/687474703a2f2f706f776465722e6d656469612f73637265656e73686f74732f506f77646572506c617965722d6e65772d342e706e67.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1665407/screenshot.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1665411/687474703a2f2f646576656c6f706572732e6174686f6d2e6e6c2f696d672f6465766b69742e706e67.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1665418/68747470733a2f2f736f6674776172652e696e74656c2e636f6d2f73697465732f64656661756c742f66696c65732f6d616e616765642f32352f63612f6275636b657473637265656e73686f74322e706e67.png)
LINKS
- Project - https://github.com/nwjs/nw.js
- V8 - https://github.com/nwjs/v8
- Docs - https://github.com/nwjs/nw.js/wiki
- Samples - https://github.com/zcbenz/nw-sample-apps
- Builder - https://github.com/nwjs/nw-builder
- Nw-dev - https://github.com/1j01/nw-dev
- Windows Executable - https://github.com/nwjs/nw.js/wiki/...
Q&A Time
ANDRIY DACENKO
SOFTWARE ENGINEER
September 9, 2015
NW.JS OVERVIEW DESKTOP APPS USING JS
THREE PLATFORMS ONE SOLUTION
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1682540/Global.png)
NW.js overview
By Andrew Dacenko
NW.js overview
Overview of nw.js - desktop applications development with JavaScript for JS Talk #20 Kyiv
- 2,068