favico.js源码
1 (function () {
2 var Favico = function (opt) {
3 "use strict";
4 opt = opt ? opt : {};
5 var _def = {
6 bgColor: "#d00",
7 textColor: "#fff",
8 fontFamily: "sans-serif",
9 fontStyle: "bold",
10 type: "circle",
11 position: "down",
12 animation: "slide",
13 elementId: false,
14 dataUrl: false,
15 win: window
16 };
17 var _opt, _orig, _h, _w, _canvas, _context, _img, _ready, _lastBadge, _running, _readyCb, _stop, _browser,
18 _animTimeout, _drawTimeout, _doc;
19 _browser = {};
20 _browser.ff = typeof InstallTrigger != "undefined";
21 _browser.chrome = !! window.chrome;
22 _browser.opera = !! window.opera || navigator.userAgent.indexOf("Opera") >= 0;
23 _browser.ie = false;
24 _browser.safari = Object.prototype.toString.call(window.HTMLElement).indexOf("Constructor") > 0;
25 _browser.supported = _browser.chrome || _browser.ff || _browser.opera;
26 var _queue = [];
27 _readyCb = function () {};
28 _ready = _stop = false;
29 var init = function () {
30 _opt = merge(_def, opt);
31 _opt.bgColor = hexToRgb(_opt.bgColor);
32 _opt.textColor = hexToRgb(_opt.textColor);
33 _opt.position = _opt.position.toLowerCase();
34 _opt.animation = animation.types["" + _opt.animation] ? _opt.animation : _def.animation;
35 _doc = _opt.win.document;
36 var isUp = _opt.position.indexOf("up") > -1;
37 var isLeft = _opt.position.indexOf("left") > -1;
38 if (isUp || isLeft) {
39 for (var i = 0; i < animation.types["" + _opt.animation].length; i++) {
40 var step = animation.types["" + _opt.animation][i];
41 if (isUp) {
42 if (step.y < .6) {
43 step.y = step.y - .4
44 } else {
45 step.y = step.y - 2 * step.y + (1 - step.w)
46 }
47 }
48 if (isLeft) {
49 if (step.x < .6) {
50 step.x = step.x - .4
51 } else {
52 step.x = step.x - 2 * step.x + (1 - step.h)
53 }
54 }
55 animation.types["" + _opt.animation][i] = step
56 }
57 }
58 _opt.type = type["" + _opt.type] ? _opt.type : _def.type;
59 _orig = link.getIcon();
60 _canvas = document.createElement("canvas");
61 _img = document.createElement("img");
62 if (_orig.hasAttribute("href")) {
63 _img.setAttribute("crossOrigin", "anonymous");
64 _img.onload = function () {
65 _h = _img.height > 0 ? _img.height : 32;
66 _w = _img.width > 0 ? _img.width : 32;
67 _canvas.height = _h;
68 _canvas.width = _w;
69 _context = _canvas.getContext("2d");
70 icon.ready()
71 };
72 _img.setAttribute("src", _orig.getAttribute("href"))
73 } else {
74 _img.onload = function () {
75 _h = 32;
76 _w = 32;
77 _img.height = _h;
78 _img.width = _w;
79 _canvas.height = _h;
80 _canvas.width = _w;
81 _context = _canvas.getContext("2d");
82 icon.ready()
83 };
84 _img.setAttribute("src", "")
85 }
86 };
87 var icon = {};
88 icon.ready = function () {
89 _ready = true;
90 icon.reset();
91 _readyCb()
92 };
93 icon.reset = function () {
94 if (!_ready) {
95 return
96 }
97 _queue = [];
98 _lastBadge = false;
99 _running = false;
100 _context.clearRect(0, 0, _w, _h);
101 _context.drawImage(_img, 0, 0, _w, _h);
102 link.setIcon(_canvas);
103 window.clearTimeout(_animTimeout);
104 window.clearTimeout(_drawTimeout)
105 };
106 icon.start = function () {
107 if (!_ready || _running) {
108 return
109 }
110 var finished = function () {
111 _lastBadge = _queue[0];
112 _running = false;
113 if (_queue.length > 0) {
114 _queue.shift();
115 icon.start()
116 } else {}
117 };
118 if (_queue.length > 0) {
119 _running = true;
120 var run = function () {
121 ["type", "animation", "bgColor", "textColor", "fontFamily", "fontStyle"].forEach(function (a) {
122 if (a in _queue[0].options) {
123 _opt[a] = _queue[0].options[a]
124 }
125 });
126 animation.run(_queue[0].options, function () {
127 finished()
128 }, false)
129 };
130 if (_lastBadge) {
131 animation.run(_lastBadge.options, function () {
132 run()
133 }, true)
134 } else {
135 run()
136 }
137 }
138 };
139 var type = {};
140 var options = function (opt) {
141 opt.n = typeof opt.n === "number" ? Math.abs(opt.n | 0) : opt.n;
142 opt.x = _w * opt.x;
143 opt.y = _h * opt.y;
144 opt.w = _w * opt.w;
145 opt.h = _h * opt.h;
146 opt.len = ("" + opt.n).length;
147 return opt
148 };
149 type.circle = function (opt) {
150 opt = options(opt);
151 var more = false;
152 if (opt.len === 2) {
153 opt.x = opt.x - opt.w * .4;
154 opt.w = opt.w * 1.4;
155 more = true
156 } else if (opt.len >= 3) {
157 opt.x = opt.x - opt.w * .65;
158 opt.w = opt.w * 1.65;
159 more = true
160 }
161 _context.clearRect(0, 0, _w, _h);
162 _context.drawImage(_img, 0, 0, _w, _h);
163 _context.beginPath();
164 _context.font = _opt.fontStyle + " " + Math.floor(opt.h * (opt.n > 99 ? .85 : 1)) + "px " + _opt.fontFamily;
165 _context.textAlign = "center";
166 if (more) {
167 _context.moveTo(opt.x + opt.w / 2, opt.y);
168 _context.lineTo(opt.x + opt.w - opt.h / 2, opt.y);
169 _context.quadraticCurveTo(opt.x + opt.w, opt.y, opt.x + opt.w, opt.y + opt.h / 2);
170 _context.lineTo(opt.x + opt.w, opt.y + opt.h - opt.h / 2);
171 _context.quadraticCurveTo(opt.x + opt.w, opt.y + opt.h, opt.x + opt.w - opt.h / 2, opt.y + opt.h);
172 _context.lineTo(opt.x + opt.h / 2, opt.y + opt.h);
173 _context.quadraticCurveTo(opt.x, opt.y + opt.h, opt.x, opt.y + opt.h - opt.h / 2);
174 _context.lineTo(opt.x, opt.y + opt.h / 2);
175 _context.quadraticCurveTo(opt.x, opt.y, opt.x + opt.h / 2, opt.y)
176 } else {
177 _context.arc(opt.x + opt.w / 2, opt.y + opt.h / 2, opt.h / 2, 0, 2 * Math.PI)
178 }
179 _context.fillStyle = "rgba(" + _opt.bgColor.r + "," + _opt.bgColor.g + "," + _opt.bgColor.b + "," + opt.o +
180 ")";
181 _context.fill();
182 _context.closePath();
183 _context.beginPath();
184 _context.stroke();
185 _context.fillStyle = "rgba(" + _opt.textColor.r + "," + _opt.textColor.g + "," + _opt.textColor.b + "," +
186 opt.o + ")";
187 if (typeof opt.n === "number" && opt.n > 999) {
188 _context.fillText((opt.n > 9999 ? 9 : Math.floor(opt.n / 1e3)) + "k+", Math.floor(opt.x + opt.w / 2),
189 Math.floor(opt.y + opt.h - opt.h * .2))
190 } else {
191 _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * .15))
192 }
193 _context.closePath()
194 };
195 type.rectangle = function (opt) {
196 opt = options(opt);
197 var more = false;
198 if (opt.len === 2) {
199 opt.x = opt.x - opt.w * .4;
200 opt.w = opt.w * 1.4;
201 more = true
202 } else if (opt.len >= 3) {
203 opt.x = opt.x - opt.w * .65;
204 opt.w = opt.w * 1.65;
205 more = true
206 }
207 _context.clearRect(0, 0, _w, _h);
208 _context.drawImage(_img, 0, 0, _w, _h);
209 _context.beginPath();
210 _context.font = _opt.fontStyle + " " + Math.floor(opt.h * (opt.n > 99 ? .9 : 1)) + "px " + _opt.fontFamily;
211 _context.textAlign = "center";
212 _context.fillStyle = "rgba(" + _opt.bgColor.r + "," + _opt.bgColor.g + "," + _opt.bgColor.b + "," + opt.o +
213 ")";
214 _context.fillRect(opt.x, opt.y, opt.w, opt.h);
215 _context.fillStyle = "rgba(" + _opt.textColor.r + "," + _opt.textColor.g + "," + _opt.textColor.b + "," +
216 opt.o + ")";
217 if (typeof opt.n === "number" && opt.n > 999) {
218 _context.fillText((opt.n > 9999 ? 9 : Math.floor(opt.n / 1e3)) + "k+", Math.floor(opt.x + opt.w / 2),
219 Math.floor(opt.y + opt.h - opt.h * .2))
220 } else {
221 _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * .15))
222 }
223 _context.closePath()
224 };
225 var badge = function (number, opts) {
226 opts = (typeof opts === "string" ? {
227 animation: opts
228 } : opts) || {};
229 _readyCb = function () {
230 try {
231 if (typeof number === "number" ? number > 0 : number !== "") {
232 var q = {
233 type: "badge",
234 options: {
235 n: number
236 }
237 };
238 if ("animation" in opts && animation.types["" + opts.animation]) {
239 q.options.animation = "" + opts.animation
240 }
241 if ("type" in opts && type["" + opts.type]) {
242 q.options.type = "" + opts.type
243 }["bgColor", "textColor"].forEach(function (o) {
244 if (o in opts) {
245 q.options[o] = hexToRgb(opts[o])
246 }
247 });
248 ["fontStyle", "fontFamily"].forEach(function (o) {
249 if (o in opts) {
250 q.options[o] = opts[o]
251 }
252 });
253 _queue.push(q);
254 if (_queue.length > 100) {
255 throw new Error("Too many badges requests in queue.")
256 }
257 icon.start()
258 } else {
259 icon.reset()
260 }
261 } catch (e) {
262 throw new Error("Error setting badge. Message: " + e.message)
263 }
264 };
265 if (_ready) {
266 _readyCb()
267 }
268 };
269 var image = function (imageElement) {
270 _readyCb = function () {
271 try {
272 var w = imageElement.width;
273 var h = imageElement.height;
274 var newImg = document.createElement("img");
275 var ratio = w / _w < h / _h ? w / _w : h / _h;
276 newImg.setAttribute("crossOrigin", "anonymous");
277 newImg.onload = function () {
278 _context.clearRect(0, 0, _w, _h);
279 _context.drawImage(newImg, 0, 0, _w, _h);
280 link.setIcon(_canvas)
281 };
282 newImg.setAttribute("src", imageElement.getAttribute("src"));
283 newImg.height = h / ratio;
284 newImg.width = w / ratio
285 } catch (e) {
286 throw new Error("Error setting image. Message: " + e.message)
287 }
288 };
289 if (_ready) {
290 _readyCb()
291 }
292 };
293 var video = function (videoElement) {
294 _readyCb = function () {
295 try {
296 if (videoElement === "stop") {
297 _stop = true;
298 icon.reset();
299 _stop = false;
300 return
301 }
302 videoElement.addEventListener("play", function () {
303 drawVideo(this)
304 }, false)
305 } catch (e) {
306 throw new Error("Error setting video. Message: " + e.message)
307 }
308 };
309 if (_ready) {
310 _readyCb()
311 }
312 };
313 var webcam = function (action) {
314 if (!window.URL || !window.URL.createObjectURL) {
315 window.URL = window.URL || {};
316 window.URL.createObjectURL = function (obj) {
317 return obj
318 }
319 }
320 if (_browser.supported) {
321 var newVideo = false;
322 navigator.getUserMedia = navigator.getUserMedia || navigator.oGetUserMedia || navigator.msGetUserMedia ||
323 navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
324 _readyCb = function () {
325 try {
326 if (action === "stop") {
327 _stop = true;
328 icon.reset();
329 _stop = false;
330 return
331 }
332 newVideo = document.createElement("video");
333 newVideo.width = _w;
334 newVideo.height = _h;
335 navigator.getUserMedia({
336 video: true,
337 audio: false
338 }, function (stream) {
339 newVideo.src = URL.createObjectURL(stream);
340 newVideo.play();
341 drawVideo(newVideo)
342 }, function () {})
343 } catch (e) {
344 throw new Error("Error setting webcam. Message: " + e.message)
345 }
346 };
347 if (_ready) {
348 _readyCb()
349 }
350 }
351 };
352
353 function drawVideo(video) {
354 if (video.paused || video.ended || _stop) {
355 return false
356 }
357 try {
358 _context.clearRect(0, 0, _w, _h);
359 _context.drawImage(video, 0, 0, _w, _h)
360 } catch (e) {}
361 _drawTimeout = setTimeout(function () {
362 drawVideo(video)
363 }, animation.duration);
364 link.setIcon(_canvas)
365 }
366 var link = {};
367 link.getIcon = function () {
368 var elm = false;
369 var getLink = function () {
370 var link = _doc.getElementsByTagName("head")[0].getElementsByTagName("link");
371 for (var l = link.length, i = l - 1; i >= 0; i--) {
372 if (/(^|s)icon(s|$)/i.test(link[i].getAttribute("rel"))) {
373 return link[i]
374 }
375 }
376 return false
377 };
378 if (_opt.element) {
379 elm = _opt.element
380 } else if (_opt.elementId) {
381 elm = _doc.getElementById(_opt.elementId);
382 elm.setAttribute("href", elm.getAttribute("src"))
383 } else {
384 elm = getLink();
385 if (elm === false) {
386 elm = _doc.createElement("link");
387 elm.setAttribute("rel", "icon");
388 _doc.getElementsByTagName("head")[0].appendChild(elm)
389 }
390 }
391 elm.setAttribute("type", "image/png");
392 return elm
393 };
394 link.setIcon = function (canvas) {
395 var url = canvas.toDataURL("image/png");
396 if (_opt.dataUrl) {
397 _opt.dataUrl(url)
398 }
399 if (_opt.element) {
400 _opt.element.setAttribute("href", url);
401 _opt.element.setAttribute("src", url)
402 } else if (_opt.elementId) {
403 var elm = _doc.getElementById(_opt.elementId);
404 elm.setAttribute("href", url);
405 elm.setAttribute("src", url)
406 } else {
407 if (_browser.ff || _browser.opera) {
408 var old = _orig;
409 _orig = _doc.createElement("link");
410 if (_browser.opera) {
411 _orig.setAttribute("rel", "icon")
412 }
413 _orig.setAttribute("rel", "icon");
414 _orig.setAttribute("type", "image/png");
415 _doc.getElementsByTagName("head")[0].appendChild(_orig);
416 _orig.setAttribute("href", url);
417 if (old.parentNode) {
418 old.parentNode.removeChild(old)
419 }
420 } else {
421 _orig.setAttribute("href", url)
422 }
423 }
424 };
425
426 function hexToRgb(hex) {
427 var shorthandRegex = /^#?([a-fd])([a-fd])([a-fd])$/i;
428 hex = hex.replace(shorthandRegex, function (m, r, g, b) {
429 return r + r + g + g + b + b
430 });
431 var result = /^#?([a-fd]{2})([a-fd]{2})([a-fd]{2})$/i.exec(hex);
432 return result ? {
433 r: parseInt(result[1], 16),
434 g: parseInt(result[2], 16),
435 b: parseInt(result[3], 16)
436 } : false
437 }
438 function merge(def, opt) {
439 var mergedOpt = {};
440 var attrname;
441 for (attrname in def) {
442 mergedOpt[attrname] = def[attrname]
443 }
444 for (attrname in opt) {
445 mergedOpt[attrname] = opt[attrname]
446 }
447 return mergedOpt
448 }
449 function isPageHidden() {
450 return _doc.hidden || _doc.msHidden || _doc.webkitHidden || _doc.mozHidden
451 }
452 var animation = {};
453 animation.duration = 40;
454 animation.types = {};
455 animation.types.fade = [{
456 x: .4,
457 y: .4,
458 w: .6,
459 h: .6,
460 o: 0
461 }, {
462 x: .4,
463 y: .4,
464 w: .6,
465 h: .6,
466 o: .1
467 }, {
468 x: .4,
469 y: .4,
470 w: .6,
471 h: .6,
472 o: .2
473 }, {
474 x: .4,
475 y: .4,
476 w: .6,
477 h: .6,
478 o: .3
479 }, {
480 x: .4,
481 y: .4,
482 w: .6,
483 h: .6,
484 o: .4
485 }, {
486 x: .4,
487 y: .4,
488 w: .6,
489 h: .6,
490 o: .5
491 }, {
492 x: .4,
493 y: .4,
494 w: .6,
495 h: .6,
496 o: .6
497 }, {
498 x: .4,
499 y: .4,
500 w: .6,
501 h: .6,
502 o: .7
503 }, {
504 x: .4,
505 y: .4,
506 w: .6,
507 h: .6,
508 o: .8
509 }, {
510 x: .4,
511 y: .4,
512 w: .6,
513 h: .6,
514 o: .9
515 }, {
516 x: .4,
517 y: .4,
518 w: .6,
519 h: .6,
520 o: 1
521 }];
522 animation.types.none = [{
523 x: .4,
524 y: .4,
525 w: .6,
526 h: .6,
527 o: 1
528 }];
529 animation.types.pop = [{
530 x: 1,
531 y: 1,
532 w: 0,
533 h: 0,
534 o: 1
535 }, {
536 x: .9,
537 y: .9,
538 w: .1,
539 h: .1,
540 o: 1
541 }, {
542 x: .8,
543 y: .8,
544 w: .2,
545 h: .2,
546 o: 1
547 }, {
548 x: .7,
549 y: .7,
550 w: .3,
551 h: .3,
552 o: 1
553 }, {
554 x: .6,
555 y: .6,
556 w: .4,
557 h: .4,
558 o: 1
559 }, {
560 x: .5,
561 y: .5,
562 w: .5,
563 h: .5,
564 o: 1
565 }, {
566 x: .4,
567 y: .4,
568 w: .6,
569 h: .6,
570 o: 1
571 }];
572 animation.types.popFade = [{
573 x: .75,
574 y: .75,
575 w: 0,
576 h: 0,
577 o: 0
578 }, {
579 x: .65,
580 y: .65,
581 w: .1,
582 h: .1,
583 o: .2
584 }, {
585 x: .6,
586 y: .6,
587 w: .2,
588 h: .2,
589 o: .4
590 }, {
591 x: .55,
592 y: .55,
593 w: .3,
594 h: .3,
595 o: .6
596 }, {
597 x: .5,
598 y: .5,
599 w: .4,
600 h: .4,
601 o: .8
602 }, {
603 x: .45,
604 y: .45,
605 w: .5,
606 h: .5,
607 o: .9
608 }, {
609 x: .4,
610 y: .4,
611 w: .6,
612 h: .6,
613 o: 1
614 }];
615 animation.types.slide = [{
616 x: .4,
617 y: 1,
618 w: .6,
619 h: .6,
620 o: 1
621 }, {
622 x: .4,
623 y: .9,
624 w: .6,
625 h: .6,
626 o: 1
627 }, {
628 x: .4,
629 y: .9,
630 w: .6,
631 h: .6,
632 o: 1
633 }, {
634 x: .4,
635 y: .8,
636 w: .6,
637 h: .6,
638 o: 1
639 }, {
640 x: .4,
641 y: .7,
642 w: .6,
643 h: .6,
644 o: 1
645 }, {
646 x: .4,
647 y: .6,
648 w: .6,
649 h: .6,
650 o: 1
651 }, {
652 x: .4,
653 y: .5,
654 w: .6,
655 h: .6,
656 o: 1
657 }, {
658 x: .4,
659 y: .4,
660 w: .6,
661 h: .6,
662 o: 1
663 }];
664 animation.run = function (opt, cb, revert, step) {
665 var animationType = animation.types[isPageHidden() ? "none" : _opt.animation];
666 if (revert === true) {
667 step = typeof step !== "undefined" ? step : animationType.length - 1
668 } else {
669 step = typeof step !== "undefined" ? step : 0
670 }
671 cb = cb ? cb : function () {};
672 if (step < animationType.length && step >= 0) {
673 type[_opt.type](merge(opt, animationType[step]));
674 _animTimeout = setTimeout(function () {
675 if (revert) {
676 step = step - 1
677 } else {
678 step = step + 1
679 }
680 animation.run(opt, cb, revert, step)
681 }, animation.duration);
682 link.setIcon(_canvas)
683 } else {
684 cb();
685 return
686 }
687 };
688 init();
689 return {
690 badge: badge,
691 video: video,
692 image: image,
693 webcam: webcam,
694 reset: icon.reset,
695 browser: {
696 supported: _browser.supported
697 }
698 }
699 };
700 if (typeof define !== "undefined" && define.amd) {
701 define([], function () {
702 return Favico
703 })
704 } else if (typeof module !== "undefined" && module.exports) {
705 module.exports = Favico
706 } else {
707 this.Favico = Favico
708 }
709 })();
favico.js cdn
http://www.bootcdn.cn/favico.js/
favioc.js 使用