diff --git a/.gitignore b/.gitignore
index 4796eb8..8006b99 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,3 +32,4 @@ node_modules
# Optional REPL history
.node_repl_history
+
diff --git a/README.md b/README.md
index 78323e1..3719e28 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,506 @@
-# apex-app-desktop
-APEX App as Desktop Application using Github Electron
+**Table of Contents**
+
+- [CrappyBird](#apex-desktop-application-using-github-electron)
+ - [Description](#description)
+ - [Helpful links](#helpful-links)
+ - [Successful](#successful)
+ - [Problems](#problems)
+ - [Changelog](#changelog)
+ - [Installation](#installation)
+ - [Preparations](#preparations)
+ - [Install Node.js](#install-node.js)
+ - [Electron App](#electron-app)
+ - [package.json](#package.json)
+ - [Install Electron into app folder and globally](#install-electron-into-app-folder-and-globally)
+ - [main.js](#main.js)
+ - [index.html](#index.html)
+ - [apexutils.js](#apexutils.js)
+ - [electronapex.js](#electronapex.js)
+ - [Starting the App](#starting-the-app)
+ - [Bundle the app into a real Application](#bundle-the-app-into-a-real-Application)
+ - [Sample functions](#sample-functions)
+ - [Desktop notifications](#desktop-notifications)
+ - [File open](#file-open)
+ - [APEX Authorization Scheme](#apex-authorization-scheme)
+ - [License](#license)
+ - [Preview](#preview)
+
+#APEX Desktop Application using Github Electron
+##Description
+This is not a ready to use software, but much more a showcase and tutorial how to build desktop APEX apps using Electron from Github...
+
+This showcase describes the steps I´ve been done (maybe wrong ones included, too :) ), there I had success and there a had problems with.
+
+##Helpful Links
+- [Electron](http://electron.atom.io)
+- [Electron on Github](https://github.com/atom/electron)
+- [Node.js](https://nodejs.org) (required on your Machine to install all other things)
+- [Electron API docs](https://github.com/atom/electron/tree/master/docs/api)
+- [Electron Packager](https://github.com/maxogden/electron-packager) (Packaging Apps into container to rollout)
+- [Electron Tutorial](http://ryanfrench.co/2015/05/02/harmonic_tutorial_1.html) (Sample App tutorial)
+
+##Successful
+- Embed APEX App into electron using webview
+- Copy&Paste support with App menu (menubar)
+- Open links (href / window.open) inside of electron app
+- Desktop Notifications
+- React on console.log events from APEX app and do things with this (for example opening a local file)
+- App style without border and titlebar
+- fullscreen mode
+
+##Problems
+- React on electron functions triggered from APEX app (nodeintegration and webview-preload functions in webviews does not work for APEX app (maybe because of the changed URL if page gets rendered))
+- Session state (on a hard refresh, app returns to login page (possible solution: on close get last webview URL and save it))
+- Closing to OS X dock: Reopening the app shows login page, instead of content from before (minimize works well)
+
+##Changelog
+
+####0.8 - Beta Testing
+
+
+##Installation
+###Preparations
+####Install Node.js
+It is required to have a up and running Node.js installation on your local development machine.
+Either install it using a package manager, or download the latest version from Node.js homepage...for example:
+- Ubuntu:
+```
+apt-get install nodejs
+apt-get install npm
+```
+
+- Mac OS X (Homebrew):
+```
+brew install nodejs
+```
+
+- Windows:
+Download and install it from Nodejs homepage
+
+npm is the package manager for node applications. Thus electron is based on node, npm is working all the same...
+
+###Electron App
+####package.json
+**not required if you decide to download this repository!**
+The first step is to generate the package.json file
+```
+mkdir apex-app-desktop
+cd apex-app-desktop
+npm init
+```
+
+The resulting json file could look like this:
+```json
+{
+ "name": "apex-app-desktop",
+ "version": "1.0.0",
+ "description": "APEX Desktop Application using Github Electron",
+ "main": "main.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/Dani3lSun/apex-app-desktop.git"
+ },
+ "author": "Daniel Hochleitner",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/Dani3lSun/apex-app-desktop/issues"
+ },
+ "homepage": "https://github.com/Dani3lSun/apex-app-desktop#readme",
+ "devDependencies": {
+ "electron-prebuilt": "^0.36.4"
+ }
+}
+```
+
+Now we have to add **"start": "electron .""** to the scripts tag of the json file.
+```json
+{
+ "name": "apex-app-desktop",
+ "version": "1.0.0",
+ "description": "APEX Desktop Application using Github Electron",
+ "main": "main.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "start": "electron ."
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/Dani3lSun/apex-app-desktop.git"
+ },
+ "author": "Daniel Hochleitner",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/Dani3lSun/apex-app-desktop/issues"
+ },
+ "homepage": "https://github.com/Dani3lSun/apex-app-desktop#readme",
+ "devDependencies": {
+ "electron-prebuilt": "^0.36.4"
+ }
+}
+```
+
+####Install Electron into app folder and globally
+**not required if you decide to download this repository!**
+
+```
+npm i electron-prebuilt --save-dev #for local app folder
+npm i -g electron-prebuilt #globally for using commandline
+```
+
+####main.js
+**not required if you decide to download this repository!**
+
+This is the main javascript file for the app (also mentioned in the package.json file). It will initialize the whole application.
+Here you can open new windows, react on app events or create menus...
+
+```javascript
+// Libraries used in the app
+var app = require('app');
+var BrowserWindow = require('browser-window');
+var appMenu = require("menu");
+app.commandLine.appendSwitch('ignore-certificate-errors', 'true');
+// Crash Reporter (Optional)
+require('crash-reporter').start({
+ productName: 'APEX Plugins',
+ companyName: 'Daniel Hochleitner',
+ submitURL: 'https://github.com/Dani3lSun/apex-app-desktop/issues',
+ autoSubmit: false
+});
+// Init mainWindow
+var mainWindow = null;
+// Kill the app when all windows are closed
+app.on('mainWindow-all-closed', function() {
+ if (process.platform != 'darwin') {
+ app.quit();
+ }
+});
+// App is loaded
+app.on('ready', function() {
+ // Create the main window for the app
+ mainWindow = new BrowserWindow({
+ "width": 1280, // init width
+ "height": 800, // init height
+ "min-width": 1024,
+ "min-height": 800,
+ "resizable": true,
+ "use-content-size": true,
+ "transparent": true, // better look in OSX
+ "title-bar-style": "hidden-inset" // better look in OSX
+ });
+ // Load in content with webview to APEX app
+ mainWindow.loadURL('file://' + __dirname + '/index.html');
+ // Ensure that garbage collection occurs when the window is closed
+ mainWindow.on('closed', function(e) {
+ mainWindow = null;
+ });
+ // Create the Application main menu (required for Copy&Paste Support)
+ // Application menu
+ var template = [{
+ label: "Application",
+ submenu: [{
+ label: "About Application",
+ selector: "orderFrontStandardAboutPanel:"
+ }, {
+ type: "separator"
+ }, {
+ label: "Quit",
+ accelerator: "Command+Q",
+ click: function() {
+ app.quit();
+ }
+ }]
+ // Edit menu
+ }, {
+ label: "Edit",
+ submenu: [{
+ label: "Undo",
+ accelerator: "CmdOrCtrl+Z",
+ selector: "undo:"
+ }, {
+ label: "Redo",
+ accelerator: "Shift+CmdOrCtrl+Z",
+ selector: "redo:"
+ }, {
+ type: "separator"
+ }, {
+ label: "Cut",
+ accelerator: "CmdOrCtrl+X",
+ selector: "cut:"
+ }, {
+ label: "Copy",
+ accelerator: "CmdOrCtrl+C",
+ selector: "copy:"
+ }, {
+ label: "Paste",
+ accelerator: "CmdOrCtrl+V",
+ selector: "paste:"
+ }, {
+ label: "Select All",
+ accelerator: "CmdOrCtrl+A",
+ selector: "selectAll:"
+ }]
+ }, {
+ // View menu
+ label: 'View',
+ submenu: [{
+ label: 'Reload',
+ accelerator: 'CmdOrCtrl+R',
+ click: function(item, focusedWindow) {
+ if (focusedWindow)
+ focusedWindow.reload();
+ }
+ }, {
+ label: 'Toggle Full Screen',
+ accelerator: (function() {
+ if (process.platform == 'darwin')
+ return 'Ctrl+Command+F';
+ else
+ return 'F11';
+ })(),
+ click: function(item, focusedWindow) {
+ if (focusedWindow)
+ focusedWindow.setFullScreen(!focusedWindow.isFullScreen());
+ }
+ }, {
+ label: 'Toggle Developer Tools',
+ accelerator: (function() {
+ if (process.platform == 'darwin')
+ return 'Alt+Command+I';
+ else
+ return 'Ctrl+Shift+I';
+ })(),
+ click: function(item, focusedWindow) {
+ if (focusedWindow)
+ focusedWindow.toggleDevTools();
+ }
+ }]
+ }];
+ // set menu with options from above
+ appMenu.setApplicationMenu(appMenu.buildFromTemplate(template));
+});
+// Create the main window for the app when App is reopened from OSX Dock
+app.on('activate', function(e, hasVisibleWindows) {
+ if (mainWindow === null) {
+ mainWindow = new BrowserWindow({
+ "width": 1280, //init width
+ "height": 800, // init height
+ "min-width": 1024,
+ "min-height": 800,
+ "resizable": true,
+ "use-content-size": true,
+ "transparent": true, // better look in OSX
+ "title-bar-style": "hidden-inset" // better look in OSX
+ });
+ mainWindow.loadURL('file://' + __dirname + '/index.html');
+ mainWindow.on('closed', function() {
+ mainWindow = null;
+ });
+ }
+});
+```
+
+####index.html
+**not required if you decide to download this repository!**
+
+This file is opened from main.js. It includes the webview element which has as source the APEX URL.
+Also this file has js functions that get triggered from webview events.
+
+```html
+
+
+
+
+ APEX Plugins
+
+
+
+
+
+
+
+
+
+
+```
+
+####apexutil.js
+**not required if you decide to download this repository!**
+
+This file contains functions for APEX triggered events in electron opponent for electronapex.js functions inside APEX.
+Generally spoken: All functions that are required by electron for the APEX js functions. Wrapped into a own file...
+
+```javascript
+// Functions for APEX triggered events in electron
+// opponent for electronapex.js functions inside APEX
+//
+
+// electron functions
+electron = window.require('electron');
+
+var stringDevider = "::";
+
+module.exports = {
+ // opens a local file from consol.log text from APEX
+ openLocalFile: function(consoleString) {
+ var position = consoleString.indexOf(stringDevider) + 2;
+ path = consoleString.substr(position);
+ var shell = electron.shell;
+ shell.openItem(path);
+ }
+};
+```
+
+####electronapex.js
+**not required if you decide to download this repository!**
+
+This file contains js functions used in the APEX app itself. For example notifications or opening files.
+File open uses console.log to trigger the event on electron side (not nice but works...hopefully someone was here more successful than I!).
+
+**Upload this file to your APEX application or workspace!**
+
+```javascript
+// Functions for electron used in APEX
+// functions often uses wrapper around console.log (webview.addEventListener('console-message' gets these events))
+// upload to APEX App!
+
+var stringDevider = "::";
+// global namespace
+var electronapex = {
+ // open file
+ openFile: function(path) {
+ var type = 'open-file';
+ console.log(type + stringDevider + path);
+ },
+ // check if notifications are enabled
+ notifyCheck: function() {
+ // Let's check if the browser supports notifications
+ if (!("Notification" in window)) {
+ alert("This browser does not support system notifications");
+ }
+ // Let's check whether notification permissions have already been granted
+ else if (Notification.permission === "granted") {
+ // If it's okay let's create a notification
+ var notification = new Notification("You already have granted Notification permissions, great!:)");
+ }
+ // Otherwise, we need to ask the user for permission
+ else if (Notification.permission !== 'denied') {
+ Notification.requestPermission(function(permission) {
+ // If the user accepts, let's create a notification
+ if (permission === "granted") {
+ var notification = new Notification("Now Notifications should work!");
+ }
+ });
+ }
+ },
+ // send a notification
+ doNotify: function(text) {
+ var notification = new Notification(text);
+ }
+};
+```
+
+####Starting the App
+- If you cloned this repository than you have to do this:
+```
+npm install #installs all dependencies from package.json
+npm start
+```
+
+- If you did all by yourself:
+```
+npm start
+```
+
+####Bundle the app into a real Application
+To create a real application from your development app folder you need the "electron-packager" package installed globally:
+```
+npm install electron-packager -g
+```
+
+Now you can create the application on commandline with:
+```
+cd apex-app-desktop
+electron-packager . "APEX Plugins" --platform=darwin --arch=x64 --version=0.36.4 --app-version=1.0.0 --icon img/app.icns
+```
+This command creates a "APEX Plugins.app" for Mac OS X (darwin) in 64bit, optionally takes the image from img folder as application icon. ""--version" is the version string for electron, can be found in your package.json file!
+
+##Sample functions
+###Desktop notifications
+For this functionality I created a dynamic action (on button click) with this code:
+```javascript
+var text = $v('P15_NOTIFY_TEXT'); //read text from APEX item
+electronapex.doNotify(text); //use function from electronapex.js
+```
+
+###Desktop notifications
+For this functionality I created a dynamic action (on button click) with this code:
+```javascript
+var text = $v('P15_NOTIFY_TEXT'); //read text from APEX item
+electronapex.doNotify(text); //use function from electronapex.js
+```
+
+Uses the Browser Notification API [MDN](https://developer.mozilla.org/de/docs/Web/API/notification)
+
+###File open
+Here I wrapped the file path + type "open-file" into console.log. On electron side I react on this with "webview.addEventListener('console-message')".
+I created a dynamic action (on button click) with this code:
+```javascript
+var path = $v('P15_FILE_PATH'); //read path from APEX item
+electronapex.openFile(path); //use function from electronapex.js
+```
+
+###APEX Authorization Scheme
+Thus the webview is created with the option (useragent="APEXDESKTOP" - index.html),
+we can use this on APEX side to create a authorization scheme which is true or false based on the user agent of the "browser".
+
+Name: Is Electron User Agent
+Message: This page is only visible inside a electron desktop app
+
+```language-sql
+DECLARE
+ l_user_agent VARCHAR2(500);
+BEGIN
+ l_user_agent := owa_util.get_cgi_env('HTTP_USER_AGENT');
+ IF l_user_agent = 'APEXDESKTOP' THEN
+ RETURN TRUE;
+ ELSE
+ RETURN FALSE;
+ END IF;
+END;
+```
+
+##License
+This software is under **MIT License**.
+
+[LICENSE](https://github.com/Dani3lSun/apex-app-desktop/blob/master/LICENSE)
+
+##Preview
+![]()
+---
diff --git a/apexutils.js b/apexutils.js
new file mode 100644
index 0000000..a2f82da
--- /dev/null
+++ b/apexutils.js
@@ -0,0 +1,18 @@
+// Functions for APEX triggered events in electron
+// opponent for electronapex.js functions inside APEX
+//
+
+// electron functions
+electron = window.require('electron');
+
+var stringDevider = "::";
+
+module.exports = {
+ // opens a local file from consol.log text from APEX
+ openLocalFile: function(consoleString) {
+ var position = consoleString.indexOf(stringDevider) + 2;
+ path = consoleString.substr(position);
+ var shell = electron.shell;
+ shell.openItem(path);
+ }
+};
diff --git a/electronapex.js b/electronapex.js
new file mode 100644
index 0000000..c24a6be
--- /dev/null
+++ b/electronapex.js
@@ -0,0 +1,38 @@
+// Functions for electron used in APEX
+// functions often uses wrapper around console.log (webview.addEventListener('console-message' gets these events))
+// upload to APEX App!
+
+var stringDevider = "::";
+// global namespace
+var electronapex = {
+ // open file
+ openFile: function(path) {
+ var type = 'open-file';
+ console.log(type + stringDevider + path);
+ },
+ // check if notifications are enabled
+ notifyCheck: function() {
+ // Let's check if the browser supports notifications
+ if (!("Notification" in window)) {
+ alert("This browser does not support system notifications");
+ }
+ // Let's check whether notification permissions have already been granted
+ else if (Notification.permission === "granted") {
+ // If it's okay let's create a notification
+ var notification = new Notification("You already have granted Notification permissions, great!:)");
+ }
+ // Otherwise, we need to ask the user for permission
+ else if (Notification.permission !== 'denied') {
+ Notification.requestPermission(function(permission) {
+ // If the user accepts, let's create a notification
+ if (permission === "granted") {
+ var notification = new Notification("Now Notifications should work!");
+ }
+ });
+ }
+ },
+ // send a notification
+ doNotify: function(text) {
+ var notification = new Notification(text);
+ }
+};
diff --git a/img/app.icns b/img/app.icns
new file mode 100644
index 0000000..5278339
Binary files /dev/null and b/img/app.icns differ
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..de22da5
--- /dev/null
+++ b/index.html
@@ -0,0 +1,39 @@
+
+
+
+
+ APEX Plugins
+
+
+
+
+
+
+
+
+
+
diff --git a/main.js b/main.js
new file mode 100644
index 0000000..8053593
--- /dev/null
+++ b/main.js
@@ -0,0 +1,143 @@
+// Libraries used in the app
+var app = require('app');
+var BrowserWindow = require('browser-window');
+var appMenu = require("menu");
+app.commandLine.appendSwitch('ignore-certificate-errors', 'true');
+// Crash Reporter (Optional)
+require('crash-reporter').start({
+ productName: 'APEX Plugins',
+ companyName: 'Daniel Hochleitner',
+ submitURL: 'https://github.com/Dani3lSun/apex-app-desktop/issues',
+ autoSubmit: false
+});
+// Init mainWindow
+var mainWindow = null;
+// Kill the app when all windows are closed
+app.on('mainWindow-all-closed', function() {
+ if (process.platform != 'darwin') {
+ app.quit();
+ }
+});
+// App is loaded
+app.on('ready', function() {
+ // Create the main window for the app
+ mainWindow = new BrowserWindow({
+ "width": 1280, // init width
+ "height": 800, // init height
+ "min-width": 1024,
+ "min-height": 800,
+ "resizable": true,
+ "use-content-size": true,
+ "transparent": true, // better look in OSX
+ "title-bar-style": "hidden-inset" // better look in OSX
+ });
+ // Load in content with webview to APEX app
+ mainWindow.loadURL('file://' + __dirname + '/index.html');
+ // Ensure that garbage collection occurs when the window is closed
+ mainWindow.on('closed', function(e) {
+ mainWindow = null;
+ });
+ // Create the Application main menu (required for Copy&Paste Support)
+ // Application menu
+ var template = [{
+ label: "Application",
+ submenu: [{
+ label: "About Application",
+ selector: "orderFrontStandardAboutPanel:"
+ }, {
+ type: "separator"
+ }, {
+ label: "Quit",
+ accelerator: "Command+Q",
+ click: function() {
+ app.quit();
+ }
+ }]
+ // Edit menu
+ }, {
+ label: "Edit",
+ submenu: [{
+ label: "Undo",
+ accelerator: "CmdOrCtrl+Z",
+ selector: "undo:"
+ }, {
+ label: "Redo",
+ accelerator: "Shift+CmdOrCtrl+Z",
+ selector: "redo:"
+ }, {
+ type: "separator"
+ }, {
+ label: "Cut",
+ accelerator: "CmdOrCtrl+X",
+ selector: "cut:"
+ }, {
+ label: "Copy",
+ accelerator: "CmdOrCtrl+C",
+ selector: "copy:"
+ }, {
+ label: "Paste",
+ accelerator: "CmdOrCtrl+V",
+ selector: "paste:"
+ }, {
+ label: "Select All",
+ accelerator: "CmdOrCtrl+A",
+ selector: "selectAll:"
+ }]
+ }, {
+ // View menu
+ label: 'View',
+ submenu: [{
+ label: 'Reload',
+ accelerator: 'CmdOrCtrl+R',
+ click: function(item, focusedWindow) {
+ if (focusedWindow)
+ focusedWindow.reload();
+ }
+ }, {
+ label: 'Toggle Full Screen',
+ accelerator: (function() {
+ if (process.platform == 'darwin')
+ return 'Ctrl+Command+F';
+ else
+ return 'F11';
+ })(),
+ click: function(item, focusedWindow) {
+ if (focusedWindow)
+ focusedWindow.setFullScreen(!focusedWindow.isFullScreen());
+ }
+ }, {
+ label: 'Toggle Developer Tools',
+ accelerator: (function() {
+ if (process.platform == 'darwin')
+ return 'Alt+Command+I';
+ else
+ return 'Ctrl+Shift+I';
+ })(),
+ click: function(item, focusedWindow) {
+ if (focusedWindow)
+ focusedWindow.toggleDevTools();
+ }
+ }]
+ }];
+ // set menu with options from above
+ appMenu.setApplicationMenu(appMenu.buildFromTemplate(template));
+});
+// Create the main window for the app when App is reopened from OSX Dock
+app.on('activate', function(e, hasVisibleWindows) {
+ if (mainWindow === null) {
+ mainWindow = new BrowserWindow({
+ "width": 1280, //init width
+ "height": 800, // init height
+ "min-width": 1024,
+ "min-height": 800,
+ "resizable": true,
+ "use-content-size": true,
+ "transparent": true, // better look in OSX
+ "title-bar-style": "hidden-inset" // better look in OSX
+ });
+ mainWindow.loadURL('file://' + __dirname + '/index.html');
+ mainWindow.on('closed', function() {
+ mainWindow = null;
+ });
+ }
+});
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..21d5586
--- /dev/null
+++ b/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "apex-app-desktop",
+ "version": "1.0.0",
+ "description": "APEX Desktop Application using Github Electron",
+ "main": "main.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "start": "electron ."
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/Dani3lSun/apex-app-desktop.git"
+ },
+ "author": "Daniel Hochleitner",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/Dani3lSun/apex-app-desktop/issues"
+ },
+ "homepage": "https://github.com/Dani3lSun/apex-app-desktop#readme",
+ "devDependencies": {
+ "electron-prebuilt": "^0.36.4"
+ }
+}