From af1c14939e443cf37ca981756bdbbc804d118c40 Mon Sep 17 00:00:00 2001 From: Chris Hunt <c@chrishunt.co> Date: Fri, 7 Jun 2013 17:15:49 -0700 Subject: [PATCH] Add 'dynamic favicon' setting --- app/assets/images/default-favicon.ico | Bin 0 -> 1342 bytes app/assets/javascripts/discourse.js | 8 + .../external/jquery.faviconNotify.js | 224 ++++++++++++++++++ app/models/site_setting.rb | 3 +- config/locales/server.en.yml | 1 + 5 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 app/assets/images/default-favicon.ico create mode 100644 app/assets/javascripts/external/jquery.faviconNotify.js diff --git a/app/assets/images/default-favicon.ico b/app/assets/images/default-favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..d20ae8eeded413f3de968472303a4dbc2a63409c GIT binary patch literal 1342 zcmd^8ZBJ5R7=98VA)LGk2*<-opa2sX2uMIXK_&%QCI}&FTf`hq#7eUjAtPGNm+C_s zt@_lL-1=5Ou<JJ4D)kq1vy1!vd7k^_Fm?(*m5SkCU=Ar`dd8RofC-jgb}+`^`<L8q z_omTkY^b%`4V_+J)@rqzdcA&4r_;?TmCCAGt$qZ3n&bHS(b3VW%jM$#Ji}x%b)gr5 z34pHk0a+jdSOBtL1)@@^v>*sV)9?3-&<_atZy+oH357zj(AU?u1lbE*1MWkg!~LcH z{(iED2zX<Ae0)3rnel?~9PB2Lbvm7Ue!q7=5D5Gb#Y^9d#o|X%6hFt~@!d!yvN}9G zyok6MkVIZXqtTdoo^L>(qI|2>S_U`at`h9!TrMY1CX<{>rAmiFp$g7;2s_nAHLiI) zo_W->0hwyGjg5^x4hDlB$VUiy>P#lH5sSqd0|Nt8Ukp9YTP&6*kmG<c8ojt}Hk<Dq zNj@~Judkm^Bof;WhhqjNuD7>0igT&{1Y?p!DwX;Q`SM}-v8+%i)M%z~o&$WGAnbw9 zU^aR5bU&R=(+p3fdcD4czHKAm24Q5xz6-u6ld)N^*V~0Zb7C?awOVZz>;Z5cciwhg zaJ}g1>7g0;(3f|mQt4T<*`(DyLY+>>5Do`Aa8SRq*v~Bb7v?zb8+5BgBFRlm1U?|Y z-fp+gVt$g}hqPL)P^D6-LB9o1k7}6R!K@}HCtGs4{3UEk^t6io>8`A-v~gcyXXkYu zd~13-^B(>JU<K}A2dVx8G8hbJ@Ev`@duSF$K*SEwemwR0e6O&-P1tC6Y4?^9M{oG+ zYaALHQXu{<zK2in%>sTP4N&YApbFd_92}go*=(mFA4|b-IBU1tx6qemnM_7+q}`@| Ng)w*j*rfm1_!9uNhV}pe literal 0 HcmV?d00001 diff --git a/app/assets/javascripts/discourse.js b/app/assets/javascripts/discourse.js index 954d8ad1b..33e1627ce 100644 --- a/app/assets/javascripts/discourse.js +++ b/app/assets/javascripts/discourse.js @@ -85,6 +85,14 @@ Discourse = Ember.Application.createWithMixins({ }, 200); }.observes('title', 'hasFocus', 'notifyCount'), + faviconChanged: function() { + if(Discourse.SiteSettings.dynamic_favicon) { + $.faviconNotify( + Discourse.SiteSettings.favicon_url, this.get('notifyCount') + ); + } + }.observes('notifyCount'), + // The classes of buttons to show on a post postButtons: function() { return Discourse.SiteSettings.post_menu.split("|").map(function(i) { diff --git a/app/assets/javascripts/external/jquery.faviconNotify.js b/app/assets/javascripts/external/jquery.faviconNotify.js new file mode 100644 index 000000000..2b071e30e --- /dev/null +++ b/app/assets/javascripts/external/jquery.faviconNotify.js @@ -0,0 +1,224 @@ +/** + * jQuery Favicon Notify + * + * Updates the favicon to notify the user of changes. In the original tests I + * had an embedded font collection to allow any charachers - I decided that the + * ~130Kb and added complexity was overkill. As such it now uses a manual glyph + * set meaning that only numerical notifications are possible. + * + * Dual licensed under the MIT and GPL licenses: + * + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * @author David King + * @copyright Copyright (c) 2011 + + * @url oodavid.com + */ +(function($){ + var canvas; + var bg = '#000000'; + var fg = '#FFFFFF'; + var pos = 'br'; + $.faviconNotify = function(icon, num, myPos, myBg, myFg){ + // Default the positions + myPos = myPos || pos; + myFg = myFg || fg; + myBg = myBg || bg; + // Create a canvas if we need one + canvas = canvas || $('<canvas />')[0]; + if(canvas.getContext){ + // Load the icon + $('<img />').load(function(e){ + // Load the icon into the canvas + canvas.height = canvas.width = 16; + var ctx = canvas.getContext('2d'); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.drawImage(this, 0, 0); + // We gots num? + if(num !== undefined){ + num = parseFloat(num, 10); + // Convert the num into a glyphs array + var myGlyphs = []; + if(num > 99){ + myGlyphs.push(glyphs['LOTS']); + } else { + num = num.toString().split(''); + $.each(num, function(k,v){ + myGlyphs.push(glyphs[v]); + }); + } + // Merge the glyphs together + var combined = []; + var glyphHeight = myGlyphs[0].length; + $.each(myGlyphs, function(k,v){ + for(y=0; y<glyphHeight; y++){ + // First pass? + if(combined[y] === undefined) { + combined[y] = v[y]; + } else { + // Merge the glyph parts, careful of the boundaries + var l = combined[y].length; + if(combined[y][(l-1)] == ' '){ + combined[y] = combined[y].substring(0, (l-1)) + v[y]; + } else { + combined[y] += v[y].substring(1); + } + } + } + }); + // Figure out our starting position + var glyphWidth = combined[0].length; + var x = (myPos.indexOf('l') != -1) ? 0 : (16 - glyphWidth); + var y = (myPos.indexOf('t') != -1) ? 0 : (16 - glyphHeight); + // Draw them pixels! + for(dX=0; dX<glyphWidth; dX++){ + for(dY=0; dY<glyphHeight; dY++){ + var pixel = combined[dY][dX]; + if(pixel != ' '){ + ctx.fillStyle = (pixel == '@') ? myFg : myBg; + ctx.fillRect((x+dX), (y+dY), 1, 1); + } + } + } + } + // Update the favicon + $('link[rel$=icon]').remove(); + $('head').append($('<link rel="shortcut icon" type="image/x-icon"/>').attr('href', canvas.toDataURL('image/png'))); + }).attr('src', icon) + } + }; + var glyphs = { + '0': [ + ' --- ', + ' -@@@- ', + '-@---@-', + '-@- -@-', + '-@- -@-', + '-@- -@-', + '-@---@-', + ' -@@@- ', + ' --- ' ], + '1': [ + ' - ', + ' -@- ', + '-@@- ', + ' -@- ', + ' -@- ', + ' -@- ', + ' -@- ', + '-@@@-', + ' --- ' ], + '2': [ + ' --- ', + ' -@@@- ', + '-@---@-', + ' - --@-', + ' -@@- ', + ' -@-- ', + '-@---- ', + '-@@@@@-', + ' ----- ' ], + '3': [ + ' --- ', + ' -@@@- ', + '-@---@-', + ' - --@-', + ' -@@- ', + ' - --@-', + '-@---@-', + ' -@@@- ', + ' --- ' ], + '4': [ + ' -- ', + ' -@@-', + ' -@-@-', + ' -@--@-', + '-@---@-', + '-@@@@@-', + ' ----@-', + ' -@-', + ' - ' ], + '5': [ + ' ----- ', + '-@@@@@-', + '-@---- ', + '-@--- ', + '-@@@@- ', + ' ----@-', + '-@---@-', + ' -@@@- ', + ' --- ' ], + '6': [ + ' --- ', + ' -@@@- ', + '-@---@-', + '-@---- ', + '-@@@@- ', + '-@---@-', + '-@---@-', + ' -@@@- ', + ' --- ' ], + '7': [ + ' ----- ', + '-@@@@@-', + ' ----@-', + ' -@- ', + ' -@- ', + ' -@- ', + ' -@- ', + ' -@- ', + ' - ' ], + '8': [ + ' --- ', + ' -@@@- ', + '-@---@-', + '-@---@-', + ' -@@@- ', + '-@---@-', + '-@---@-', + ' -@@@- ', + ' --- ' ], + '9': [ + ' --- ', + ' -@@@- ', + '-@---@-', + '-@---@-', + ' -@@@@-', + ' ----@-', + '-@---@-', + ' -@@@- ', + ' --- ' ], + '!': [ + ' - ', + '-@-', + '-@-', + '-@-', + '-@-', + '-@-', + ' - ', + '-@-', + ' - ' ], + '.': [ + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' - ', + '-@-', + ' - ' ], + 'LOTS': [ + ' - -- --- -- ', + '-@- -@@-@@@--@@-', + '-@--@--@-@--@- ', + '-@--@--@-@--@- ', + '-@--@--@-@- -@- ', + '-@--@--@-@- -@-', + '-@--@--@-@----@-', + '-@@@-@@--@-@@@- ', + ' --- -- - --- ' + ] + }; +})(jQuery); diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb index e5b99a922..93fee5b34 100644 --- a/app/models/site_setting.rb +++ b/app/models/site_setting.rb @@ -79,7 +79,8 @@ class SiteSetting < ActiveRecord::Base setting(:invite_expiry_days, 14) setting(:active_user_rate_limit_secs, 60) setting(:previous_visit_timeout_hours, 1) - setting(:favicon_url, '/assets/default-favicon.png') + client_setting(:favicon_url, '/assets/default-favicon.ico') + client_setting(:dynamic_favicon, false) setting(:apple_touch_icon_url, '/assets/default-apple-touch-icon.png') setting(:ninja_edit_window, 5.minutes.to_i) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 47f225f6e..9e689872a 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -460,6 +460,7 @@ en: logo_url: "The logo for your site eg: http://example.com/logo.png" logo_small_url: "The small logo for your site used when scrolling down on topics eg: http://example.com/logo-small.png" favicon_url: "A favicon for your site, see http://en.wikipedia.org/wiki/Favicon" + dynamic_favicon: "Show incoming message notifications on favicon" apple_touch_icon_url: "Icon used for Apple touch devices. Recommended size is 144px by 144px." notification_email: "The return email address used when sending system emails such as notifying users of lost passwords, new accounts etc"