Added Tinycon (Unread message tab's icon)

This commit is contained in:
RainLoop Team 2015-03-04 04:30:37 +04:00
parent f1b3271b4d
commit 47bb848905
15 changed files with 426 additions and 17 deletions

View file

@ -172,7 +172,7 @@
/**
* @param {string} sTitle
*/
AbstractApp.prototype.setTitle = function (sTitle)
AbstractApp.prototype.setWindowTitle = function (sTitle)
{
sTitle = ((Utils.isNormal(sTitle) && 0 < sTitle.length) ? sTitle + ' - ' : '') +
Settings.settingsGet('Title') || '';

View file

@ -9,6 +9,7 @@
$ = require('$'),
moment = require('moment'),
SimplePace = require('SimplePace'),
Tinycon = require('Tinycon'),
Enums = require('Common/Enums'),
Globals = require('Common/Globals'),
@ -1342,7 +1343,7 @@
{
Globals.$html.addClass('rl-user-auth');
this.setTitle(Translator.i18n('TITLES/LOADING'));
this.setWindowTitle(Translator.i18n('TITLES/LOADING'));
//require.ensure([], function() { // require code splitting
@ -1479,6 +1480,17 @@
_.defer(function () {
self.initVerticalLayoutResizer(Enums.ClientSideKeyName.FolderListSize);
});
if (Tinycon)
{
Tinycon.setOptions({
fallback: false
});
Events.sub('mailbox.inbox-unread-count', function (iCount) {
Tinycon.setBubble(0 < iCount ? (99 < iCount ? 99 : iCount) : 0);
});
}
}
}
else

View file

@ -24,7 +24,7 @@
LoginAdminScreen.prototype.onShow = function ()
{
require('App/Admin').setTitle('');
require('App/Admin').setWindowTitle('');
};
module.exports = LoginAdminScreen;

View file

@ -75,7 +75,7 @@
SettingsAdminScreen.prototype.onShow = function ()
{
require('App/Admin').setTitle('');
require('App/Admin').setWindowTitle('');
};
module.exports = SettingsAdminScreen;

View file

@ -24,7 +24,7 @@
AboutUserScreen.prototype.onShow = function ()
{
require('App/User').setTitle('RainLoop');
require('App/User').setWindowTitle('RainLoop');
};
module.exports = AboutUserScreen;

View file

@ -24,7 +24,7 @@
LoginUserScreen.prototype.onShow = function ()
{
require('App/User').setTitle('');
require('App/User').setWindowTitle('');
};
module.exports = LoginUserScreen;

View file

@ -45,21 +45,21 @@
*/
MailBoxUserScreen.prototype.oLastRoute = {};
MailBoxUserScreen.prototype.setNewTitle = function ()
MailBoxUserScreen.prototype.updateWindowTitle = function ()
{
var
sEmail = AccountStore.email(),
nFoldersInboxUnreadCount = FolderStore.foldersInboxUnreadCount()
;
require('App/User').setTitle(('' === sEmail ? '' :
require('App/User').setWindowTitle(('' === sEmail ? '' : '' +
(0 < nFoldersInboxUnreadCount ? '(' + nFoldersInboxUnreadCount + ') ' : ' ') +
sEmail + ' - ') + Translator.i18n('TITLES/MAILBOX'));
};
MailBoxUserScreen.prototype.onShow = function ()
{
this.setNewTitle();
this.updateWindowTitle();
Globals.keyScope(Enums.KeyState.MessageList);
};
@ -112,7 +112,7 @@
SettingsStore.layout.valueHasMutated();
}, 50);
Events.sub('mailbox.inbox-unread-count', function (iCount) {
Events.sub('mailbox.inbox-unread-count', _.bind(function (iCount) {
FolderStore.foldersInboxUnreadCount(iCount);
@ -124,12 +124,10 @@
oItem.count(iCount);
}
});
});
FolderStore.foldersInboxUnreadCount.subscribe(function () {
this.setNewTitle();
}, this);
this.updateWindowTitle();
}, this));
};
MailBoxUserScreen.prototype.onBuild = function ()

View file

@ -122,7 +122,7 @@
SettingsUserScreen.prototype.setSettingsTitle = function ()
{
require('App/User').setTitle(this.sSettingsTitle);
require('App/User').setWindowTitle(this.sSettingsTitle);
};
module.exports = SettingsUserScreen;

5
dev/bootstrap.js vendored
View file

@ -54,7 +54,7 @@
window['__APP_BOOT'] = function (fCall) {
$(function () {
$(_.delay(function () {
if (!$('#rl-content').is(':visible'))
{
@ -82,7 +82,8 @@
}
window['__APP_BOOT'] = null;
});
}, 10));
};
};

View file

@ -157,6 +157,7 @@ cfg.paths.js = {
'vendors/queue/queue.min.js',
'vendors/inputosaurus/inputosaurus.min.js',
'vendors/moment/min/moment.min.js ',
'vendors/tinycon/tinycon.min.js ',
'vendors/routes/signals.min.js',
'vendors/routes/hasher.min.js',
'vendors/routes/crossroads.min.js',

19
vendors/tinycon/LICENSE vendored Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2013 Tom Moor
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

90
vendors/tinycon/README.md vendored Normal file
View file

@ -0,0 +1,90 @@
# Tinycon
A small library for manipulating the favicon, in particular adding alert bubbles and changing images. Tinycon gracefully falls back to a number in title approach for browers that don't support canvas or dynamic favicons.
<img src="https://github.com/tommoor/tinycon/blob/master/examples/screenshot.png?raw=true" />
<a href="http://tommoor.github.com/tinycon/">See the Live Demo here.</a>
## Documentation
Tinycon adds a single object to the global namespace and does not require initialisation.
### Basic Usage
```javascript
Tinycon.setBubble(6);
```
### Options
Tinycon can take a range of options to customise the look
* width: the width of the alert bubble
* height: the height of the alert bubble
* font: a css string to use for the fontface (recommended to leave this)
* colour: the foreground font colour
* background: the alert bubble background colour
* fallback: should we fallback to a number in brackets for browsers that don't support canvas/dynamic favicons? Boolean, or use the string 'force' to ensure a title update even in supported browsers.
* abbreviate: should tinycon shrink large numbers such as 1000 to an abbreviated version (1k). Boolean, defaults to true
```javascript
Tinycon.setOptions({
width: 7,
height: 9,
font: '10px arial',
colour: '#ffffff',
background: '#549A2F',
fallback: true
});
```
### AMD support
Tinycon can also be used as an asynchronous module.
```javascript
require([
'tinycon.js'
], function (T) {
T.setOptions({
width: 7,
height: 9,
font: '10px arial',
colour: '#ffffff',
background: '#549A2F',
fallback: true
});
T.setBubble(7);
});
```
## Browser Support
Tinycon has been tested to work completely in the following browsers. Older versions may be supported, but haven't been tested:
* Chrome 15+
* Firefox 9+
* Opera 11+
Currently the library degrades to title update:
* Internet Explorer 9
* Safari 5
## License / Credits
Tinycon is released under the MIT license. It is simple and easy to understand and places almost no restrictions on what you can do with Tinycon.
[More Information](http://en.wikipedia.org/wiki/MIT_License)
Tinycon was inspired by [Notificon](https://github.com/makeable/Notificon)
## Download
Releases are available for download from
[GitHub](http://github.com/tommoor/tinycon/downloads).

279
vendors/tinycon/tinycon.js vendored Normal file
View file

@ -0,0 +1,279 @@
/*!
* Tinycon - A small library for manipulating the Favicon
* Tom Moor, http://tommoor.com
* Copyright (c) 2012 Tom Moor
* @license MIT Licensed
* @version 0.6.3
*/
(function(){
var Tinycon = {};
var currentFavicon = null;
var originalFavicon = null;
var faviconImage = null;
var canvas = null;
var options = {};
var r = window.devicePixelRatio || 1;
var size = 16 * r;
var defaults = {
width: 7,
height: 9,
font: 10 * r + 'px arial',
colour: '#ffffff',
background: '#F03D25',
fallback: true,
crossOrigin: true,
abbreviate: true
};
var ua = (function () {
var agent = navigator.userAgent.toLowerCase();
// New function has access to 'agent' via closure
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')
};
// private methods
var getFaviconTag = function(){
var links = document.getElementsByTagName('link');
for(var i=0, len=links.length; i < len; i++) {
if ((links[i].getAttribute('rel') || '').match(/\bicon\b/)) {
return links[i];
}
}
return false;
};
var removeFaviconTag = function(){
var links = document.getElementsByTagName('link');
var head = document.getElementsByTagName('head')[0];
for(var i=0, len=links.length; i < len; i++) {
var exists = (typeof(links[i]) !== 'undefined');
if (exists && (links[i].getAttribute('rel') || '').match(/\bicon\b/)) {
head.removeChild(links[i]);
}
}
};
var getCurrentFavicon = function(){
if (!originalFavicon || !currentFavicon) {
var tag = getFaviconTag();
originalFavicon = currentFavicon = tag ? tag.getAttribute('href') : '/favicon.ico';
}
return currentFavicon;
};
var getCanvas = function (){
if (!canvas) {
canvas = document.createElement("canvas");
canvas.width = size;
canvas.height = size;
}
return canvas;
};
var setFaviconTag = function(url){
removeFaviconTag();
var link = document.createElement('link');
link.type = 'image/x-icon';
link.rel = 'icon';
link.href = url;
document.getElementsByTagName('head')[0].appendChild(link);
};
var log = function(message){
if (window.console) window.console.log(message);
};
var drawFavicon = function(label, colour) {
// fallback to updating the browser title if unsupported
if (!getCanvas().getContext || browser.ie || browser.safari || options.fallback === 'force') {
return updateTitle(label);
}
var context = getCanvas().getContext("2d");
var colour = colour || '#000000';
var src = getCurrentFavicon();
faviconImage = document.createElement('img');
faviconImage.onload = function() {
// clear canvas
context.clearRect(0, 0, size, size);
// draw the favicon
context.drawImage(faviconImage, 0, 0, faviconImage.width, faviconImage.height, 0, 0, size, size);
// draw bubble over the top
if ((label + '').length > 0) drawBubble(context, label, colour);
// refresh tag in page
refreshFavicon();
};
// allow cross origin resource requests if the image is not a data:uri
// as detailed here: https://github.com/mrdoob/three.js/issues/1305
if (!src.match(/^data/) && options.crossOrigin) {
faviconImage.crossOrigin = 'anonymous';
}
faviconImage.src = src;
};
var updateTitle = function(label) {
if (options.fallback) {
// Grab the current title that we can prefix with the label
var originalTitle = document.title;
// Strip out the old label if there is one
if (originalTitle[0] === '(') {
originalTitle = originalTitle.slice(originalTitle.indexOf(' '));
}
if ((label + '').length > 0) {
document.title = '(' + label + ') ' + originalTitle;
} else {
document.title = originalTitle;
}
}
};
var drawBubble = function(context, label, colour) {
// automatic abbreviation for long (>2 digits) numbers
if (typeof label == 'number' && label > 99 && options.abbreviate) {
label = abbreviateNumber(label);
}
// bubble needs to be larger for double digits
var len = (label + '').length-1;
var width = options.width * r + (6 * r * len),
height = options.height * r;
var top = size - height,
left = size - width - r,
bottom = 16 * r,
right = 16 * r,
radius = 2 * r;
// webkit seems to render fonts lighter than firefox
context.font = (browser.webkit ? 'bold ' : '') + options.font;
context.fillStyle = options.background;
context.strokeStyle = options.background;
context.lineWidth = r;
// bubble
context.beginPath();
context.moveTo(left + radius, top);
context.quadraticCurveTo(left, top, left, top + radius);
context.lineTo(left, bottom - radius);
context.quadraticCurveTo(left, bottom, left + radius, bottom);
context.lineTo(right - radius, bottom);
context.quadraticCurveTo(right, bottom, right, bottom - radius);
context.lineTo(right, top + radius);
context.quadraticCurveTo(right, top, right - radius, top);
context.closePath();
context.fill();
// bottom shadow
context.beginPath();
context.strokeStyle = "rgba(0,0,0,0.3)";
context.moveTo(left + radius / 2.0, bottom);
context.lineTo(right - radius / 2.0, bottom);
context.stroke();
// label
context.fillStyle = options.colour;
context.textAlign = "right";
context.textBaseline = "top";
// unfortunately webkit/mozilla are a pixel different in text positioning
context.fillText(label, r === 2 ? 29 : 15, browser.mozilla ? 7*r : 6*r);
};
var refreshFavicon = function(){
// check support
if (!getCanvas().getContext) return;
setFaviconTag(getCanvas().toDataURL());
};
var abbreviateNumber = function(label) {
var metricPrefixes = [
['G', 1000000000],
['M', 1000000],
['k', 1000]
];
for(var i = 0; i < metricPrefixes.length; ++i) {
if (label >= metricPrefixes[i][1]) {
label = round(label / metricPrefixes[i][1]) + metricPrefixes[i][0];
break;
}
}
return label;
};
var round = function (value, precision) {
var number = new Number(value);
return number.toFixed(precision);
};
// public methods
Tinycon.setOptions = function(custom){
options = {};
for(var key in defaults){
options[key] = custom.hasOwnProperty(key) ? custom[key] : defaults[key];
}
return this;
};
Tinycon.setImage = function(url){
currentFavicon = url;
refreshFavicon();
return this;
};
Tinycon.setBubble = function(label, colour) {
label = label || '';
drawFavicon(label, colour);
return this;
};
Tinycon.reset = function(){
setFaviconTag(originalFavicon);
};
Tinycon.setOptions(defaults);
window.Tinycon = Tinycon;
if(typeof define === 'function' && define.amd) {
define(Tinycon);
}
})();

8
vendors/tinycon/tinycon.min.js vendored Normal file
View file

@ -0,0 +1,8 @@
/*!
Tinycon - A small library for manipulating the Favicon
Tom Moor, http://tommoor.com
Copyright (c) 2012 Tom Moor
@license MIT Licensed
@version 0.6.3
*/
(function(){var Tinycon={};var currentFavicon=null;var originalFavicon=null;var faviconImage=null;var canvas=null;var options={};var r=window.devicePixelRatio||1;var size=16*r;var defaults={width:7,height:9,font:10*r+'px arial',colour:'#ffffff',background:'#F03D25',fallback:true,crossOrigin:true,abbreviate:true};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')};var getFaviconTag=function(){var links=document.getElementsByTagName('link');for(var i=0,len=links.length;i<len;i++){if((links[i].getAttribute('rel')||'').match(/\bicon\b/)){return links[i]}}return false};var removeFaviconTag=function(){var links=document.getElementsByTagName('link');var head=document.getElementsByTagName('head')[0];for(var i=0,len=links.length;i<len;i++){var exists=(typeof(links[i])!=='undefined');if(exists&&(links[i].getAttribute('rel')||'').match(/\bicon\b/)){head.removeChild(links[i])}}};var getCurrentFavicon=function(){if(!originalFavicon||!currentFavicon){var tag=getFaviconTag();originalFavicon=currentFavicon=tag?tag.getAttribute('href'):'/favicon.ico'}return currentFavicon};var getCanvas=function(){if(!canvas){canvas=document.createElement("canvas");canvas.width=size;canvas.height=size}return canvas};var setFaviconTag=function(url){removeFaviconTag();var link=document.createElement('link');link.type='image/x-icon';link.rel='icon';link.href=url;document.getElementsByTagName('head')[0].appendChild(link)};var log=function(message){if(window.console)window.console.log(message)};var drawFavicon=function(label,colour){if(!getCanvas().getContext||browser.ie||browser.safari||options.fallback==='force'){return updateTitle(label)}var context=getCanvas().getContext("2d");var colour=colour||'#000000';var src=getCurrentFavicon();faviconImage=document.createElement('img');faviconImage.onload=function(){context.clearRect(0,0,size,size);context.drawImage(faviconImage,0,0,faviconImage.width,faviconImage.height,0,0,size,size);if((label+'').length>0)drawBubble(context,label,colour);refreshFavicon()};if(!src.match(/^data/)&&options.crossOrigin){faviconImage.crossOrigin='anonymous'}faviconImage.src=src};var updateTitle=function(label){if(options.fallback){var originalTitle=document.title;if(originalTitle[0]==='('){originalTitle=originalTitle.slice(originalTitle.indexOf(' '))}if((label+'').length>0){document.title='('+label+') '+originalTitle}else{document.title=originalTitle}}};var drawBubble=function(context,label,colour){if(typeof label=='number'&&label>99&&options.abbreviate){label=abbreviateNumber(label)}var len=(label+'').length-1;var width=options.width*r+(6*r*len),height=options.height*r;var top=size-height,left=size-width-r,bottom=16*r,right=16*r,radius=2*r;context.font=(browser.webkit?'bold ':'')+options.font;context.fillStyle=options.background;context.strokeStyle=options.background;context.lineWidth=r;context.beginPath();context.moveTo(left+radius,top);context.quadraticCurveTo(left,top,left,top+radius);context.lineTo(left,bottom-radius);context.quadraticCurveTo(left,bottom,left+radius,bottom);context.lineTo(right-radius,bottom);context.quadraticCurveTo(right,bottom,right,bottom-radius);context.lineTo(right,top+radius);context.quadraticCurveTo(right,top,right-radius,top);context.closePath();context.fill();context.beginPath();context.strokeStyle="rgba(0,0,0,0.3)";context.moveTo(left+radius/2.0,bottom);context.lineTo(right-radius/2.0,bottom);context.stroke();context.fillStyle=options.colour;context.textAlign="right";context.textBaseline="top";context.fillText(label,r===2?29:15,browser.mozilla?7*r:6*r)};var refreshFavicon=function(){if(!getCanvas().getContext)return;setFaviconTag(getCanvas().toDataURL())};var abbreviateNumber=function(label){var metricPrefixes=[['G',1000000000],['M',1000000],['k',1000]];for(var i=0;i<metricPrefixes.length;++i){if(label>=metricPrefixes[i][1]){label=round(label/metricPrefixes[i][1])+metricPrefixes[i][0];break}}return label};var round=function(value,precision){var number=new Number(value);return number.toFixed(precision)};Tinycon.setOptions=function(custom){options={};for(var key in defaults){options[key]=custom.hasOwnProperty(key)?custom[key]:defaults[key]}return this};Tinycon.setImage=function(url){currentFavicon=url;refreshFavicon();return this};Tinycon.setBubble=function(label,colour){label=label||'';drawFavicon(label,colour);return this};Tinycon.reset=function(){setFaviconTag(originalFavicon)};Tinycon.setOptions(defaults);window.Tinycon=Tinycon;if(typeof define==='function'&&define.amd){define(Tinycon)}})();

View file

@ -38,6 +38,7 @@ module.exports = {
'hasher': 'window.hasher',
'Jua': 'window.Jua',
'Autolinker': 'window.Autolinker',
'Tinycon': 'window.Tinycon',
'buzz': 'window.buzz',
'ssm': 'window.ssm',
'key': 'window.key',