From 18a7fc58e5db4d5117654e827b1150f815051914 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Fri, 10 May 2013 23:09:13 +0200 Subject: [PATCH] Added init version of desktop notifications. --- .../_application_controller.js.coffee | 4 + .../app/controllers/chat_widget.js.coffee | 6 + .../app/controllers/notify.js.coffee | 11 + .../app/lib/base/desktop-notify.js | 201 ++++++++++++++++++ 4 files changed, 222 insertions(+) create mode 100644 app/assets/javascripts/app/lib/base/desktop-notify.js diff --git a/app/assets/javascripts/app/controllers/_application_controller.js.coffee b/app/assets/javascripts/app/controllers/_application_controller.js.coffee index e8a824184..540ab2d2d 100644 --- a/app/assets/javascripts/app/controllers/_application_controller.js.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller.js.coffee @@ -15,6 +15,10 @@ class App.Controller extends Spine.Controller notify: (data) -> App.Event.trigger 'notify', data + # add @notifyDesktop methode to create desktop notification + notifyDesktop: (data) -> + App.Event.trigger 'notifyDesktop', data + # add @navupdate methode to update navigation navupdate: (url) -> App.Event.trigger 'navupdate', url diff --git a/app/assets/javascripts/app/controllers/chat_widget.js.coffee b/app/assets/javascripts/app/controllers/chat_widget.js.coffee index a55596606..463a53a80 100644 --- a/app/assets/javascripts/app/controllers/chat_widget.js.coffee +++ b/app/assets/javascripts/app/controllers/chat_widget.js.coffee @@ -55,6 +55,12 @@ class App.ChatWidget extends App.Controller if length > 10 @messageLog = @messageLog.slice( length - max, length ) @render() + + if !e.spool + @notifyDesktop( + msg: 'Chat' + body: "#{e.nick}: #{e.message}" + ) ) App.Event.bind( diff --git a/app/assets/javascripts/app/controllers/notify.js.coffee b/app/assets/javascripts/app/controllers/notify.js.coffee index 2cbc3b786..ea5ef516c 100644 --- a/app/assets/javascripts/app/controllers/notify.js.coffee +++ b/app/assets/javascripts/app/controllers/notify.js.coffee @@ -14,6 +14,17 @@ class App.Notify extends Spine.Controller @log 'notify:removeall', @ @destroyAll() + App.Event.bind 'notifyDesktop', (data) => + if !data['icon'] + data['icon'] = 'unknown' + notify.createNotification( data.msg, data ) + + # request desktop notification after login + App.Event.bind 'auth', (data) -> + if !_.isEmpty(data) + notify.config( pageVisibility: false ) + notify.requestPermission() + render: (data) -> # notify = App.view('notify')(data: data) # @append( notify ) diff --git a/app/assets/javascripts/app/lib/base/desktop-notify.js b/app/assets/javascripts/app/lib/base/desktop-notify.js new file mode 100644 index 000000000..e746531da --- /dev/null +++ b/app/assets/javascripts/app/lib/base/desktop-notify.js @@ -0,0 +1,201 @@ +/** + * Copyright 2012 Tsvetan Tsvetkov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Author: Tsvetan Tsvetkov (tsekach@gmail.com) + */ +(function (win) { + /* + Safari native methods required for Notifications do NOT run in strict mode. + */ + //"use strict"; + var PERMISSION_DEFAULT = "default", + PERMISSION_GRANTED = "granted", + PERMISSION_DENIED = "denied", + PERMISSION = [PERMISSION_GRANTED, PERMISSION_DEFAULT, PERMISSION_DENIED], + defaultSetting = { + pageVisibility: true, + autoClose: 5000 + }, + empty = {}, + emptyString = "", + isSupported = (function () { + var isSupported = false; + /* + * Use try {} catch() {} because the check for IE may throws an exception + * if the code is run on browser that is not Safar/Chrome/IE or + * Firefox with html5notifications plugin. + * + * Also, we canNOT detect if msIsSiteMode method exists, as it is + * a method of host object. In IE check for existing method of host + * object returns undefined. So, we try to run it - if it runs + * successfully - then it is IE9+, if not - an exceptions is thrown. + */ + try { + isSupported = !!(/* Safari, Chrome */win.Notification || /* Chrome & ff-html5notifications plugin */win.webkitNotifications || /* Firefox Mobile */navigator.mozNotification || /* IE9+ */(win.external && win.external.msIsSiteMode() !== undefined)); + } catch (e) {} + return isSupported; + }()), + ieVerification = Math.floor((Math.random() * 10) + 1), + isFunction = function (value) { return (value && (value).constructor === Function); }, + isString = function (value) {return (value && (value).constructor === String); }, + isObject = function (value) {return (value && (value).constructor === Object); }, + /** + * Dojo Mixin + */ + mixin = function (target, source) { + var name, s; + for (name in source) { + s = source[name]; + if (!(name in target) || (target[name] !== s && (!(name in empty) || empty[name] !== s))) { + target[name] = s; + } + } + return target; // Object + }, + noop = function () {}, + settings = defaultSetting; + function getNotification(title, options) { + var notification; + if (win.Notification) { /* Safari 6, Chrome (23+) */ + notification = new win.Notification(title, { + /* The notification's icon - For Chrome in Windows, Linux & Chrome OS */ + icon: isString(options.icon) ? options.icon : options.icon.x32, + /* The notification’s subtitle. */ + body: options.body || emptyString, + /* + The notification’s unique identifier. + This prevents duplicate entries from appearing if the user has multiple instances of your website open at once. + */ + tag: options.tag || emptyString + }); + } else if (win.webkitNotifications) { /* FF with html5Notifications plugin installed */ + notification = win.webkitNotifications.createNotification(options.icon, title, options.body); + notification.show(); + } else if (navigator.mozNotification) { /* Firefox Mobile */ + notification = navigator.mozNotification.createNotification(title, options.body, options.icon); + notification.show(); + } else if (win.external && win.external.msIsSiteMode()) { /* IE9+ */ + //Clear any previous notifications + win.external.msSiteModeClearIconOverlay(); + win.external.msSiteModeSetIconOverlay((isString(options.icon) ? options.icon : options.icon.x16), title); + win.external.msSiteModeActivate(); + notification = { + "ieVerification": ieVerification + 1 + }; + } + return notification; + } + function getWrapper(notification) { + return { + close: function () { + if (notification) { + if (notification.close) { + //http://code.google.com/p/ff-html5notifications/issues/detail?id=58 + notification.close(); + } else if (win.external && win.external.msIsSiteMode()) { + if (notification.ieVerification === ieVerification) { + win.external.msSiteModeClearIconOverlay(); + } + } + } + } + }; + } + function requestPermission(callback) { + if (!isSupported) { return; } + var callbackFunction = isFunction(callback) ? callback : noop; + if (win.webkitNotifications && win.webkitNotifications.checkPermission) { + /* + * Chrome 23 supports win.Notification.requestPermission, but it + * breaks the browsers, so use the old-webkit-prefixed + * win.webkitNotifications.checkPermission instead. + * + * Firefox with html5notifications plugin supports this method + * for requesting permissions. + */ + win.webkitNotifications.requestPermission(callbackFunction); + } else if (win.Notification && win.Notification.requestPermission) { + win.Notification.requestPermission(callbackFunction); + } + } + function permissionLevel() { + var permission; + if (!isSupported) { return; } + if (win.Notification && win.Notification.permissionLevel) { + //Safari 6 + permission = win.Notification.permissionLevel(); + } else if (win.webkitNotifications && win.webkitNotifications.checkPermission) { + //Chrome & Firefox with html5-notifications plugin installed + permission = PERMISSION[win.webkitNotifications.checkPermission()]; + } else if (navigator.mozNotification) { + //Firefox Mobile + permission = PERMISSION_GRANTED; + } else if (win.external && win.external.msIsSiteMode()) { /* keep last */ + //IE9+ + permission = PERMISSION_GRANTED; + } + return permission; + } + /** + * + */ + function config(params) { + if (params && isObject(params)) { + mixin(settings, params); + } + return settings; + } + function isDocumentHidden() { + return settings.pageVisibility ? (document.hidden || document.msHidden || document.mozHidden || document.webkitHidden) : true; + } + function createNotification(title, options) { + var notification, + notificationWrapper; + /* + Return undefined if notifications are not supported. + + Return undefined if no permissions for displaying notifications. + + Title and icons are required. Return undefined if not set. + */ + if (isSupported && isDocumentHidden() && isString(title) && (options && (isString(options.icon) || isObject(options.icon))) && (permissionLevel() === PERMISSION_GRANTED)) { + notification = getNotification(title, options); + } + notificationWrapper = getWrapper(notification); + //Auto-close notification + if (settings.autoClose && notification && !notification.ieVerification && notification.addEventListener) { + notification.addEventListener("show", function () { + var notification = notificationWrapper; + win.setTimeout(function () { + notification.close(); + }, settings.autoClose); + }); + } + return notificationWrapper; + } + win.notify = { + PERMISSION_DEFAULT: PERMISSION_DEFAULT, + PERMISSION_GRANTED: PERMISSION_GRANTED, + PERMISSION_DENIED: PERMISSION_DENIED, + isSupported: isSupported, + config: config, + createNotification: createNotification, + permissionLevel: permissionLevel, + requestPermission: requestPermission + }; + if (isFunction(Object.freeze)) { + Object.freeze(win.notify); + } +}(window));