0) ? this._requestWithCallback(...arguments) : this._requestAsPromise();\n }\n\n /**\n * Old permissions implementation deprecated in favor of a promise based one\n * @deprecated Since V1.0.4\n * @param {Function} onGranted - Function to execute once permission is granted\n * @param {Function} onDenied - Function to execute once permission is denied\n * @return {void}\n */\n _requestWithCallback(onGranted, onDenied) {\n const existing = this.get();\n\n var resolve = (result = this._win.Notification.permission) => {\n if (typeof(result)==='undefined' && this._win.webkitNotifications)\n result = this._win.webkitNotifications.checkPermission();\n if (result === this.GRANTED || result === 0) {\n if (onGranted) onGranted();\n } else if (onDenied) onDenied();\n }\n\n /* Permissions already set */\n if (existing !== this.DEFAULT) {\n resolve(existing);\n }\n /* Safari 6+, Legacy webkit browsers */\n else if (this._win.webkitNotifications && this._win.webkitNotifications.checkPermission) {\n this._win.webkitNotifications.requestPermission(resolve);\n }\n /* Chrome 23+ */\n else if (this._win.Notification && this._win.Notification.requestPermission) {\n this._win.Notification.requestPermission().then(resolve).catch(function () {\n if (onDenied) onDenied();\n });\n }\n /* Let the user continue by default */\n else if (onGranted) {\n onGranted();\n }\n }\n\n /**\n * Requests permission for desktop notifications in a promise based way\n * @return {Promise}\n */\n _requestAsPromise() {\n const existing = this.get();\n\n let isGranted = result => (result === this.GRANTED || result === 0);\n\n /* Permissions already set */\n var hasPermissions = (existing !== this.DEFAULT);\n\n /* Safari 6+, Chrome 23+ */\n var isModernAPI = (this._win.Notification && this._win.Notification.requestPermission);\n\n /* Legacy webkit browsers */\n var isWebkitAPI = (this._win.webkitNotifications && this._win.webkitNotifications.checkPermission);\n\n return new Promise((resolvePromise, rejectPromise) => {\n\n var resolver = result => (isGranted(result)) ? resolvePromise() : rejectPromise();\n\n if (hasPermissions) {\n resolver(existing)\n }\n else if (isWebkitAPI) {\n this._win.webkitNotifications.requestPermission(result => { resolver(result) });\n }\n else if (isModernAPI) {\n this._win.Notification.requestPermission().then(result => { resolver(result) }).catch(rejectPromise)\n }\n else resolvePromise()\n })\n }\n\n /**\n * Returns whether Push has been granted permission to run\n * @return {Boolean}\n */\n has() {\n return this.get() === this.GRANTED;\n }\n\n /**\n * Gets the permission level\n * @return {Permission} The permission level\n */\n get() {\n let permission;\n\n /* Safari 6+, Chrome 23+ */\n if (this._win.Notification && this._win.Notification.permission)\n permission = this._win.Notification.permission;\n\n /* Legacy webkit browsers */\n else if (this._win.webkitNotifications && this._win.webkitNotifications.checkPermission)\n permission = this._permissions[this._win.webkitNotifications.checkPermission()];\n\n /* Firefox Mobile */\n else if (navigator.mozNotification)\n permission = this.GRANTED;\n\n /* IE9+ */\n else if (this._win.external && this._win.external.msIsSiteMode)\n permission = this._win.external.msIsSiteMode() ? this.GRANTED : this.DEFAULT;\n\n else\n permission = this.GRANTED;\n\n return permission;\n }\n}\n","import Messages from \"./Messages\";\nimport Permission from \"./Permission\";\nimport Util from \"./Util\";\n/* Import notification agents */\nimport DesktopAgent from \"./agents/DesktopAgent\";\nimport MobileChromeAgent from \"./agents/MobileChromeAgent\";\nimport MobileFirefoxAgent from \"./agents/MobileFirefoxAgent\";\nimport MSAgent from \"./agents/MSAgent\";\nimport WebKitAgent from \"./agents/WebKitAgent\";\n\nexport default class Push {\n\n constructor(win) {\n /* Private variables */\n\n /* ID to use for new notifications */\n this._currentId = 0;\n\n /* Map of open notifications */\n this._notifications = {};\n\n /* Window object */\n this._win = win;\n\n /* Public variables */\n this.Permission = new Permission(win);\n\n /* Agents */\n this._agents = {\n desktop: new DesktopAgent(win),\n chrome: new MobileChromeAgent(win),\n firefox: new MobileFirefoxAgent(win),\n ms: new MSAgent(win),\n webkit: new WebKitAgent(win)\n };\n\n this._configuration = {\n serviceWorker: '/serviceWorker.min.js',\n fallback: function(payload) {}\n }\n }\n\n /**\n * Closes a notification\n * @param {Notification} notification\n * @return {Boolean} boolean denoting whether the operation was successful\n * @private\n */\n _closeNotification(id) {\n let success = true;\n const notification = this._notifications[id];\n\n if (notification !== undefined) {\n success = this._removeNotification(id);\n\n /* Safari 6+, Firefox 22+, Chrome 22+, Opera 25+ */\n if (this._agents.desktop.isSupported())\n this._agents.desktop.close(notification);\n\n /* Legacy WebKit browsers */\n else if (this._agents.webkit.isSupported())\n this._agents.webkit.close(notification);\n\n /* IE9 */\n else if (this._agents.ms.isSupported())\n this._agents.ms.close();\n\n else {\n success = false;\n throw new Error(Messages.errors.unknown_interface);\n }\n\n return success;\n }\n\n return false;\n }\n\n /**\n * Adds a notification to the global dictionary of notifications\n * @param {Notification} notification\n * @return {Integer} Dictionary key of the notification\n * @private\n */\n _addNotification(notification) {\n const id = this._currentId;\n this._notifications[id] = notification;\n this._currentId++;\n return id;\n }\n\n /**\n * Removes a notification with the given ID\n * @param {Integer} id - Dictionary key/ID of the notification to remove\n * @return {Boolean} boolean denoting success\n * @private\n */\n _removeNotification(id) {\n let success = false;\n\n if (this._notifications.hasOwnProperty(id)) {\n /* We're successful if we omit the given ID from the new array */\n delete this._notifications[id];\n success = true;\n }\n\n return success;\n }\n\n /**\n * Creates the wrapper for a given notification\n *\n * @param {Integer} id - Dictionary key/ID of the notification\n * @param {Map} options - Options used to create the notification\n * @returns {Map} wrapper hashmap object\n * @private\n */\n _prepareNotification(id, options) {\n let wrapper;\n\n /* Wrapper used to get/close notification later on */\n wrapper = {\n get: () => {\n return this._notifications[id];\n },\n\n close: () => {\n this._closeNotification(id);\n }\n };\n\n /* Autoclose timeout */\n if (options.timeout) {\n setTimeout(() => {\n wrapper.close();\n }, options.timeout);\n }\n\n return wrapper;\n }\n\n /**\n * Find the most recent notification from a ServiceWorker and add it to the global array\n * @param notifications\n * @private\n */\n _serviceWorkerCallback(notifications, options, resolve) {\n let id = this._addNotification(notifications[notifications.length - 1]);\n\n /* Listen for close requests from the ServiceWorker */\n navigator.serviceWorker.addEventListener('message', event => {\n const data = JSON.parse(event.data);\n\n if (data.action === 'close' && Number.isInteger(data.id))\n this._removeNotification(data.id);\n });\n\n resolve(this._prepareNotification(id, options));\n }\n\n /**\n * Callback function for the 'create' method\n * @return {void}\n * @private\n */\n _createCallback(title, options, resolve) {\n let notification = null;\n let onClose;\n\n /* Set empty settings if none are specified */\n options = options || {};\n\n /* onClose event handler */\n onClose = (id) => {\n /* A bit redundant, but covers the cases when close() isn't explicitly called */\n this._removeNotification(id);\n if (Util.isFunction(options.onClose)) {\n options.onClose.call(this, notification);\n }\n };\n\n /* Safari 6+, Firefox 22+, Chrome 22+, Opera 25+ */\n if (this._agents.desktop.isSupported()) {\n try {\n /* Create a notification using the API if possible */\n notification = this._agents.desktop.create(title, options);\n } catch (e) {\n const id = this._currentId;\n const sw = this.config().serviceWorker;\n const cb = (notifications) => this._serviceWorkerCallback(notifications, options, resolve);\n /* Create a Chrome ServiceWorker notification if it isn't supported */\n if (this._agents.chrome.isSupported()) {\n this._agents.chrome.create(id, title, options, sw, cb);\n }\n }\n /* Legacy WebKit browsers */\n } else if (this._agents.webkit.isSupported())\n notification = this._agents.webkit.create(title, options);\n\n /* Firefox Mobile */\n else if (this._agents.firefox.isSupported())\n this._agents.firefox.create(title, options);\n\n /* IE9 */\n else if (this._agents.ms.isSupported())\n notification = this._agents.ms.create(title, options);\n\n /* Default fallback */\n else {\n options.title = title;\n this.config().fallback(options);\n }\n\n if (notification !== null) {\n const id = this._addNotification(notification);\n const wrapper = this._prepareNotification(id, options);\n\n /* Notification callbacks */\n if (Util.isFunction(options.onShow))\n notification.addEventListener('show', options.onShow);\n\n if (Util.isFunction(options.onError))\n notification.addEventListener('error', options.onError);\n\n if (Util.isFunction(options.onClick))\n notification.addEventListener('click', options.onClick);\n\n notification.addEventListener('close', () => {\n onClose(id);\n });\n\n notification.addEventListener('cancel', () => {\n onClose(id);\n });\n\n /* Return the wrapper so the user can call close() */\n resolve(wrapper);\n }\n\n /* By default, pass an empty wrapper */\n resolve(null);\n }\n\n /**\n * Creates and displays a new notification\n * @param {Array} options\n * @return {Promise}\n */\n create(title, options) {\n let promiseCallback;\n\n /* Fail if no or an invalid title is provided */\n if (!Util.isString(title)) {\n throw new Error(Messages.errors.invalid_title);\n }\n\n /* Request permission if it isn't granted */\n if (!this.Permission.has()) {\n promiseCallback = (resolve, reject) => {\n this.Permission.request().then(() => {\n this._createCallback(title, options, resolve);\n }).catch(() => {\n reject(Messages.errors.permission_denied);\n })\n };\n } else {\n promiseCallback = (resolve, reject) => {\n try {\n this._createCallback(title, options, resolve);\n } catch (e) {\n reject(e);\n }\n };\n }\n\n return new Promise(promiseCallback);\n }\n\n /**\n * Returns the notification count\n * @return {Integer} The notification count\n */\n count() {\n let count = 0;\n let key;\n\n for (key in this._notifications)\n if (this._notifications.hasOwnProperty(key)) count++;\n\n return count;\n }\n\n /**\n * Closes a notification with the given tag\n * @param {String} tag - Tag of the notification to close\n * @return {Boolean} boolean denoting success\n */\n close(tag) {\n let key, notification;\n\n for (key in this._notifications) {\n if (this._notifications.hasOwnProperty(key)) {\n notification = this._notifications[key];\n\n /* Run only if the tags match */\n if (notification.tag === tag) {\n\n /* Call the notification's close() method */\n return this._closeNotification(key);\n }\n }\n }\n }\n\n /**\n * Clears all notifications\n * @return {Boolean} boolean denoting whether the clear was successful in closing all notifications\n */\n clear() {\n let key, success = true;\n\n for (key in this._notifications)\n if (this._notifications.hasOwnProperty(key))\n success = success && this._closeNotification(key);\n\n return success;\n }\n\n /**\n * Denotes whether Push is supported in the current browser\n * @returns {boolean}\n */\n supported() {\n let supported = false;\n\n for (var agent in this._agents)\n if (this._agents.hasOwnProperty(agent))\n supported = supported || this._agents[agent].isSupported()\n\n return supported;\n }\n\n /**\n * Modifies settings or returns all settings if no parameter passed\n * @param settings\n */\n config(settings) {\n if (typeof settings !== 'undefined' || settings !== null && Util.isObject(settings))\n Util.objectMerge(this._configuration, settings);\n return this._configuration;\n }\n\n /**\n * Copies the functions from a plugin to the main library\n * @param plugin\n */\n extend(manifest) {\n var plugin, Plugin,\n hasProp = {}.hasOwnProperty;\n\n if (!hasProp.call(manifest, 'plugin')) {\n throw new Error(Messages.errors.invalid_plugin);\n } else {\n if (hasProp.call(manifest, 'config') && Util.isObject(manifest.config) && manifest.config !== null) {\n this.config(manifest.config);\n }\n\n Plugin = manifest.plugin;\n plugin = new Plugin(this.config())\n\n for (var member in plugin) {\n if (hasProp.call(plugin, member) && Util.isFunction(plugin[member]))\n this[member] = plugin[member];\n }\n }\n }\n}\n","export default class Util {\n static isUndefined(obj) {\n return obj === undefined;\n }\n\n static isString(obj) {\n return typeof obj === 'string';\n }\n\n static isFunction(obj) {\n return obj && {}.toString.call(obj) === '[object Function]';\n }\n\n static isObject(obj) {\n return typeof obj === 'object'\n }\n\n static objectMerge(target, source) {\n for (var key in source) {\n if (target.hasOwnProperty(key) && this.isObject(target[key]) && this.isObject(source[key])) {\n this.objectMerge(target[key], source[key]);\n } else {\n target[key] = source[key]\n }\n }\n }\n}\n","export default class AbstractAgent {\n constructor(win) {\n this._win = win;\n }\n}\n","import AbstractAgent from './AbstractAgent';\nimport Util from '../Util';\n\n/**\n * Notification agent for modern desktop browsers:\n * Safari 6+, Firefox 22+, Chrome 22+, Opera 25+\n */\nexport default class DesktopAgent extends AbstractAgent {\n\n /**\n * Returns a boolean denoting support\n * @returns {Boolean} boolean denoting whether webkit notifications are supported\n */\n isSupported() {\n return this._win.Notification !== undefined;\n }\n\n /**\n * Creates a new notification\n * @param title - notification title\n * @param options - notification options array\n * @returns {Notification}\n */\n create(title, options) {\n return new this._win.Notification(\n title,\n {\n icon: (Util.isString(options.icon) || Util.isUndefined(options.icon)) ? options.icon : options.icon.x32,\n body: options.body,\n tag: options.tag,\n requireInteraction: options.requireInteraction\n }\n );\n }\n\n /**\n * Close a given notification\n * @param notification - notification to close\n */\n close(notification) {\n notification.close();\n }\n}\n","import AbstractAgent from './AbstractAgent';\nimport Util from '../Util';\n\n/**\n * Notification agent for IE9\n */\nexport default class MSAgent extends AbstractAgent {\n\n /**\n * Returns a boolean denoting support\n * @returns {Boolean} boolean denoting whether webkit notifications are supported\n */\n isSupported() {\n return (this._win.external !== undefined) && (this._win.external.msIsSiteMode !== undefined);\n }\n\n /**\n * Creates a new notification\n * @param title - notification title\n * @param options - notification options array\n * @returns {Notification}\n */\n create(title, options) {\n /* Clear any previous notifications */\n this._win.external.msSiteModeClearIconOverlay();\n\n this._win.external.msSiteModeSetIconOverlay(\n ((Util.isString(options.icon) || Util.isUndefined(options.icon))\n ? options.icon\n : options.icon.x16), title\n );\n\n this._win.external.msSiteModeActivate();\n\n return null;\n }\n\n /**\n * Close a given notification\n * @param notification - notification to close\n */\n close() {\n this._win.external.msSiteModeClearIconOverlay()\n }\n}\n","import AbstractAgent from './AbstractAgent';\nimport Util from '../Util';\nimport Messages from '../Messages';\n\n/**\n * Notification agent for modern desktop browsers:\n * Safari 6+, Firefox 22+, Chrome 22+, Opera 25+\n */\nexport default class MobileChromeAgent extends AbstractAgent {\n\n /**\n * Returns a boolean denoting support\n * @returns {Boolean} boolean denoting whether webkit notifications are supported\n */\n isSupported() {\n return this._win.navigator !== undefined &&\n this._win.navigator.serviceWorker !== undefined;\n }\n\n /**\n * Returns the function body as a string\n * @param func\n */\n getFunctionBody(func) {\n return func.toString().match(/function[^{]+{([\\s\\S]*)}$/)[1];\n }\n\n /**\n * Creates a new notification\n * @param title - notification title\n * @param options - notification options array\n * @returns {Notification}\n */\n create(id, title, options, serviceWorker, callback) {\n /* Register ServiceWorker */\n this._win.navigator.serviceWorker.register(serviceWorker);\n\n this._win.navigator.serviceWorker.ready.then(registration => {\n /* Local data the service worker will use */\n let localData = {\n id: id,\n link: options.link,\n origin: document.location.href,\n onClick: (Util.isFunction(options.onClick)) ? this.getFunctionBody(options.onClick) : '',\n onClose: (Util.isFunction(options.onClose)) ? this.getFunctionBody(options.onClose) : ''\n };\n\n /* Merge the local data with user-provided data */\n if (options.data !== undefined && options.data !== null)\n localData = Object.assign(localData, options.data);\n\n /* Show the notification */\n registration.showNotification(\n title,\n {\n icon: options.icon,\n body: options.body,\n vibrate: options.vibrate,\n tag: options.tag,\n data: localData,\n requireInteraction: options.requireInteraction,\n silent: options.silent\n }\n ).then(() => {\n\n registration.getNotifications().then(notifications => {\n /* Send an empty message so the ServiceWorker knows who the client is */\n registration.active.postMessage('');\n\n /* Trigger callback */\n callback(notifications);\n });\n }).catch(function(error) {\n throw new Error(Messages.errors.sw_notification_error + error.message);\n });\n }).catch(function(error) {\n throw new Error(Messages.errors.sw_registration_error + error.message);\n });\n }\n\n /**\n * Close all notification\n */\n close() {\n // Can't do this with service workers\n }\n}\n","import AbstractAgent from './AbstractAgent';\n\n/**\n * Notification agent for modern desktop browsers:\n * Safari 6+, Firefox 22+, Chrome 22+, Opera 25+\n */\nexport default class MobileFirefoxAgent extends AbstractAgent {\n\n /**\n * Returns a boolean denoting support\n * @returns {Boolean} boolean denoting whether webkit notifications are supported\n */\n isSupported() {\n return this._win.navigator.mozNotification !== undefined;\n }\n\n /**\n * Creates a new notification\n * @param title - notification title\n * @param options - notification options array\n * @returns {Notification}\n */\n create(title, options) {\n let notification = this._win.navigator.mozNotification.createNotification(\n title,\n options.body,\n options.icon\n );\n\n notification.show();\n\n return notification;\n }\n}\n","import AbstractAgent from './AbstractAgent';\n\n/**\n * Notification agent for old Chrome versions (and some) Firefox\n */\nexport default class WebKitAgent extends AbstractAgent {\n\n /**\n * Returns a boolean denoting support\n * @returns {Boolean} boolean denoting whether webkit notifications are supported\n */\n isSupported() {\n return this._win.webkitNotifications !== undefined;\n }\n\n /**\n * Creates a new notification\n * @param title - notification title\n * @param options - notification options array\n * @returns {Notification}\n */\n create(title, options) {\n let notification = this._win.webkitNotifications.createNotification(\n options.icon,\n title,\n options.body\n );\n\n notification.show();\n\n return notification;\n }\n\n /**\n * Close a given notification\n * @param notification - notification to close\n */\n close(notification) {\n notification.cancel();\n }\n}\n","import Push from './classes/Push';\n\nmodule.exports = new Push(typeof window !== 'undefined' ? window : this);\n"]}
\ No newline at end of file
diff --git a/bower_components/push.js/bin/serviceWorker.js b/bower_components/push.js/bin/serviceWorker.js
new file mode 100644
index 0000000..1d6ee88
--- /dev/null
+++ b/bower_components/push.js/bin/serviceWorker.js
@@ -0,0 +1,75 @@
+/* eslint eqeqeq: "off", curly: "error" */
+'use strict';
+
+function isFunction(obj) {
+ return obj && {}.toString.call(obj) === '[object Function]';
+}
+
+function runFunctionString(funcStr) {
+ if (funcStr.trim().length > 0) {
+ var func = new Function(funcStr);
+ if (isFunction(func)) {
+ func();
+ }
+ }
+}
+
+self.addEventListener('message', function (event) {
+ self.client = event.source;
+});
+
+self.onnotificationclose = function (event) {
+ runFunctionString(event.notification.data.onClose);
+
+ /* Tell Push to execute close callback */
+ self.client.postMessage(JSON.stringify({
+ id: event.notification.data.id,
+ action: 'close'
+ }));
+}
+
+self.onnotificationclick = function (event) {
+ var link, origin, href;
+
+ if (typeof event.notification.data.link !== 'undefined' && event.notification.data.link !== null) {
+ origin = event.notification.data.origin;
+ link = event.notification.data.link;
+ href = origin.substring(0, origin.indexOf('/', 8)) + '/';
+
+ /* Removes prepending slash, as we don't need it */
+ if (link[0] === '/') {
+ link = (link.length > 1) ? link.substring(1, link.length) : '';
+ }
+
+ event.notification.close();
+
+ /* This looks to see if the current is already open and focuses if it is */
+ event.waitUntil(clients.matchAll({
+ type: "window"
+ }).then(function (clientList) {
+ var client, full_url;
+
+ for (var i = 0; i < clientList.length; i++) {
+ client = clientList[i];
+ full_url = href + link;
+
+ /* Covers case where full_url might be http://example.com/john and the client URL is http://example.com/john/ */
+ if (full_url[full_url.length - 1] !== '/' && client.url[client.url.length - 1] === '/') {
+ full_url += '/';
+ }
+
+ if (client.url === full_url && 'focus' in client){
+ return client.focus();
+ }
+ }
+
+ if (clients.openWindow) {
+ return clients.openWindow('/' + link);
+ }
+ }).catch(function (error) {
+ throw new Error("A ServiceWorker error occurred: " + error.message);
+ }));
+ }
+
+ runFunctionString(event.notification.data.onClick);
+}
diff --git a/bower_components/push.js/bower.json b/bower_components/push.js/bower.json
new file mode 100644
index 0000000..57d84f7
--- /dev/null
+++ b/bower_components/push.js/bower.json
@@ -0,0 +1,21 @@
+{
+ "name": "push.js",
+ "description": "A compact, cross-browser solution for the Javascript Notifications API",
+ "main": "bin/push.js",
+ "authors": [
+ "Tyler Nickerson"
+ ],
+ "license": "MIT",
+ "homepage": "https://pushjs.org",
+ "ignore": [
+ "**/.*",
+ "coverage",
+ "node_modules",
+ "bower_components",
+ "tests",
+ "src",
+ "build",
+ "*.lock",
+ "*.json"
+ ]
+}
diff --git a/bower_components/push.js/browserstack.png b/bower_components/push.js/browserstack.png
new file mode 100644
index 0000000..50c25f6
Binary files /dev/null and b/bower_components/push.js/browserstack.png differ
diff --git a/bower_components/push.js/logo.png b/bower_components/push.js/logo.png
new file mode 100644
index 0000000..fc7973b
Binary files /dev/null and b/bower_components/push.js/logo.png differ
diff --git a/index.html b/index.html
index f68739a..4758bc8 100644
--- a/index.html
+++ b/index.html
@@ -2,12 +2,25 @@
-
- ScaleDrone HTML5 JavaScript push notifications
-
+
+
+
+ Scaledrone HTML5 JavaScript push notifications
+
-
+ You can push a notification from Terminal using cURL:
+
+
+
+curl -H "Content-Type: application/json" \
+ -X POST \
+ -d '{"title":"Important!", "body":"Dogecoin is going to the moon!", "icon":"logo.png"}' \
+ https://api2.scaledrone.com/KtJ2qzn3CF3svSFe/notifications/publish
+
+
+
+
-
\ No newline at end of file
+