Commit | Line | Data |
---|---|---|
0b7949de | 1 | _kiwi.view.Favicon = Backbone.View.extend({ |
bd85cbdb VC |
2 | initialize: function () { |
3 | var that = this, | |
4 | $win = $(window); | |
5 | ||
6 | this.has_focus = true; | |
7 | this.highlight_count = 0; | |
bc1fda38 VC |
8 | // Check for html5 canvas support |
9 | this.has_canvas_support = !!window.CanvasRenderingContext2D; | |
bd85cbdb | 10 | |
bc1fda38 | 11 | // Store the original favicon |
bd85cbdb VC |
12 | this.original_favicon = $('link[rel~="icon"]')[0].href; |
13 | ||
277e821f VC |
14 | // Create our favicon canvas |
15 | this._createCanvas(); | |
16 | ||
bc1fda38 | 17 | // Reset favicon notifications when user focuses window |
bd85cbdb VC |
18 | $win.on('focus', function () { |
19 | that.has_focus = true; | |
20 | that._resetHighlights(); | |
21 | }); | |
22 | $win.on('blur', function () { | |
23 | that.has_focus = false; | |
24 | }); | |
25 | }, | |
26 | ||
27 | newHighlight: function () { | |
28 | var that = this; | |
29 | if (!this.has_focus) { | |
30 | this.highlight_count++; | |
9e2e2fc5 | 31 | if (this.has_canvas_support) { |
447dea1c VC |
32 | this._drawFavicon(function() { |
33 | that._drawBubble(that.highlight_count.toString()); | |
34 | that._refreshFavicon(that.canvas.toDataURL()); | |
9e2e2fc5 VC |
35 | }); |
36 | } | |
bd85cbdb VC |
37 | } |
38 | }, | |
39 | ||
40 | _resetHighlights: function () { | |
41 | var that = this; | |
42 | this.highlight_count = 0; | |
aa92fb91 | 43 | this._refreshFavicon(this.original_favicon); |
bd85cbdb VC |
44 | }, |
45 | ||
46 | _drawFavicon: function (callback) { | |
47 | var that = this, | |
277e821f VC |
48 | canvas = this.canvas, |
49 | context = canvas.getContext('2d'), | |
bd85cbdb VC |
50 | favicon_image = new Image(); |
51 | ||
52 | // Allow cross origin resource requests | |
53 | favicon_image.crossOrigin = 'anonymous'; | |
54 | // Trigger the load event | |
55 | favicon_image.src = this.original_favicon; | |
56 | ||
57 | favicon_image.onload = function() { | |
bc1fda38 | 58 | // Clear canvas from prevous iteration |
aa92fb91 | 59 | context.clearRect(0, 0, canvas.width, canvas.height); |
bd85cbdb | 60 | // Draw the favicon itself |
4e726fa4 | 61 | context.drawImage(favicon_image, 0, 0, canvas.width, canvas.height); |
447dea1c | 62 | callback(); |
bd85cbdb VC |
63 | }; |
64 | }, | |
65 | ||
447dea1c | 66 | _drawBubble: function (label) { |
bc1fda38 | 67 | var letter_spacing, |
7c87efb2 | 68 | bubble_width = 0, bubble_height = 0, |
277e821f VC |
69 | canvas = this.canvas, |
70 | context = test_context = canvas.getContext('2d'), | |
bd85cbdb VC |
71 | canvas_width = canvas.width, |
72 | canvas_height = canvas.height; | |
73 | ||
bc1fda38 VC |
74 | // Different letter spacing for MacOS |
75 | if (navigator.appVersion.indexOf("Mac") !== -1) { | |
76 | letter_spacing = -1.5; | |
77 | } | |
78 | else { | |
79 | letter_spacing = -1; | |
80 | } | |
81 | ||
bd85cbdb | 82 | // Setup a test canvas to get text width |
277e821f VC |
83 | test_context.font = context.font = 'bold 10px Arial'; |
84 | test_context.textAlign = 'right'; | |
85 | this._renderText(test_context, label, 0, 0, letter_spacing); | |
bd85cbdb | 86 | |
7c87efb2 | 87 | // Calculate bubble width based on letter spacing and padding |
277e821f | 88 | bubble_width = test_context.measureText(label).width + letter_spacing * (label.length - 1) + 2; |
7c87efb2 VC |
89 | // Canvas does not have any way of measuring text height, so we just do it manually and add 1px top/bottom padding |
90 | bubble_height = 9; | |
bd85cbdb | 91 | |
7c87efb2 VC |
92 | // Set bubble coordinates |
93 | bubbleX = canvas_width - bubble_width; | |
94 | bubbleY = canvas_height - bubble_height; | |
bd85cbdb VC |
95 | |
96 | // Draw bubble background | |
97 | context.fillStyle = 'red'; | |
7c87efb2 | 98 | context.fillRect(bubbleX, bubbleY, bubble_width, bubble_height); |
bd85cbdb VC |
99 | |
100 | // Draw the text | |
101 | context.fillStyle = 'white'; | |
9e2e2fc5 | 102 | this._renderText(context, label, canvas_width - 1, canvas_height - 1, letter_spacing); |
bd85cbdb VC |
103 | }, |
104 | ||
105 | _refreshFavicon: function (url) { | |
106 | $('link[rel~="icon"]').remove(); | |
107 | $('<link rel="shortcut icon" href="' + url + '">').appendTo($('head')); | |
108 | }, | |
109 | ||
277e821f VC |
110 | _createCanvas: function () { |
111 | var canvas = document.createElement('canvas'); | |
aa92fb91 VC |
112 | canvas.width = 16; |
113 | canvas.height = 16; | |
277e821f VC |
114 | |
115 | this.canvas = canvas; | |
9e2e2fc5 VC |
116 | }, |
117 | ||
118 | _renderText: function (context, text, x, y, letter_spacing) { | |
119 | // A hacky solution for letter-spacing, but works well with small favicon text | |
120 | // Modified from http://jsfiddle.net/davidhong/hKbJ4/ | |
121 | var current, | |
122 | characters = text.split('').reverse(), | |
123 | index = 0, | |
124 | currentPosition = x; | |
125 | ||
126 | while (index < text.length) { | |
127 | current = characters[index++]; | |
128 | context.fillText(current, currentPosition, y); | |
129 | currentPosition += (-1 * (context.measureText(current).width + letter_spacing)); | |
130 | } | |
131 | ||
132 | return context; | |
bd85cbdb | 133 | } |
c97214a7 | 134 | }); |