mailcatcher 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +3 -1
- data/bin/catchmail +1 -1
- data/lib/mail_catcher/mail.rb +13 -22
- data/lib/mail_catcher/version.rb +1 -1
- data/lib/mail_catcher/web/application.rb +1 -27
- data/lib/mail_catcher/web.rb +1 -6
- data/public/assets/date.js +104 -0
- data/public/assets/favcount.js +102 -0
- data/public/assets/jquery.js +10598 -0
- data/public/assets/keymaster.js +296 -0
- data/public/assets/mailcatcher.css +437 -1
- data/public/assets/mailcatcher.js +462 -5
- data/public/assets/modernizr.js +357 -0
- data/public/assets/url.js +634 -0
- data/views/index.erb +8 -2
- metadata +46 -121
- checksums.yaml.gz.sig +0 -3
- data.tar.gz.sig +0 -0
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f7f2d5479bcae9993d99138058a04648104372c084ea5b49ebca2d1e30b7d2c2
|
|
4
|
+
data.tar.gz: da34164dac3a23281db1a44db03cd07f2c31e5e3220fe9b8aa034c90f49d10bc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c9ea737a97aba8c1c00ee4d383ceac7de9a6f27acb10b86b415379ff777ca4af5c77179685126a70dc771de34ae41eb3a5ff1272152d9c61ad0eceb85d87ab51
|
|
7
|
+
data.tar.gz: f7b99106a86b2074fd8bee3750b8cf83e3e3aa82291f3f8a070bebec1bb5536a28d72cf59726d5915c8e702d744d09edd184b0bec3f9934fa52f2b9cff8b419c
|
data/README.md
CHANGED
|
@@ -60,6 +60,8 @@ gem install mailcatcher
|
|
|
60
60
|
|
|
61
61
|
### Ruby
|
|
62
62
|
|
|
63
|
+
MailCatcher requires Ruby 3.3 or newer.
|
|
64
|
+
|
|
63
65
|
If you have trouble with the setup commands, make sure you have [Ruby installed](https://www.ruby-lang.org/en/documentation/installation/):
|
|
64
66
|
|
|
65
67
|
```
|
|
@@ -130,7 +132,7 @@ if DEBUG:
|
|
|
130
132
|
There is a Docker image available [on Docker Hub](https://hub.docker.com/r/sj26/mailcatcher):
|
|
131
133
|
|
|
132
134
|
```
|
|
133
|
-
$ docker run -p 1080 -p 1025 sj26/mailcatcher
|
|
135
|
+
$ docker run -p 1080:1080 -p 1025:1025 sj26/mailcatcher
|
|
134
136
|
Unable to find image 'sj26/mailcatcher:latest' locally
|
|
135
137
|
latest: Pulling from sj26/mailcatcher
|
|
136
138
|
8c6d1654570f: Already exists
|
data/bin/catchmail
CHANGED
data/lib/mail_catcher/mail.rb
CHANGED
|
@@ -8,7 +8,7 @@ require "sqlite3"
|
|
|
8
8
|
module MailCatcher::Mail extend self
|
|
9
9
|
def db
|
|
10
10
|
@__db ||= begin
|
|
11
|
-
SQLite3::Database.new(":memory:", :type_translation => true).tap do |db|
|
|
11
|
+
SQLite3::Database.new(":memory:", :type_translation => true, :results_as_hash => true).tap do |db|
|
|
12
12
|
db.execute(<<-SQL)
|
|
13
13
|
CREATE TABLE message (
|
|
14
14
|
id INTEGER PRIMARY KEY ASC,
|
|
@@ -69,13 +69,14 @@ module MailCatcher::Mail extend self
|
|
|
69
69
|
|
|
70
70
|
def latest_created_at
|
|
71
71
|
@latest_created_at_query ||= db.prepare "SELECT created_at FROM message ORDER BY created_at DESC LIMIT 1"
|
|
72
|
-
@latest_created_at_query.execute.next
|
|
72
|
+
row = @latest_created_at_query.execute.next
|
|
73
|
+
row && row.values
|
|
73
74
|
end
|
|
74
75
|
|
|
75
76
|
def messages
|
|
76
77
|
@messages_query ||= db.prepare "SELECT id, sender, recipients, subject, size, created_at FROM message ORDER BY created_at, id ASC"
|
|
77
78
|
@messages_query.execute.map do |row|
|
|
78
|
-
|
|
79
|
+
row.tap do |message|
|
|
79
80
|
message["recipients"] &&= JSON.parse(message["recipients"])
|
|
80
81
|
end
|
|
81
82
|
end
|
|
@@ -84,7 +85,7 @@ module MailCatcher::Mail extend self
|
|
|
84
85
|
def message(id)
|
|
85
86
|
@message_query ||= db.prepare "SELECT id, sender, recipients, subject, size, type, created_at FROM message WHERE id = ? LIMIT 1"
|
|
86
87
|
row = @message_query.execute(id).next
|
|
87
|
-
row &&
|
|
88
|
+
row && row.tap do |message|
|
|
88
89
|
message["recipients"] &&= JSON.parse(message["recipients"])
|
|
89
90
|
end
|
|
90
91
|
end
|
|
@@ -92,7 +93,7 @@ module MailCatcher::Mail extend self
|
|
|
92
93
|
def message_source(id)
|
|
93
94
|
@message_source_query ||= db.prepare "SELECT source FROM message WHERE id = ? LIMIT 1"
|
|
94
95
|
row = @message_source_query.execute(id).next
|
|
95
|
-
row && row
|
|
96
|
+
row && row["source"]
|
|
96
97
|
end
|
|
97
98
|
|
|
98
99
|
def message_has_html?(id)
|
|
@@ -107,28 +108,22 @@ module MailCatcher::Mail extend self
|
|
|
107
108
|
|
|
108
109
|
def message_parts(id)
|
|
109
110
|
@message_parts_query ||= db.prepare "SELECT cid, type, filename, size FROM message_part WHERE message_id = ? ORDER BY filename ASC"
|
|
110
|
-
@message_parts_query.execute(id).
|
|
111
|
-
Hash[row.fields.zip(row)]
|
|
112
|
-
end
|
|
111
|
+
@message_parts_query.execute(id).to_a
|
|
113
112
|
end
|
|
114
113
|
|
|
115
114
|
def message_attachments(id)
|
|
116
115
|
@message_parts_query ||= db.prepare "SELECT cid, type, filename, size FROM message_part WHERE message_id = ? AND is_attachment = 1 ORDER BY filename ASC"
|
|
117
|
-
@message_parts_query.execute(id).
|
|
118
|
-
Hash[row.fields.zip(row)]
|
|
119
|
-
end
|
|
116
|
+
@message_parts_query.execute(id).to_a
|
|
120
117
|
end
|
|
121
118
|
|
|
122
119
|
def message_part(message_id, part_id)
|
|
123
120
|
@message_part_query ||= db.prepare "SELECT * FROM message_part WHERE message_id = ? AND id = ? LIMIT 1"
|
|
124
|
-
|
|
125
|
-
row && Hash[row.fields.zip(row)]
|
|
121
|
+
@message_part_query.execute(message_id, part_id).next
|
|
126
122
|
end
|
|
127
123
|
|
|
128
124
|
def message_part_type(message_id, part_type)
|
|
129
125
|
@message_part_type_query ||= db.prepare "SELECT * FROM message_part WHERE message_id = ? AND type = ? AND is_attachment = 0 LIMIT 1"
|
|
130
|
-
|
|
131
|
-
row && Hash[row.fields.zip(row)]
|
|
126
|
+
@message_part_type_query.execute(message_id, part_type).next
|
|
132
127
|
end
|
|
133
128
|
|
|
134
129
|
def message_part_html(message_id)
|
|
@@ -146,9 +141,7 @@ module MailCatcher::Mail extend self
|
|
|
146
141
|
|
|
147
142
|
def message_part_cid(message_id, cid)
|
|
148
143
|
@message_part_cid_query ||= db.prepare "SELECT * FROM message_part WHERE message_id = ?"
|
|
149
|
-
@message_part_cid_query.execute(message_id).
|
|
150
|
-
Hash[row.fields.zip(row)]
|
|
151
|
-
end.find do |part|
|
|
144
|
+
@message_part_cid_query.execute(message_id).find do |part|
|
|
152
145
|
part["cid"] == cid
|
|
153
146
|
end
|
|
154
147
|
end
|
|
@@ -173,10 +166,8 @@ module MailCatcher::Mail extend self
|
|
|
173
166
|
|
|
174
167
|
def delete_older_messages!(count = MailCatcher.options[:messages_limit])
|
|
175
168
|
return if count.nil?
|
|
176
|
-
@older_messages_query ||= db.prepare "SELECT id FROM message WHERE id NOT IN (SELECT id FROM message ORDER BY created_at DESC LIMIT ?)"
|
|
177
|
-
@older_messages_query.execute(count).
|
|
178
|
-
Hash[row.fields.zip(row)]
|
|
179
|
-
end.each do |message|
|
|
169
|
+
@older_messages_query ||= db.prepare "SELECT id FROM message WHERE id NOT IN (SELECT id FROM message ORDER BY created_at DESC, id DESC LIMIT ?)"
|
|
170
|
+
@older_messages_query.execute(count).each do |message|
|
|
180
171
|
delete_message!(message["id"])
|
|
181
172
|
end
|
|
182
173
|
end
|
data/lib/mail_catcher/version.rb
CHANGED
|
@@ -36,34 +36,8 @@ module MailCatcher
|
|
|
36
36
|
class Application < Sinatra::Base
|
|
37
37
|
set :environment, MailCatcher.env
|
|
38
38
|
set :prefix, MailCatcher.options[:http_path]
|
|
39
|
-
set :asset_prefix, File.join(prefix, "assets")
|
|
40
39
|
set :root, File.expand_path("#{__FILE__}/../../../..")
|
|
41
40
|
|
|
42
|
-
if development?
|
|
43
|
-
require "sprockets-helpers"
|
|
44
|
-
|
|
45
|
-
configure do
|
|
46
|
-
require "mail_catcher/web/assets"
|
|
47
|
-
Sprockets::Helpers.configure do |config|
|
|
48
|
-
config.environment = Assets
|
|
49
|
-
config.prefix = settings.asset_prefix
|
|
50
|
-
config.digest = false
|
|
51
|
-
config.public_path = public_folder
|
|
52
|
-
config.debug = true
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
helpers do
|
|
57
|
-
include Sprockets::Helpers
|
|
58
|
-
end
|
|
59
|
-
else
|
|
60
|
-
helpers do
|
|
61
|
-
def asset_path(filename)
|
|
62
|
-
File.join(settings.asset_prefix, filename)
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
41
|
get "/" do
|
|
68
42
|
erb :index
|
|
69
43
|
end
|
|
@@ -133,7 +107,7 @@ module MailCatcher
|
|
|
133
107
|
body = part["body"]
|
|
134
108
|
|
|
135
109
|
# Rewrite body to link to embedded attachments served by cid
|
|
136
|
-
body.gsub
|
|
110
|
+
body = body.gsub /cid:([^'"> ]+)/, "#{id}/parts/\\1"
|
|
137
111
|
|
|
138
112
|
body
|
|
139
113
|
else
|
data/lib/mail_catcher/web.rb
CHANGED
|
@@ -9,16 +9,11 @@ module MailCatcher
|
|
|
9
9
|
def app
|
|
10
10
|
@@app ||= Rack::Builder.new do
|
|
11
11
|
map(MailCatcher.options[:http_path]) do
|
|
12
|
-
if MailCatcher.development?
|
|
13
|
-
require "mail_catcher/web/assets"
|
|
14
|
-
map("/assets") { run Assets }
|
|
15
|
-
end
|
|
16
|
-
|
|
17
12
|
run Application
|
|
18
13
|
end
|
|
19
14
|
|
|
20
15
|
# This should only affect when http_path is anything but "/" above
|
|
21
|
-
run lambda { |env| [302, {"
|
|
16
|
+
run lambda { |env| [302, {"location" => MailCatcher.options[:http_path]}, []] }
|
|
22
17
|
end
|
|
23
18
|
end
|
|
24
19
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version: 1.0 Alpha-1
|
|
3
|
+
* Build Date: 13-Nov-2007
|
|
4
|
+
* Copyright (c) 2006-2007, Coolite Inc. (http://www.coolite.com/). All rights reserved.
|
|
5
|
+
* License: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/.
|
|
6
|
+
* Website: http://www.datejs.com/ or http://www.coolite.com/datejs/
|
|
7
|
+
*/
|
|
8
|
+
Date.CultureInfo={name:"en-US",englishName:"English (United States)",nativeName:"English (United States)",dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],abbreviatedDayNames:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],shortestDayNames:["Su","Mo","Tu","We","Th","Fr","Sa"],firstLetterDayNames:["S","M","T","W","T","F","S"],monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],abbreviatedMonthNames:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],amDesignator:"AM",pmDesignator:"PM",firstDayOfWeek:0,twoDigitYearMax:2029,dateElementOrder:"mdy",formatPatterns:{shortDate:"M/d/yyyy",longDate:"dddd, MMMM dd, yyyy",shortTime:"h:mm tt",longTime:"h:mm:ss tt",fullDateTime:"dddd, MMMM dd, yyyy h:mm:ss tt",sortableDateTime:"yyyy-MM-ddTHH:mm:ss",universalSortableDateTime:"yyyy-MM-dd HH:mm:ssZ",rfc1123:"ddd, dd MMM yyyy HH:mm:ss GMT",monthDay:"MMMM dd",yearMonth:"MMMM, yyyy"},regexPatterns:{jan:/^jan(uary)?/i,feb:/^feb(ruary)?/i,mar:/^mar(ch)?/i,apr:/^apr(il)?/i,may:/^may/i,jun:/^jun(e)?/i,jul:/^jul(y)?/i,aug:/^aug(ust)?/i,sep:/^sep(t(ember)?)?/i,oct:/^oct(ober)?/i,nov:/^nov(ember)?/i,dec:/^dec(ember)?/i,sun:/^su(n(day)?)?/i,mon:/^mo(n(day)?)?/i,tue:/^tu(e(s(day)?)?)?/i,wed:/^we(d(nesday)?)?/i,thu:/^th(u(r(s(day)?)?)?)?/i,fri:/^fr(i(day)?)?/i,sat:/^sa(t(urday)?)?/i,future:/^next/i,past:/^last|past|prev(ious)?/i,add:/^(\+|after|from)/i,subtract:/^(\-|before|ago)/i,yesterday:/^yesterday/i,today:/^t(oday)?/i,tomorrow:/^tomorrow/i,now:/^n(ow)?/i,millisecond:/^ms|milli(second)?s?/i,second:/^sec(ond)?s?/i,minute:/^min(ute)?s?/i,hour:/^h(ou)?rs?/i,week:/^w(ee)?k/i,month:/^m(o(nth)?s?)?/i,day:/^d(ays?)?/i,year:/^y((ea)?rs?)?/i,shortMeridian:/^(a|p)/i,longMeridian:/^(a\.?m?\.?|p\.?m?\.?)/i,timezone:/^((e(s|d)t|c(s|d)t|m(s|d)t|p(s|d)t)|((gmt)?\s*(\+|\-)\s*\d\d\d\d?)|gmt)/i,ordinalSuffix:/^\s*(st|nd|rd|th)/i,timeContext:/^\s*(\:|a|p)/i},abbreviatedTimeZoneStandard:{GMT:"-000",EST:"-0400",CST:"-0500",MST:"-0600",PST:"-0700"},abbreviatedTimeZoneDST:{GMT:"-000",EDT:"-0500",CDT:"-0600",MDT:"-0700",PDT:"-0800"}};
|
|
9
|
+
Date.getMonthNumberFromName=function(name){var n=Date.CultureInfo.monthNames,m=Date.CultureInfo.abbreviatedMonthNames,s=name.toLowerCase();for(var i=0;i<n.length;i++){if(n[i].toLowerCase()==s||m[i].toLowerCase()==s){return i;}}
|
|
10
|
+
return-1;};Date.getDayNumberFromName=function(name){var n=Date.CultureInfo.dayNames,m=Date.CultureInfo.abbreviatedDayNames,o=Date.CultureInfo.shortestDayNames,s=name.toLowerCase();for(var i=0;i<n.length;i++){if(n[i].toLowerCase()==s||m[i].toLowerCase()==s){return i;}}
|
|
11
|
+
return-1;};Date.isLeapYear=function(year){return(((year%4===0)&&(year%100!==0))||(year%400===0));};Date.getDaysInMonth=function(year,month){return[31,(Date.isLeapYear(year)?29:28),31,30,31,30,31,31,30,31,30,31][month];};Date.getTimezoneOffset=function(s,dst){return(dst||false)?Date.CultureInfo.abbreviatedTimeZoneDST[s.toUpperCase()]:Date.CultureInfo.abbreviatedTimeZoneStandard[s.toUpperCase()];};Date.getTimezoneAbbreviation=function(offset,dst){var n=(dst||false)?Date.CultureInfo.abbreviatedTimeZoneDST:Date.CultureInfo.abbreviatedTimeZoneStandard,p;for(p in n){if(n[p]===offset){return p;}}
|
|
12
|
+
return null;};Date.prototype.clone=function(){return new Date(this.getTime());};Date.prototype.compareTo=function(date){if(isNaN(this)){throw new Error(this);}
|
|
13
|
+
if(date instanceof Date&&!isNaN(date)){return(this>date)?1:(this<date)?-1:0;}else{throw new TypeError(date);}};Date.prototype.equals=function(date){return(this.compareTo(date)===0);};Date.prototype.between=function(start,end){var t=this.getTime();return t>=start.getTime()&&t<=end.getTime();};Date.prototype.addMilliseconds=function(value){this.setMilliseconds(this.getMilliseconds()+value);return this;};Date.prototype.addSeconds=function(value){return this.addMilliseconds(value*1000);};Date.prototype.addMinutes=function(value){return this.addMilliseconds(value*60000);};Date.prototype.addHours=function(value){return this.addMilliseconds(value*3600000);};Date.prototype.addDays=function(value){return this.addMilliseconds(value*86400000);};Date.prototype.addWeeks=function(value){return this.addMilliseconds(value*604800000);};Date.prototype.addMonths=function(value){var n=this.getDate();this.setDate(1);this.setMonth(this.getMonth()+value);this.setDate(Math.min(n,this.getDaysInMonth()));return this;};Date.prototype.addYears=function(value){return this.addMonths(value*12);};Date.prototype.add=function(config){if(typeof config=="number"){this._orient=config;return this;}
|
|
14
|
+
var x=config;if(x.millisecond||x.milliseconds){this.addMilliseconds(x.millisecond||x.milliseconds);}
|
|
15
|
+
if(x.second||x.seconds){this.addSeconds(x.second||x.seconds);}
|
|
16
|
+
if(x.minute||x.minutes){this.addMinutes(x.minute||x.minutes);}
|
|
17
|
+
if(x.hour||x.hours){this.addHours(x.hour||x.hours);}
|
|
18
|
+
if(x.month||x.months){this.addMonths(x.month||x.months);}
|
|
19
|
+
if(x.year||x.years){this.addYears(x.year||x.years);}
|
|
20
|
+
if(x.day||x.days){this.addDays(x.day||x.days);}
|
|
21
|
+
return this;};Date._validate=function(value,min,max,name){if(typeof value!="number"){throw new TypeError(value+" is not a Number.");}else if(value<min||value>max){throw new RangeError(value+" is not a valid value for "+name+".");}
|
|
22
|
+
return true;};Date.validateMillisecond=function(n){return Date._validate(n,0,999,"milliseconds");};Date.validateSecond=function(n){return Date._validate(n,0,59,"seconds");};Date.validateMinute=function(n){return Date._validate(n,0,59,"minutes");};Date.validateHour=function(n){return Date._validate(n,0,23,"hours");};Date.validateDay=function(n,year,month){return Date._validate(n,1,Date.getDaysInMonth(year,month),"days");};Date.validateMonth=function(n){return Date._validate(n,0,11,"months");};Date.validateYear=function(n){return Date._validate(n,1,9999,"seconds");};Date.prototype.set=function(config){var x=config;if(!x.millisecond&&x.millisecond!==0){x.millisecond=-1;}
|
|
23
|
+
if(!x.second&&x.second!==0){x.second=-1;}
|
|
24
|
+
if(!x.minute&&x.minute!==0){x.minute=-1;}
|
|
25
|
+
if(!x.hour&&x.hour!==0){x.hour=-1;}
|
|
26
|
+
if(!x.day&&x.day!==0){x.day=-1;}
|
|
27
|
+
if(!x.month&&x.month!==0){x.month=-1;}
|
|
28
|
+
if(!x.year&&x.year!==0){x.year=-1;}
|
|
29
|
+
if(x.millisecond!=-1&&Date.validateMillisecond(x.millisecond)){this.addMilliseconds(x.millisecond-this.getMilliseconds());}
|
|
30
|
+
if(x.second!=-1&&Date.validateSecond(x.second)){this.addSeconds(x.second-this.getSeconds());}
|
|
31
|
+
if(x.minute!=-1&&Date.validateMinute(x.minute)){this.addMinutes(x.minute-this.getMinutes());}
|
|
32
|
+
if(x.hour!=-1&&Date.validateHour(x.hour)){this.addHours(x.hour-this.getHours());}
|
|
33
|
+
if(x.month!==-1&&Date.validateMonth(x.month)){this.addMonths(x.month-this.getMonth());}
|
|
34
|
+
if(x.year!=-1&&Date.validateYear(x.year)){this.addYears(x.year-this.getFullYear());}
|
|
35
|
+
if(x.day!=-1&&Date.validateDay(x.day,this.getFullYear(),this.getMonth())){this.addDays(x.day-this.getDate());}
|
|
36
|
+
if(x.timezone){this.setTimezone(x.timezone);}
|
|
37
|
+
if(x.timezoneOffset){this.setTimezoneOffset(x.timezoneOffset);}
|
|
38
|
+
return this;};Date.prototype.clearTime=function(){this.setHours(0);this.setMinutes(0);this.setSeconds(0);this.setMilliseconds(0);return this;};Date.prototype.isLeapYear=function(){var y=this.getFullYear();return(((y%4===0)&&(y%100!==0))||(y%400===0));};Date.prototype.isWeekday=function(){return!(this.is().sat()||this.is().sun());};Date.prototype.getDaysInMonth=function(){return Date.getDaysInMonth(this.getFullYear(),this.getMonth());};Date.prototype.moveToFirstDayOfMonth=function(){return this.set({day:1});};Date.prototype.moveToLastDayOfMonth=function(){return this.set({day:this.getDaysInMonth()});};Date.prototype.moveToDayOfWeek=function(day,orient){var diff=(day-this.getDay()+7*(orient||+1))%7;return this.addDays((diff===0)?diff+=7*(orient||+1):diff);};Date.prototype.moveToMonth=function(month,orient){var diff=(month-this.getMonth()+12*(orient||+1))%12;return this.addMonths((diff===0)?diff+=12*(orient||+1):diff);};Date.prototype.getDayOfYear=function(){return Math.floor((this-new Date(this.getFullYear(),0,1))/86400000);};Date.prototype.getWeekOfYear=function(firstDayOfWeek){var y=this.getFullYear(),m=this.getMonth(),d=this.getDate();var dow=firstDayOfWeek||Date.CultureInfo.firstDayOfWeek;var offset=7+1-new Date(y,0,1).getDay();if(offset==8){offset=1;}
|
|
39
|
+
var daynum=((Date.UTC(y,m,d,0,0,0)-Date.UTC(y,0,1,0,0,0))/86400000)+1;var w=Math.floor((daynum-offset+7)/7);if(w===dow){y--;var prevOffset=7+1-new Date(y,0,1).getDay();if(prevOffset==2||prevOffset==8){w=53;}else{w=52;}}
|
|
40
|
+
return w;};Date.prototype.isDST=function(){return this.toString().match(/(E|C|M|P)(S|D)T/)[2]=="D";};Date.prototype.getTimezone=function(){return Date.getTimezoneAbbreviation(this.getUTCOffset,this.isDST());};Date.prototype.setTimezoneOffset=function(s){var here=this.getTimezoneOffset(),there=Number(s)*-6/10;this.addMinutes(there-here);return this;};Date.prototype.setTimezone=function(s){return this.setTimezoneOffset(Date.getTimezoneOffset(s));};Date.prototype.getUTCOffset=function(){var n=this.getTimezoneOffset()*-10/6,r;if(n<0){r=(n-10000).toString();return r[0]+r.substr(2);}else{r=(n+10000).toString();return"+"+r.substr(1);}};Date.prototype.getDayName=function(abbrev){return abbrev?Date.CultureInfo.abbreviatedDayNames[this.getDay()]:Date.CultureInfo.dayNames[this.getDay()];};Date.prototype.getMonthName=function(abbrev){return abbrev?Date.CultureInfo.abbreviatedMonthNames[this.getMonth()]:Date.CultureInfo.monthNames[this.getMonth()];};Date.prototype._toString=Date.prototype.toString;Date.prototype.toString=function(format){var self=this;var p=function p(s){return(s.toString().length==1)?"0"+s:s;};return format?format.replace(/dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|zz?z?/g,function(format){switch(format){case"hh":return p(self.getHours()<13?self.getHours():(self.getHours()-12));case"h":return self.getHours()<13?self.getHours():(self.getHours()-12);case"HH":return p(self.getHours());case"H":return self.getHours();case"mm":return p(self.getMinutes());case"m":return self.getMinutes();case"ss":return p(self.getSeconds());case"s":return self.getSeconds();case"yyyy":return self.getFullYear();case"yy":return self.getFullYear().toString().substring(2,4);case"dddd":return self.getDayName();case"ddd":return self.getDayName(true);case"dd":return p(self.getDate());case"d":return self.getDate().toString();case"MMMM":return self.getMonthName();case"MMM":return self.getMonthName(true);case"MM":return p((self.getMonth()+1));case"M":return self.getMonth()+1;case"t":return self.getHours()<12?Date.CultureInfo.amDesignator.substring(0,1):Date.CultureInfo.pmDesignator.substring(0,1);case"tt":return self.getHours()<12?Date.CultureInfo.amDesignator:Date.CultureInfo.pmDesignator;case"zzz":case"zz":case"z":return"";}}):this._toString();};
|
|
41
|
+
Date.now=function(){return new Date();};Date.today=function(){return Date.now().clearTime();};Date.prototype._orient=+1;Date.prototype.next=function(){this._orient=+1;return this;};Date.prototype.last=Date.prototype.prev=Date.prototype.previous=function(){this._orient=-1;return this;};Date.prototype._is=false;Date.prototype.is=function(){this._is=true;return this;};Number.prototype._dateElement="day";Number.prototype.fromNow=function(){var c={};c[this._dateElement]=this;return Date.now().add(c);};Number.prototype.ago=function(){var c={};c[this._dateElement]=this*-1;return Date.now().add(c);};(function(){var $D=Date.prototype,$N=Number.prototype;var dx=("sunday monday tuesday wednesday thursday friday saturday").split(/\s/),mx=("january february march april may june july august september october november december").split(/\s/),px=("Millisecond Second Minute Hour Day Week Month Year").split(/\s/),de;var df=function(n){return function(){if(this._is){this._is=false;return this.getDay()==n;}
|
|
42
|
+
return this.moveToDayOfWeek(n,this._orient);};};for(var i=0;i<dx.length;i++){$D[dx[i]]=$D[dx[i].substring(0,3)]=df(i);}
|
|
43
|
+
var mf=function(n){return function(){if(this._is){this._is=false;return this.getMonth()===n;}
|
|
44
|
+
return this.moveToMonth(n,this._orient);};};for(var j=0;j<mx.length;j++){$D[mx[j]]=$D[mx[j].substring(0,3)]=mf(j);}
|
|
45
|
+
var ef=function(j){return function(){if(j.substring(j.length-1)!="s"){j+="s";}
|
|
46
|
+
return this["add"+j](this._orient);};};var nf=function(n){return function(){this._dateElement=n;return this;};};for(var k=0;k<px.length;k++){de=px[k].toLowerCase();$D[de]=$D[de+"s"]=ef(px[k]);$N[de]=$N[de+"s"]=nf(de);}}());Date.prototype.toJSONString=function(){return this.toString("yyyy-MM-ddThh:mm:ssZ");};Date.prototype.toShortDateString=function(){return this.toString(Date.CultureInfo.formatPatterns.shortDatePattern);};Date.prototype.toLongDateString=function(){return this.toString(Date.CultureInfo.formatPatterns.longDatePattern);};Date.prototype.toShortTimeString=function(){return this.toString(Date.CultureInfo.formatPatterns.shortTimePattern);};Date.prototype.toLongTimeString=function(){return this.toString(Date.CultureInfo.formatPatterns.longTimePattern);};Date.prototype.getOrdinal=function(){switch(this.getDate()){case 1:case 21:case 31:return"st";case 2:case 22:return"nd";case 3:case 23:return"rd";default:return"th";}};
|
|
47
|
+
(function(){Date.Parsing={Exception:function(s){this.message="Parse error at '"+s.substring(0,10)+" ...'";}};var $P=Date.Parsing;var _=$P.Operators={rtoken:function(r){return function(s){var mx=s.match(r);if(mx){return([mx[0],s.substring(mx[0].length)]);}else{throw new $P.Exception(s);}};},token:function(s){return function(s){return _.rtoken(new RegExp("^\s*"+s+"\s*"))(s);};},stoken:function(s){return _.rtoken(new RegExp("^"+s));},until:function(p){return function(s){var qx=[],rx=null;while(s.length){try{rx=p.call(this,s);}catch(e){qx.push(rx[0]);s=rx[1];continue;}
|
|
48
|
+
break;}
|
|
49
|
+
return[qx,s];};},many:function(p){return function(s){var rx=[],r=null;while(s.length){try{r=p.call(this,s);}catch(e){return[rx,s];}
|
|
50
|
+
rx.push(r[0]);s=r[1];}
|
|
51
|
+
return[rx,s];};},optional:function(p){return function(s){var r=null;try{r=p.call(this,s);}catch(e){return[null,s];}
|
|
52
|
+
return[r[0],r[1]];};},not:function(p){return function(s){try{p.call(this,s);}catch(e){return[null,s];}
|
|
53
|
+
throw new $P.Exception(s);};},ignore:function(p){return p?function(s){var r=null;r=p.call(this,s);return[null,r[1]];}:null;},product:function(){var px=arguments[0],qx=Array.prototype.slice.call(arguments,1),rx=[];for(var i=0;i<px.length;i++){rx.push(_.each(px[i],qx));}
|
|
54
|
+
return rx;},cache:function(rule){var cache={},r=null;return function(s){try{r=cache[s]=(cache[s]||rule.call(this,s));}catch(e){r=cache[s]=e;}
|
|
55
|
+
if(r instanceof $P.Exception){throw r;}else{return r;}};},any:function(){var px=arguments;return function(s){var r=null;for(var i=0;i<px.length;i++){if(px[i]==null){continue;}
|
|
56
|
+
try{r=(px[i].call(this,s));}catch(e){r=null;}
|
|
57
|
+
if(r){return r;}}
|
|
58
|
+
throw new $P.Exception(s);};},each:function(){var px=arguments;return function(s){var rx=[],r=null;for(var i=0;i<px.length;i++){if(px[i]==null){continue;}
|
|
59
|
+
try{r=(px[i].call(this,s));}catch(e){throw new $P.Exception(s);}
|
|
60
|
+
rx.push(r[0]);s=r[1];}
|
|
61
|
+
return[rx,s];};},all:function(){var px=arguments,_=_;return _.each(_.optional(px));},sequence:function(px,d,c){d=d||_.rtoken(/^\s*/);c=c||null;if(px.length==1){return px[0];}
|
|
62
|
+
return function(s){var r=null,q=null;var rx=[];for(var i=0;i<px.length;i++){try{r=px[i].call(this,s);}catch(e){break;}
|
|
63
|
+
rx.push(r[0]);try{q=d.call(this,r[1]);}catch(ex){q=null;break;}
|
|
64
|
+
s=q[1];}
|
|
65
|
+
if(!r){throw new $P.Exception(s);}
|
|
66
|
+
if(q){throw new $P.Exception(q[1]);}
|
|
67
|
+
if(c){try{r=c.call(this,r[1]);}catch(ey){throw new $P.Exception(r[1]);}}
|
|
68
|
+
return[rx,(r?r[1]:s)];};},between:function(d1,p,d2){d2=d2||d1;var _fn=_.each(_.ignore(d1),p,_.ignore(d2));return function(s){var rx=_fn.call(this,s);return[[rx[0][0],r[0][2]],rx[1]];};},list:function(p,d,c){d=d||_.rtoken(/^\s*/);c=c||null;return(p instanceof Array?_.each(_.product(p.slice(0,-1),_.ignore(d)),p.slice(-1),_.ignore(c)):_.each(_.many(_.each(p,_.ignore(d))),px,_.ignore(c)));},set:function(px,d,c){d=d||_.rtoken(/^\s*/);c=c||null;return function(s){var r=null,p=null,q=null,rx=null,best=[[],s],last=false;for(var i=0;i<px.length;i++){q=null;p=null;r=null;last=(px.length==1);try{r=px[i].call(this,s);}catch(e){continue;}
|
|
69
|
+
rx=[[r[0]],r[1]];if(r[1].length>0&&!last){try{q=d.call(this,r[1]);}catch(ex){last=true;}}else{last=true;}
|
|
70
|
+
if(!last&&q[1].length===0){last=true;}
|
|
71
|
+
if(!last){var qx=[];for(var j=0;j<px.length;j++){if(i!=j){qx.push(px[j]);}}
|
|
72
|
+
p=_.set(qx,d).call(this,q[1]);if(p[0].length>0){rx[0]=rx[0].concat(p[0]);rx[1]=p[1];}}
|
|
73
|
+
if(rx[1].length<best[1].length){best=rx;}
|
|
74
|
+
if(best[1].length===0){break;}}
|
|
75
|
+
if(best[0].length===0){return best;}
|
|
76
|
+
if(c){try{q=c.call(this,best[1]);}catch(ey){throw new $P.Exception(best[1]);}
|
|
77
|
+
best[1]=q[1];}
|
|
78
|
+
return best;};},forward:function(gr,fname){return function(s){return gr[fname].call(this,s);};},replace:function(rule,repl){return function(s){var r=rule.call(this,s);return[repl,r[1]];};},process:function(rule,fn){return function(s){var r=rule.call(this,s);return[fn.call(this,r[0]),r[1]];};},min:function(min,rule){return function(s){var rx=rule.call(this,s);if(rx[0].length<min){throw new $P.Exception(s);}
|
|
79
|
+
return rx;};}};var _generator=function(op){return function(){var args=null,rx=[];if(arguments.length>1){args=Array.prototype.slice.call(arguments);}else if(arguments[0]instanceof Array){args=arguments[0];}
|
|
80
|
+
if(args){for(var i=0,px=args.shift();i<px.length;i++){args.unshift(px[i]);rx.push(op.apply(null,args));args.shift();return rx;}}else{return op.apply(null,arguments);}};};var gx="optional not ignore cache".split(/\s/);for(var i=0;i<gx.length;i++){_[gx[i]]=_generator(_[gx[i]]);}
|
|
81
|
+
var _vector=function(op){return function(){if(arguments[0]instanceof Array){return op.apply(null,arguments[0]);}else{return op.apply(null,arguments);}};};var vx="each any all".split(/\s/);for(var j=0;j<vx.length;j++){_[vx[j]]=_vector(_[vx[j]]);}}());(function(){var flattenAndCompact=function(ax){var rx=[];for(var i=0;i<ax.length;i++){if(ax[i]instanceof Array){rx=rx.concat(flattenAndCompact(ax[i]));}else{if(ax[i]){rx.push(ax[i]);}}}
|
|
82
|
+
return rx;};Date.Grammar={};Date.Translator={hour:function(s){return function(){this.hour=Number(s);};},minute:function(s){return function(){this.minute=Number(s);};},second:function(s){return function(){this.second=Number(s);};},meridian:function(s){return function(){this.meridian=s.slice(0,1).toLowerCase();};},timezone:function(s){return function(){var n=s.replace(/[^\d\+\-]/g,"");if(n.length){this.timezoneOffset=Number(n);}else{this.timezone=s.toLowerCase();}};},day:function(x){var s=x[0];return function(){this.day=Number(s.match(/\d+/)[0]);};},month:function(s){return function(){this.month=((s.length==3)?Date.getMonthNumberFromName(s):(Number(s)-1));};},year:function(s){return function(){var n=Number(s);this.year=((s.length>2)?n:(n+(((n+2000)<Date.CultureInfo.twoDigitYearMax)?2000:1900)));};},rday:function(s){return function(){switch(s){case"yesterday":this.days=-1;break;case"tomorrow":this.days=1;break;case"today":this.days=0;break;case"now":this.days=0;this.now=true;break;}};},finishExact:function(x){x=(x instanceof Array)?x:[x];var now=new Date();this.year=now.getFullYear();this.month=now.getMonth();this.day=1;this.hour=0;this.minute=0;this.second=0;for(var i=0;i<x.length;i++){if(x[i]){x[i].call(this);}}
|
|
83
|
+
this.hour=(this.meridian=="p"&&this.hour<13)?this.hour+12:this.hour;if(this.day>Date.getDaysInMonth(this.year,this.month)){throw new RangeError(this.day+" is not a valid value for days.");}
|
|
84
|
+
var r=new Date(this.year,this.month,this.day,this.hour,this.minute,this.second);if(this.timezone){r.set({timezone:this.timezone});}else if(this.timezoneOffset){r.set({timezoneOffset:this.timezoneOffset});}
|
|
85
|
+
return r;},finish:function(x){x=(x instanceof Array)?flattenAndCompact(x):[x];if(x.length===0){return null;}
|
|
86
|
+
for(var i=0;i<x.length;i++){if(typeof x[i]=="function"){x[i].call(this);}}
|
|
87
|
+
if(this.now){return new Date();}
|
|
88
|
+
var today=Date.today();var method=null;var expression=!!(this.days!=null||this.orient||this.operator);if(expression){var gap,mod,orient;orient=((this.orient=="past"||this.operator=="subtract")?-1:1);if(this.weekday){this.unit="day";gap=(Date.getDayNumberFromName(this.weekday)-today.getDay());mod=7;this.days=gap?((gap+(orient*mod))%mod):(orient*mod);}
|
|
89
|
+
if(this.month){this.unit="month";gap=(this.month-today.getMonth());mod=12;this.months=gap?((gap+(orient*mod))%mod):(orient*mod);this.month=null;}
|
|
90
|
+
if(!this.unit){this.unit="day";}
|
|
91
|
+
if(this[this.unit+"s"]==null||this.operator!=null){if(!this.value){this.value=1;}
|
|
92
|
+
if(this.unit=="week"){this.unit="day";this.value=this.value*7;}
|
|
93
|
+
this[this.unit+"s"]=this.value*orient;}
|
|
94
|
+
return today.add(this);}else{if(this.meridian&&this.hour){this.hour=(this.hour<13&&this.meridian=="p")?this.hour+12:this.hour;}
|
|
95
|
+
if(this.weekday&&!this.day){this.day=(today.addDays((Date.getDayNumberFromName(this.weekday)-today.getDay()))).getDate();}
|
|
96
|
+
if(this.month&&!this.day){this.day=1;}
|
|
97
|
+
return today.set(this);}}};var _=Date.Parsing.Operators,g=Date.Grammar,t=Date.Translator,_fn;g.datePartDelimiter=_.rtoken(/^([\s\-\.\,\/\x27]+)/);g.timePartDelimiter=_.stoken(":");g.whiteSpace=_.rtoken(/^\s*/);g.generalDelimiter=_.rtoken(/^(([\s\,]|at|on)+)/);var _C={};g.ctoken=function(keys){var fn=_C[keys];if(!fn){var c=Date.CultureInfo.regexPatterns;var kx=keys.split(/\s+/),px=[];for(var i=0;i<kx.length;i++){px.push(_.replace(_.rtoken(c[kx[i]]),kx[i]));}
|
|
98
|
+
fn=_C[keys]=_.any.apply(null,px);}
|
|
99
|
+
return fn;};g.ctoken2=function(key){return _.rtoken(Date.CultureInfo.regexPatterns[key]);};g.h=_.cache(_.process(_.rtoken(/^(0[0-9]|1[0-2]|[1-9])/),t.hour));g.hh=_.cache(_.process(_.rtoken(/^(0[0-9]|1[0-2])/),t.hour));g.H=_.cache(_.process(_.rtoken(/^([0-1][0-9]|2[0-3]|[0-9])/),t.hour));g.HH=_.cache(_.process(_.rtoken(/^([0-1][0-9]|2[0-3])/),t.hour));g.m=_.cache(_.process(_.rtoken(/^([0-5][0-9]|[0-9])/),t.minute));g.mm=_.cache(_.process(_.rtoken(/^[0-5][0-9]/),t.minute));g.s=_.cache(_.process(_.rtoken(/^([0-5][0-9]|[0-9])/),t.second));g.ss=_.cache(_.process(_.rtoken(/^[0-5][0-9]/),t.second));g.hms=_.cache(_.sequence([g.H,g.mm,g.ss],g.timePartDelimiter));g.t=_.cache(_.process(g.ctoken2("shortMeridian"),t.meridian));g.tt=_.cache(_.process(g.ctoken2("longMeridian"),t.meridian));g.z=_.cache(_.process(_.rtoken(/^(\+|\-)?\s*\d\d\d\d?/),t.timezone));g.zz=_.cache(_.process(_.rtoken(/^(\+|\-)\s*\d\d\d\d/),t.timezone));g.zzz=_.cache(_.process(g.ctoken2("timezone"),t.timezone));g.timeSuffix=_.each(_.ignore(g.whiteSpace),_.set([g.tt,g.zzz]));g.time=_.each(_.optional(_.ignore(_.stoken("T"))),g.hms,g.timeSuffix);g.d=_.cache(_.process(_.each(_.rtoken(/^([0-2]\d|3[0-1]|\d)/),_.optional(g.ctoken2("ordinalSuffix"))),t.day));g.dd=_.cache(_.process(_.each(_.rtoken(/^([0-2]\d|3[0-1])/),_.optional(g.ctoken2("ordinalSuffix"))),t.day));g.ddd=g.dddd=_.cache(_.process(g.ctoken("sun mon tue wed thu fri sat"),function(s){return function(){this.weekday=s;};}));g.M=_.cache(_.process(_.rtoken(/^(1[0-2]|0\d|\d)/),t.month));g.MM=_.cache(_.process(_.rtoken(/^(1[0-2]|0\d)/),t.month));g.MMM=g.MMMM=_.cache(_.process(g.ctoken("jan feb mar apr may jun jul aug sep oct nov dec"),t.month));g.y=_.cache(_.process(_.rtoken(/^(\d\d?)/),t.year));g.yy=_.cache(_.process(_.rtoken(/^(\d\d)/),t.year));g.yyy=_.cache(_.process(_.rtoken(/^(\d\d?\d?\d?)/),t.year));g.yyyy=_.cache(_.process(_.rtoken(/^(\d\d\d\d)/),t.year));_fn=function(){return _.each(_.any.apply(null,arguments),_.not(g.ctoken2("timeContext")));};g.day=_fn(g.d,g.dd);g.month=_fn(g.M,g.MMM);g.year=_fn(g.yyyy,g.yy);g.orientation=_.process(g.ctoken("past future"),function(s){return function(){this.orient=s;};});g.operator=_.process(g.ctoken("add subtract"),function(s){return function(){this.operator=s;};});g.rday=_.process(g.ctoken("yesterday tomorrow today now"),t.rday);g.unit=_.process(g.ctoken("minute hour day week month year"),function(s){return function(){this.unit=s;};});g.value=_.process(_.rtoken(/^\d\d?(st|nd|rd|th)?/),function(s){return function(){this.value=s.replace(/\D/g,"");};});g.expression=_.set([g.rday,g.operator,g.value,g.unit,g.orientation,g.ddd,g.MMM]);_fn=function(){return _.set(arguments,g.datePartDelimiter);};g.mdy=_fn(g.ddd,g.month,g.day,g.year);g.ymd=_fn(g.ddd,g.year,g.month,g.day);g.dmy=_fn(g.ddd,g.day,g.month,g.year);g.date=function(s){return((g[Date.CultureInfo.dateElementOrder]||g.mdy).call(this,s));};g.format=_.process(_.many(_.any(_.process(_.rtoken(/^(dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|zz?z?)/),function(fmt){if(g[fmt]){return g[fmt];}else{throw Date.Parsing.Exception(fmt);}}),_.process(_.rtoken(/^[^dMyhHmstz]+/),function(s){return _.ignore(_.stoken(s));}))),function(rules){return _.process(_.each.apply(null,rules),t.finishExact);});var _F={};var _get=function(f){return _F[f]=(_F[f]||g.format(f)[0]);};g.formats=function(fx){if(fx instanceof Array){var rx=[];for(var i=0;i<fx.length;i++){rx.push(_get(fx[i]));}
|
|
100
|
+
return _.any.apply(null,rx);}else{return _get(fx);}};g._formats=g.formats(["yyyy-MM-ddTHH:mm:ss","ddd, MMM dd, yyyy H:mm:ss tt","ddd MMM d yyyy HH:mm:ss zzz","d"]);g._start=_.process(_.set([g.date,g.time,g.expression],g.generalDelimiter,g.whiteSpace),t.finish);g.start=function(s){try{var r=g._formats.call({},s);if(r[1].length===0){return r;}}catch(e){}
|
|
101
|
+
return g._start.call({},s);};}());Date._parse=Date.parse;Date.parse=function(s){var r=null;if(!s){return null;}
|
|
102
|
+
try{r=Date.Grammar.start.call({},s);}catch(e){return null;}
|
|
103
|
+
return((r[1].length===0)?r[0]:null);};Date.getParseFunction=function(fx){var fn=Date.Grammar.formats(fx);return function(s){var r=null;try{r=fn.call({},s);}catch(e){return null;}
|
|
104
|
+
return((r[1].length===0)?r[0]:null);};};Date.parseExact=function(s,fx){return Date.getParseFunction(fx)(s);};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* favcount.js v1.5.0
|
|
3
|
+
* http://chrishunt.co/favcount
|
|
4
|
+
* Dynamically updates the favicon with a number.
|
|
5
|
+
*
|
|
6
|
+
* Copyright 2013, Chris Hunt
|
|
7
|
+
* Released under the MIT license
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
(function(){
|
|
11
|
+
function Favcount(icon) {
|
|
12
|
+
this.icon = icon;
|
|
13
|
+
this.opacity = 0.4;
|
|
14
|
+
this.canvas = document.createElement('canvas');
|
|
15
|
+
this.font = "Helvetica, Arial, sans-serif";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
Favcount.prototype.set = function(count) {
|
|
19
|
+
var self = this,
|
|
20
|
+
img = document.createElement('img');
|
|
21
|
+
|
|
22
|
+
if (self.canvas.getContext) {
|
|
23
|
+
img.crossOrigin = "anonymous";
|
|
24
|
+
|
|
25
|
+
img.onload = function() {
|
|
26
|
+
drawCanvas(self.canvas, self.opacity, self.font, img, normalize(count));
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
img.src = this.icon;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function normalize(count) {
|
|
34
|
+
count = Math.round(count);
|
|
35
|
+
|
|
36
|
+
if (isNaN(count) || count < 1) {
|
|
37
|
+
return '';
|
|
38
|
+
} else if (count < 10) {
|
|
39
|
+
return ' ' + count;
|
|
40
|
+
} else if (count > 99) {
|
|
41
|
+
return '99';
|
|
42
|
+
} else {
|
|
43
|
+
return count;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function drawCanvas(canvas, opacity, font, img, count) {
|
|
48
|
+
var head = document.getElementsByTagName('head')[0],
|
|
49
|
+
favicon = document.createElement('link'),
|
|
50
|
+
multiplier, fontSize, context, xOffset, yOffset, border, shadow;
|
|
51
|
+
|
|
52
|
+
favicon.rel = 'icon';
|
|
53
|
+
|
|
54
|
+
// Scale canvas elements based on favicon size
|
|
55
|
+
multiplier = img.width / 16;
|
|
56
|
+
fontSize = multiplier * 11;
|
|
57
|
+
xOffset = multiplier;
|
|
58
|
+
yOffset = multiplier * 11;
|
|
59
|
+
border = multiplier;
|
|
60
|
+
shadow = multiplier * 2;
|
|
61
|
+
|
|
62
|
+
canvas.height = canvas.width = img.width;
|
|
63
|
+
context = canvas.getContext('2d');
|
|
64
|
+
context.font = 'bold ' + fontSize + 'px ' + font;
|
|
65
|
+
|
|
66
|
+
// Draw faded favicon background
|
|
67
|
+
if (count) { context.globalAlpha = opacity; }
|
|
68
|
+
context.drawImage(img, 0, 0);
|
|
69
|
+
context.globalAlpha = 1.0;
|
|
70
|
+
|
|
71
|
+
// Draw white drop shadow
|
|
72
|
+
context.shadowColor = '#FFF';
|
|
73
|
+
context.shadowBlur = shadow;
|
|
74
|
+
context.shadowOffsetX = 0;
|
|
75
|
+
context.shadowOffsetY = 0;
|
|
76
|
+
|
|
77
|
+
// Draw white border
|
|
78
|
+
context.fillStyle = '#FFF';
|
|
79
|
+
context.fillText(count, xOffset, yOffset);
|
|
80
|
+
context.fillText(count, xOffset + border, yOffset);
|
|
81
|
+
context.fillText(count, xOffset, yOffset + border);
|
|
82
|
+
context.fillText(count, xOffset + border, yOffset + border);
|
|
83
|
+
|
|
84
|
+
// Draw black count
|
|
85
|
+
context.fillStyle = '#000';
|
|
86
|
+
context.fillText(count,
|
|
87
|
+
xOffset + (border / 2.0),
|
|
88
|
+
yOffset + (border / 2.0)
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Replace favicon with new favicon
|
|
92
|
+
favicon.href = canvas.toDataURL('image/png');
|
|
93
|
+
head.removeChild(document.querySelector('link[rel=icon]'));
|
|
94
|
+
head.appendChild(favicon);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.Favcount = Favcount;
|
|
98
|
+
}).call(this);
|
|
99
|
+
|
|
100
|
+
(function(){
|
|
101
|
+
Favcount.VERSION = '1.5.0';
|
|
102
|
+
}).call(this);
|