node-webkit for a health kit (healthify)

node-webkit is an app runtime based on Chromium and node.js. You can write native apps in HTML and JavaScript with node-webkit. It also lets you call Node.js modules directly from the DOM and enables a new way of writing native applications with all Web technologies.

The time has come to create the first pet project with help of node-webkit (already known as nw.js) to be more familiar with it.

Idea

Every project is born from a great idea. This project rather based on a general daily problem or risk of people, who perform sitting work in an office, front of one or two monitor(s). Maybe this project can help to avoid or handle the problems can be traced from this work-style.
The main idea was a desktop application which is running in the background and giving tips and hints regularly about what should the user do to protect his/her health during the work.

blink more, stand up and move more, drink more, etc.

It seems node-webkit could help me in the realization of my idea.

Taking of the first steps

To start the work with the nw.js, it should be downloaded. I used the latest available release (0.12.0-alpha2). It's available for different platforms as well, but this project is using the Windows x64 version of nw.js on Windows 8.1 OS.

After we downloaded the file it should be decompressed and copied into a different directory. The content of the file is necessary to execute own nw.js files. So the project root directory (healthify) is the clone directory of the downloaded node-webkit because we need to run the nw.exe which will be looking for js and html files inside the root directory. It's working like the node.js, we need an executor application for our files. It is the nw.exe. Firstly we need to create a package.json file with initial parameters or we can use the npm init command to generate it automatically.

{
    "name": "Healthify - your healthier world",
    "main": "./html/index.html",
    "icon": "/img/icon.png",
    "window": {       
        "width": 600,
        "height": 500
    },
    "webkit": {
        "plugin": true
    }
}

The heart of the application is the index.html. It's recommended to create a html folder and put index.html into it. The html folder will contain html files that are used in the application. I created a js folder as well for the javascript files and a node_modules folder (recommended also) for the node modules. Yeah we can use node.js modules too. With them it's easy to access the filesystem or handle http requests from the node-webkit application.

Folder structure
healhtify  
|- html            # own html files
   |- index.html   # the main file for application
|- js              # own javascript codes
|- node_modules    # node modules (e.g. express)
|- package.json    # initial configuration and properties
|- nw.exe          # the executor
|- ...

I chose the easier structure for the application, but we can use the originally downloaded instance of nw.js. In this case we need to compress (zip) the application folder (contains index.html, package.json, and so on) and change its extension to .nw. Thus, we can use the originally downloaded node-webkit instance to execute the application:

$ path/to/nw/nw.exe path/to/our/app.nw

a new .nw file needs to be created (zip + rename) if the application changes

Conclusion

I used the same directory for the source code of the application and for the node-webkit source. So it's needed just run the nw.exe in the project folder to start the application.

Useful Getting Started pages:

Getting-Started-with-node-webkit
introduction-to-html5-desktop-apps-with-node-webkit

Code name is healthify

Let's see the pet project. I planned that the application will be running in the background and a cute tray icon will be appeared in taskbar next to the system clock. For this we need to see how it is possible to hide the application windows which is shown by default. On the other hand, we need to check the possibility of adding a tray icon to the application along with an "Exit" menu item to close the application. It's required because if we run the program in the background somehow we need to control it.

package.json with additional attributes

{
    ...
    "window": {
        "show": false,      # the application will be running in the background without window
        "width": 600,
        "height": 500,
        "toolbar": false    # the toolbar with chromium controls is not needed
    },
    ...
}

main.js

var gui = require('nw.gui'),  
    menu = new gui.Menu(),
    menuItem,
    tray;

menuItem = new gui.MenuItem({  
    type: "normal",           
    label: "Exit",                
    tooltip: "Close the application",            
    click: function() {           
        gui.App.quit();
    },
});

menu.append(menuItem);

tray = new gui.Tray({  
    title: 'Healthify',       
    tooltip: 'Healthify is running to protect your health!',       
    icon: 'img/icon.png',
    menu: menu
});

Cool, now the application is running "in the background" and it has a tray icon.

Scope

The main element of the idea is the notification. With that, the application can warn the user about the necessity of the mentioned activities (like drink more often). So, therefore we need a "window", which will be shown and focused regurarly.
It's known about node-webkit, it uses html pages as user interface of the windowed applications. Then a notification.html should be created with the corresponding message. It's a pure html page wihtout any style and javascript code.

Now this notification should be shown somewhere, somehow. We could create a function which checks regurarly (e.g. in every 2 sec) that the notification is visible or not. If it's not visible (because it's already closed) then the application should create/open the notification window. Because it's a warning it won't be good if the window will be just opened but not on top of the others. Fortunately nw.js offers the right API to handle OS' windows, so they will be focused and always on top.

windowForMoving = gui.Window.open('notification.html', {  
    frame: false,
    toolbar: false,        
    focus: true,
    width: 300,
    height: 200,
    x: window.screen.availWidth - 320,
    y: window.screen.availHeight - 200
});
windowForMoving.on('close', function () {  
    windowForMoving.close(true);
    windowForMoving = undefined;
});

windowForMoving.on('loaded', function () {  
    compile.call(windowForMoving.window.document, messageForMoving);
});

windowForMoving.setAlwaysOnTop(true);  
windowForMoving.setShowInTaskbar(false);  
windowForMoving.setResizable(false);  

Great, we have already a notification which can warn the user in the right way. Ok we have a notification that is regurarly popped up, but how can it disable (in the final solution how will be decided which notifcation should be came up). Therefore a configuration is needed which can be modified via tray icon's context menu. The configuration can be very simple:

var configuration = {  
    notifyForMoving: true,
    notifyForBlinking: false,
    notifyForDrinking: false
};

This object will be maintained by the context menu and will be saved into a file, because usually the users would like to use the settings already set before. Specially I stored the configuration object as JSON in a settings.hfy with help of fs module of node.js. As I already mentioned, one of the coolest thing in node-webkit that all node modules can be used as in a normal node.js application.
What should the application do when the user clicked on a checkbox in the context menu? Let's collect them:

  • update the configuration object
  • update the application timers to turn off/on the relevant notifications
  • store the new configuration in the settings.hfy file

The update is an easy step. We need to update the configuration then clear/create the timers for them.

function updateSettings () {  
    if (configuration.notifyForMoving) {
        if (!intervalForMoving) {
            intervalForMoving = setInterval(moving, 30 * 60 * 1000);
        }
    } else {
        if (intervalForMoving) {
            clearInterval(intervalForMoving);
            intervalForMoving = null;
            windowForMoving.close();
        }
    }
}
User experience

Now the "look and feel" not so user friendly at the moment. Therefore I decided that I will use a framework for styling of the pages. Today, the most widely used UI frameworks Boostrap and Foundation give us the possibility to create a unified and clean user interfaces with negligible effort.
With the help of bower I've installed the Foundation package, and at the same time I've installed the separated Foundation font/icon-set too, because it will be needed ;)

notification.html with Foundation components

<body>  
    <div class="row">
        <div class="small-12 columns">
            <img class="logo" src="../img/icon.png"/> 
            <div id="note" class="notification"></div>
        </div>
    </div>                 
    <input type="hidden" id="type"/>
    <div class="row toolbar">
        <div class="small-12 text-center columns">
            <a href="#" class="button round success" onclick="window.close();">
                <i class="fi-check medium"></i> Ok
            </a>
            <a href="#" class="button round success">
                <i class="fi-calendar medium"></i> Snooze
            </a>
        </div>
    </div>
</body>  

When the user run the application for the first time, a Setup Wizard could make the configuration easier, so I created a wizard window also.

Why I choosed Foundation, it is the easy customization. Foundation uses SASS in component styling and we can set a lot of SASS variable to customize our project without any CSS hacking. It's more flexible and faster way to change colors, font styles, and other CSS properties. The details can be found at Foundation's SASS documentation site
Moreover, I found a really nice component to replace the traditional checkbox layout in the application. It is the so called "switch".
Switch is a toggleable element which can represent checkboxes, it has two states, On and Off (checked or not checked).

Actually the user interface of a node-webkit application is a web page with coordinated usage of JavaScript (frameworks) and CSS. Therefore I decided that I try to use some of them. In the Setup Wizard there is a form where user can decide which notification will be useful, and will be turned on. If none of them is turned on, then starting the application is unnecessary. So the "submit" button should be reflected to the state of the form. AngularJS do it for me easily.

index.html code snippet

<!-- Switch (checkbox) 1 of 3 -->  
<div class="switch tiny round">  
     <input id="moving" type="checkbox" name="moving" ng-model="moving" checked="checked"/>
     <label for="moving"></label>
</div>

<!-- Button with Angular directives -->  
<a href="#" class="button round success" id="behealthy" ng-disabled="!(moving || blinking || drinking)">  
    <i class="fi-heart medium"></i> Be healthy
</a>  

Foundation is used for the notification windows as well to ensure the unified design. Finally before we create the executable package, let's see the result of the UI fine tunings.

Setup wizard

Context menu

Notification

Packaging

The packaging is a little bit tricky. For the different operating systems we need to create different executables. First time I run into fails, because the application will generate (unzip) the files in the OS' temp directory. So when we use the following code:

fs.writeFile('settings.hfy', JSON.stringify(firstSetup, null, 4));  

Then the configuration file will be created in the temporary folder. So next time it won't be existed. The solution is that we get the path where the executable is found, and use it for file reading and writing:

var settingsPath = path.resolve(path.dirname(process.execPath), 'settings.hfy');  
fs.writeFile(settingsPath, JSON.stringify(firstSetup, null, 4));  

The following steps are tested only on Windows 8.1

1. - Select all own files and folders + package.json, and create an archive (healthify.zip)

|- bower_components
|- html
|- img
|- js
|- node_modules
|- styles
|- package.json

2. - Change the extension from .zip to .nw
Git bash command!

mv healthify.zip healthify.nw

3. - Create executable
Window Cmd command!

copy /b nw.exe+healthify.nw healthify.exe

4. - Copy healthify.exe and required DLLs to a separate directory

|- d3dcompiler_47.dll
|- ffmpegsumo.dll
|- icudtl.dat
|- libEGL.dll
|- libGLESv2.dll
|- nw.pak
|- healthify.exe

5. - Thats it! Now we can distribute them (in archive for example)

I hope this blog will help people to be more familiar with node-webkit's world.