Add favicon notifications
authorVlad Chernushevich <mailbox@happytodesign.com>
Sun, 23 Jun 2013 22:15:50 +0000 (00:15 +0200)
committerVlad Chernushevich <mailbox@happytodesign.com>
Sun, 23 Jun 2013 22:15:50 +0000 (00:15 +0200)
client/assets/img/favicon.png [new file with mode: 0644]
client/assets/src/index.html.tmpl
client/assets/src/views/application.js
client/assets/src/views/panel.js

diff --git a/client/assets/img/favicon.png b/client/assets/img/favicon.png
new file mode 100644 (file)
index 0000000..4cbbd8c
Binary files /dev/null and b/client/assets/img/favicon.png differ
index 90ad9276566f5fb5a421281bfd96781662d95139..cd02cc13606ecc4ec2b559fe9118ef8af41140fe 100644 (file)
@@ -1,12 +1,14 @@
 <!DOCTYPE html>\r
 <html>\r
 <head>\r
-<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> \r
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>\r
 <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">\r
 <base target="_blank">\r
 \r
 <title> KiwiIRC </title>\r
 \r
+<link rel="shortcut icon" href="<%base_path%>/assets/img/favicon.png">\r
+\r
 <link rel="stylesheet" type="text/css" href="<%base_path%>/assets/css/style.css" />\r
 <link rel="stylesheet" type="text/css" href="<%base_path%>/assets/css/font-awesome.min.css" />\r
 <!--[if IE 7]>\r
index bb72eb72b312719ae0e45f938aa447e823fa628e..bc3e8c15794741cbd058fee1eef9948c26304f6d 100644 (file)
@@ -27,6 +27,7 @@ _kiwi.view.Application = Backbone.View.extend({
             }
         };
 
+        this.notificationFavicon();
         this.initSound();
     },
 
@@ -226,6 +227,174 @@ _kiwi.view.Application = Backbone.View.extend({
     },
 
 
+    // This will have to be rewiritten to store highlights in kiwi, and not here
+    notificationFavicon: function () {
+        var base_path = this.model.get('base_path');
+
+        this.favicon = new (function () {
+            var that = this,
+                hasFocus = true,
+                highlightCount = 0,
+                hasConvasSupport = !!window.CanvasRenderingContext2D,
+                originalTitle = document.title,
+                originalFaviconLink = $('link[rel~="icon"]')[0].href,
+               font = 'bold 10px Arial',
+               letterSpacing = -1.5;
+
+            var ua = (function () {
+                var agent = navigator.userAgent.toLowerCase();
+                return function (browser) {
+                    return agent.indexOf(browser) !== -1;
+                };
+            })();
+
+            var browser = {
+                ie: ua('msie'),
+                chrome: ua('chrome'),
+                webkit: ua('chrome') || ua('safari'),
+                safari: ua('safari') && !ua('chrome'),
+                mozilla: ua('mozilla') && !ua('chrome') && !ua('safari')
+            };
+
+            this.newHighlight = function () {
+                if (!hasFocus) {
+                    highlightCount++;
+                    that._updateFavicon(highlightCount);
+                }
+            };
+
+            this.resetHighlights = function () {
+                highlightCount = 0;
+                that._refreshFavicon(originalFaviconLink);
+                that._setTitle();
+            }
+
+            this._updateFavicon = function (text) {
+                text = text.toString();
+                if (!hasConvasSupport || browser.ie || browser.safari) {
+                    that._setTitle(text);
+                }
+                else {
+                    that._drawFavicon(text);
+                }
+            };
+
+            this._drawFavicon = function (text) {
+                var context = that._createCanvas().getContext('2d'),
+                    faviconImage = new Image();
+
+                // Allow cross origin resource requests
+                faviconImage.crossOrigin = 'anonymous';
+                // Trigger the load event
+                faviconImage.src = originalFaviconLink;
+
+                // Wait for the favicon image to load
+                faviconImage.onload = function () {
+                    // Draw the favicon itself
+                    context.drawImage(faviconImage, 0, 0, faviconImage.width, faviconImage.height);
+                    // Add highlight bubble
+                    that._drawBubble(context, text);
+                    //Update
+                    that._refreshFavicon(canvas.toDataURL());
+                };
+            };
+
+            this._drawBubble = function (context, text) {
+               var textWidth = 0, textHidth = 0,
+                       test = context,
+                       canvasWidth = context.canvas.width,
+                       canvasHeight = context.canvas.height;
+
+               // A hacky solution for letter-spacing, but works well with small favicon text
+                   CanvasRenderingContext2D.prototype.renderText = function (text, x, y, letterSpacing) {
+                                       if (!text || typeof text !== 'string' || text.length === 0) {
+                                           return;
+                                       }
+                                       if (typeof letterSpacing === 'undefined') {
+                                           letterSpacing = 0;
+                                       }
+                                       // letterSpacing of 0 means normal letter-spacing
+                                       var characters = String.prototype.split.call(text, ''),
+                                           index = 0,
+                                           current,
+                                           currentPosition = x,
+                                           align = 1;
+
+                                       if (this.textAlign === 'right') {
+                                           characters = characters.reverse();
+                                           align = -1;
+                                       } else if (this.textAlign === 'center') {
+                                           var totalWidth = 0;
+                                           for (var i = 0; i < characters.length; i++) {
+                                               totalWidth += (this.measureText(characters[i]).width + letterSpacing);
+                                           }
+                                           currentPosition = x - (totalWidth / 2);
+                                       }
+
+                                       while (index < text.length) {
+                                           current = characters[index++];
+                                           this.fillText(current, currentPosition, y);
+                                           currentPosition += (align * (this.measureText(current).width + letterSpacing));
+                                       }
+                   }
+
+                // Setup a test canvas to get text width
+               test.font = context.font = 'bold 10px Arial';
+                       test.textAlign = 'right';
+                       test.renderText(text, 0, 0, letterSpacing);
+
+                // Calculate text width based on letter spacing and padding
+                textWidth = test.measureText(text).width + letterSpacing * (text.length - 1) + 2;
+                       textHeight = 8;
+
+                       // Set bubble parameters
+                       bubbleX = canvasWidth - textWidth;
+                       bubbleY = canvasHeight - textHeight;
+
+                       // Draw bubble background
+                       context.fillStyle = 'red';
+                       context.fillRect(bubbleX, bubbleY, textWidth, textHeight);
+
+                // Draw the text
+                       context.fillStyle = 'white';
+                       context.renderText(text, canvasWidth - 1, canvasHeight - 1, letterSpacing);
+            };
+
+            this._refreshFavicon = function (url) {
+                // Remove existing favicon since Firefox doesn't update fivacons on href change
+                $('link[rel~="icon"]').remove();
+                // Add new favicon
+                $('<link rel="shortcut icon" href="' + url + '">').appendTo($('head'));
+            };
+
+            this._createCanvas = function () {
+                               canvas = document.createElement('canvas');
+                               canvas.width = 16;
+                               canvas.height = 16;
+
+                               return canvas;
+                       };
+
+            this._setTitle = function (text) {
+                if (text) {
+                    document.title = '(' + text + ') ' + originalTitle;
+                }
+                else {
+                    document.title = originalTitle;
+                }
+            };
+
+            $(window).on('focus', function () {
+                hasFocus = true;
+                that.resetHighlights();
+            });
+            $(window).on('blur', function () {
+                hasFocus = false;
+            });
+        })();
+    },
+
+
     barsHide: function (instant) {
         var that = this;
 
index 7d94db8295f978ec3cc350df1668db3b352cc7ac..a250d27372fab74959bcadcc5e36eb61e1002e8a 100644 (file)
@@ -134,6 +134,7 @@ _kiwi.view.Panel = Backbone.View.extend({
 
         } else if (is_highlight) {
             _kiwi.app.view.alertWindow('* People are talking!');
+            _kiwi.app.view.favicon.newHighlight();
             _kiwi.app.view.playSound('highlight');
             this.alert('highlight');