UNPKG

favoritos

Version:

Favoritos is a JavaScript plugin that adds some HTML5 canvas magic to your favicon. With just a wee bit of code, we can make some really cool effects.

576 lines (496 loc) 19.1 kB
/** * * favoritos * * @version 1.1.0 * @author Alexey Istomin * @email: webistomin@gmail.com * @license: MIT * **/ 'use strict';Object.defineProperty(exports,'__esModule',{value:true});var FAVORITOS_POSITIONS = { TOP_LEFT: 'top-left', TOP_RIGHT: 'top-right', BOTTOM_LEFT: 'bottom-left', BOTTOM_RIGHT: 'bottom-right' };var FAVORITOS_SHAPES = { CIRCLE: 'circle', RECT: 'rectangle' };var SSR_MESSAGE = 'Favoritos was not initialised! Probably it is used in SSR.';var ICON_NOT_FOUND = "Favoritos: favicon element wasn't found \n Please, add <link rel=\"shortcut icon\" href=\"...\"> to head section";var DEBUG_NOT_FOUND = "Favoritos: debugger was enabled but debug element wasn't found";var DEFAULT_OPTIONS = { icon: { iconSelector: 'link[rel*="icon"]', backgroundColor: '#d21f3c', shape: FAVORITOS_SHAPES.CIRCLE, lineWidth: 4, width: 32, height: 32 }, badge: { fontSize: 18, fontFamily: 'Helvetica, Arial, sans-serif', backgroundColor: '#d21f3c', color: '#ffffff', position: FAVORITOS_POSITIONS.BOTTOM_RIGHT, shape: FAVORITOS_SHAPES.CIRCLE, minWidth: 22, minHeight: 22 }, debug: { enabled: false, debugSelector: '#favoritos-debug' } };var getContextGradientEntries = function getContextGradientEntries(backgrounds) { var arrayLength = backgrounds.length; var step = 1 / (arrayLength - 1); return backgrounds.map(function (background, index) { return [index * step, background]; }); };var getObjectKeys = Object.keys; var isObject = function isObject(item) { return item && typeof item === 'object' && !Array.isArray(item) && true; }; var mergeDeep = function mergeDeep(target, source) { var sourceKeys = getObjectKeys(source); if (isObject(target) && isObject(source)) { for (var _i2 = 0; _i2 < sourceKeys.length; _i2++) { var key = sourceKeys[_i2]; if (isObject(source[key])) { if (!target[key]) { var _Object$assign; Object.assign(target, (_Object$assign = {}, _Object$assign[key] = {}, _Object$assign)); } mergeDeep(target[key], source[key]); } else { var _Object$assign2; Object.assign(target, (_Object$assign2 = {}, _Object$assign2[key] = source[key], _Object$assign2)); } } } return target; };var loadImage = function loadImage(src, callback) { var img = new Image(); img.crossOrigin = 'anonymous'; img.addEventListener('load', function () { return callback(img); }, { once: true }); img.addEventListener('error', function () { return callback(img); }, { once: true }); img.src = src; };var roundedRect = function roundedRect(context, x, y, width, height, radius) { var rectX = x; var rectY = y; var rectWidth = width; var rectHeight = height; var cornerRadius = Math.min(Math.max(width - 1, 1), Math.max(height - 1, 1), radius); context.lineJoin = 'round'; context.lineWidth = cornerRadius; context.strokeRect(rectX + cornerRadius / 2, rectY + cornerRadius / 2, rectWidth - cornerRadius, rectHeight - cornerRadius); context.fillRect(rectX + cornerRadius / 2, rectY + cornerRadius / 2, rectWidth - cornerRadius, rectHeight - cornerRadius); context.stroke(); context.fill(); };var Favoritos = /*#__PURE__*/function () { function Favoritos(options) { if (options === void 0) { options = {}; } this.userIconCache = null; this.debugElement = null; this.arcDegrees = { '0': 0, '90': 0.5 * Math.PI, '180': Math.PI, '270': 1.5 * Math.PI, '360': 2 * Math.PI }; /** * Check SSR */ if (typeof window === 'undefined') { console.warn(SSR_MESSAGE); } else { /** * Overwrite default options to user options */ this.options = mergeDeep(DEFAULT_OPTIONS, options); this.init(); } } var _proto = Favoritos.prototype; _proto.init = function init() { var _this = this; var options = this.options; var iconOptions = options.icon, debugOptions = options.debug; var isDebugEnabled = debugOptions.enabled, debugSelector = debugOptions.debugSelector; this.iconElement = document.querySelector(iconOptions.iconSelector); if (!this.iconElement) { console.warn(ICON_NOT_FOUND); } else { this.userIconHref = this.iconElement.href; loadImage(this.userIconHref, function (img) { _this.userIconCache = img; }); } if (isDebugEnabled) { this.debugElement = document.querySelector(debugSelector); if (!this.debugElement) { console.warn(DEBUG_NOT_FOUND); } } this.initIconCanvas(); }; _proto.initIconCanvas = function initIconCanvas() { /** * Set default canvas params */ var options = this.options; var iconOptions = options.icon, badgeOptions = options.badge; var iconWidth = iconOptions.width, iconHeight = iconOptions.height; /** * Fix retina blur with DPR calculation */ var DPR = window.devicePixelRatio || 1; this.iconCanvas = document.createElement('canvas'); this.iconCanvas.width = iconWidth * DPR; this.iconCanvas.height = iconHeight * DPR; this.iconCanvasContext = this.iconCanvas.getContext('2d'); this.iconCanvasContext.font = badgeOptions.fontSize + "px " + badgeOptions.fontFamily; this.iconCanvasContext.textAlign = 'center'; this.iconCanvasContext.textBaseline = 'middle'; this.iconCanvasContext.scale(DPR, DPR); }; _proto.getContextBackgroundColor = function getContextBackgroundColor(backgroundColor, width, height) { var resultBackground; var context = this.iconCanvasContext; if (Array.isArray(backgroundColor)) { var gradient = context.createLinearGradient(0, 0, width, height); var backgroundEntries = getContextGradientEntries(backgroundColor); backgroundEntries.map(function (entry) { gradient.addColorStop(entry[0], entry[1]); }); resultBackground = gradient; } else { resultBackground = backgroundColor; } return resultBackground; }; _proto.setOptions = function setOptions(options) { this.options = mergeDeep(this.options, options); }; _proto.setIcon = function setIcon(newIcon) { this.iconElement.href = newIcon; this.userIconCache = null; }; _proto.reset = function reset() { var context = this.iconCanvasContext; var iconOptions = this.options.icon; var iconWidth = iconOptions.width, iconHeight = iconOptions.height; this.options = DEFAULT_OPTIONS; this.setIcon(this.userIconHref); context.clearRect(0, 0, iconWidth, iconHeight); this.setDebugger(); delete this.badgeContent; }; _proto.drawImage = function drawImage(content) { /** * Draw video, image, etc. to context */ var context = this.iconCanvasContext; var iconOptions = this.options.icon; var iconWidth = iconOptions.width, iconHeight = iconOptions.height; if ('crossOrigin' in content) { content.crossOrigin = 'anonymous'; } context.drawImage(content, 0, 0, iconWidth, iconHeight); this.iconElement.href = this.iconCanvas.toDataURL('image/webp', 1.0); this.setDebugger(); }; _proto.drawBadge = function drawBadge(count) { var _this2 = this; if (count === void 0) { count = ''; } var setBadge = function setBadge(img) { _this2.badgeContent = count; var newValue = count; var context = _this2.iconCanvasContext; var iconOptions = _this2.options.icon; var badgeOptions = _this2.options.badge; var badgeFontSize = badgeOptions.fontSize, badgeBackgroundColor = badgeOptions.backgroundColor, badgeShape = badgeOptions.shape, badgeColor = badgeOptions.color; var iconWidth = iconOptions.width, iconHeight = iconOptions.height; var textParams = _this2.iconCanvasContext.measureText(String(newValue)); var textWidth = textParams.width; var textHeight = badgeFontSize; context.clearRect(0, 0, iconWidth, iconHeight); context.drawImage(img, 0, 0, iconWidth, iconHeight); context.fillStyle = _this2.getContextBackgroundColor(badgeBackgroundColor, iconWidth, iconHeight); context.beginPath(); if (badgeShape === FAVORITOS_SHAPES.CIRCLE) { _this2.drawCircleBadge(textWidth, textHeight, newValue); } else { _this2.drawRectBadge(textWidth, textHeight); } context.fill(); context.fillStyle = badgeColor; context.fillText(String(newValue), _this2.getBadgeTextXPosition(textWidth), _this2.getBadgeTextYPosition(textHeight), iconWidth); context.closePath(); _this2.iconElement.href = _this2.iconCanvas.toDataURL('image/webp', 1.0); _this2.setDebugger(); }; if (!this.userIconCache) { loadImage(this.userIconHref, function (img) { _this2.userIconCache = img; setBadge(img); }); } else { setBadge(this.userIconCache); } }; _proto.drawProgressBar = function drawProgressBar(progress, shouldUseFavicon) { var _this3 = this; if (shouldUseFavicon === void 0) { shouldUseFavicon = false; } var userIconCache = this.userIconCache; var setProgress = function setProgress(img) { var context = _this3.iconCanvasContext; var iconOptions = _this3.options.icon; var iconWidth = iconOptions.width, iconHeight = iconOptions.height, iconBackgroundColor = iconOptions.backgroundColor, iconLineWidth = iconOptions.lineWidth; context.clearRect(0, 0, iconWidth, iconHeight); if (img) { context.drawImage(img, 0, 0, iconWidth, iconHeight); } context.beginPath(); context.lineWidth = iconLineWidth; if (iconOptions.shape === FAVORITOS_SHAPES.CIRCLE) { _this3.drawCircleProgressBar(progress); } else { _this3.drawRectProgressBar(progress); } context.strokeStyle = _this3.getContextBackgroundColor(iconBackgroundColor, iconWidth, iconHeight); context.stroke(); _this3.iconElement.href = _this3.iconCanvas.toDataURL('image/webp', 1.0); _this3.setDebugger(); }; if (shouldUseFavicon) { if (!userIconCache) { loadImage(this.userIconHref, function (img) { _this3.userIconCache = img; }); } else { setProgress(userIconCache); } } else { setProgress(); } }; _proto.getBadgeXPosition = function getBadgeXPosition(textWidth) { var options = this.options; var _options$badge = options.badge, badgeMinWidth = _options$badge.minWidth, badgePosition = _options$badge.position; var iconWidth = options.icon.width; var badgeShape = options.badge.shape; var badgeMaxWidth = iconWidth; var badgeValue = this.badgeContent; var shouldUseShape = typeof badgeValue === 'number' ? badgeValue >= 10 : badgeValue.length >= 1; var finalBadgeWidth = badgeMinWidth >= textWidth ? badgeMinWidth : textWidth >= badgeMaxWidth ? badgeMaxWidth : textWidth; switch (badgePosition) { case FAVORITOS_POSITIONS.TOP_LEFT: case FAVORITOS_POSITIONS.BOTTOM_LEFT: switch (badgeShape) { case FAVORITOS_SHAPES.CIRCLE: if (shouldUseShape) { return 0; } return finalBadgeWidth / 2; case FAVORITOS_SHAPES.RECT: return 0; } break; case FAVORITOS_POSITIONS.TOP_RIGHT: case FAVORITOS_POSITIONS.BOTTOM_RIGHT: switch (badgeShape) { case FAVORITOS_SHAPES.CIRCLE: if (shouldUseShape) { return iconWidth - finalBadgeWidth; } return iconWidth - finalBadgeWidth / 2; case FAVORITOS_SHAPES.RECT: return iconWidth - finalBadgeWidth; } break; } }; _proto.getBadgeYPosition = function getBadgeYPosition(textHeight) { var options = this.options; var badgeValue = this.badgeContent; var _options$badge2 = options.badge, badgePosition = _options$badge2.position, badgeMinHeight = _options$badge2.minHeight, badgeShape = _options$badge2.shape; var iconHeight = options.icon.height; var shouldUseShape = typeof badgeValue === 'number' ? badgeValue >= 10 : badgeValue.length >= 1; var finalBadgeHeight = badgeMinHeight >= textHeight ? badgeMinHeight : textHeight; switch (badgePosition) { case FAVORITOS_POSITIONS.TOP_LEFT: case FAVORITOS_POSITIONS.TOP_RIGHT: switch (badgeShape) { case FAVORITOS_SHAPES.CIRCLE: if (shouldUseShape) { return 0; } return finalBadgeHeight / 2; case FAVORITOS_SHAPES.RECT: return 0; } break; case FAVORITOS_POSITIONS.BOTTOM_LEFT: case FAVORITOS_POSITIONS.BOTTOM_RIGHT: switch (badgeShape) { case FAVORITOS_SHAPES.CIRCLE: if (shouldUseShape) { return iconHeight - finalBadgeHeight; } return iconHeight - finalBadgeHeight / 2; case FAVORITOS_SHAPES.RECT: return iconHeight - finalBadgeHeight; } } }; _proto.getBadgeTextXPosition = function getBadgeTextXPosition(textWidth) { var options = this.options; var _options$badge3 = options.badge, badgePosition = _options$badge3.position, badgeMinWidth = _options$badge3.minWidth; var iconWidth = options.icon.width; var badgeMaxWidth = iconWidth; var finalBadgeTextWidth = badgeMinWidth >= textWidth ? badgeMinWidth : textWidth >= badgeMaxWidth ? badgeMaxWidth : textWidth; switch (badgePosition) { case FAVORITOS_POSITIONS.TOP_RIGHT: case FAVORITOS_POSITIONS.BOTTOM_RIGHT: return Math.abs(iconWidth - finalBadgeTextWidth / 2); case FAVORITOS_POSITIONS.TOP_LEFT: case FAVORITOS_POSITIONS.BOTTOM_LEFT: return Math.abs(finalBadgeTextWidth / 2); } }; _proto.getBadgeTextYPosition = function getBadgeTextYPosition(textHeight) { var options = this.options; var _options$badge4 = options.badge, badgePosition = _options$badge4.position, badgeMinHeight = _options$badge4.minHeight; var iconHeight = options.icon.height; var badgeValue = this.badgeContent; var isBadgeValueNumber = typeof badgeValue === 'number'; var additionalHeight = isBadgeValueNumber ? textHeight * 0.085 : 0; var finalHeight = badgeMinHeight >= textHeight ? badgeMinHeight : textHeight; switch (badgePosition) { case FAVORITOS_POSITIONS.TOP_RIGHT: case FAVORITOS_POSITIONS.TOP_LEFT: return Math.abs(finalHeight / 2 + additionalHeight); case FAVORITOS_POSITIONS.BOTTOM_RIGHT: case FAVORITOS_POSITIONS.BOTTOM_LEFT: return Math.abs(iconHeight - finalHeight / 2 + additionalHeight); } }; _proto.drawCircleBadge = function drawCircleBadge(textWidth, textHeight, newValue) { var options = this.options; var _options$icon = options.icon, iconWidth = _options$icon.width, iconHeight = _options$icon.height; var _options$badge5 = options.badge, badgeMinWidth = _options$badge5.minWidth, badgeMinHeight = _options$badge5.minHeight, badgeBackgroundColor = _options$badge5.backgroundColor; var badgeMaxWidth = iconWidth; var context = this.iconCanvasContext; var finalBadgeWidth = badgeMinWidth >= textWidth ? badgeMinWidth : textWidth >= badgeMaxWidth ? badgeMaxWidth : textWidth; var finalBadgeHeight = badgeMinHeight >= textHeight ? badgeMinHeight : textHeight; if (typeof newValue === 'number' ? newValue >= 10 : newValue.length >= 1) { context.strokeStyle = this.getContextBackgroundColor(badgeBackgroundColor, iconWidth, iconHeight); roundedRect(context, this.getBadgeXPosition(textWidth), this.getBadgeYPosition(textHeight), finalBadgeWidth, finalBadgeHeight, 10); } else { context.arc(this.getBadgeXPosition(textWidth), this.getBadgeYPosition(textHeight), finalBadgeWidth / 2, this.arcDegrees['0'], this.arcDegrees['360']); } }; _proto.drawRectBadge = function drawRectBadge(textWidth, textHeight) { var options = this.options; var iconWidth = options.icon.width; var _options$badge6 = options.badge, badgeMinWidth = _options$badge6.minWidth, badgeMinHeight = _options$badge6.minHeight; var badgeMaxWidth = iconWidth; var finalBadgeWidth = badgeMinWidth >= textWidth ? badgeMinWidth : textWidth >= badgeMaxWidth ? badgeMaxWidth : textWidth; var finalBadgeHeight = badgeMinHeight >= textHeight ? badgeMinHeight : textHeight; this.iconCanvasContext.rect(this.getBadgeXPosition(textWidth), this.getBadgeYPosition(textHeight), finalBadgeWidth, finalBadgeHeight); }; _proto.drawCircleProgressBar = function drawCircleProgressBar(progress) { var options = this.options; var context = this.iconCanvasContext; var _options$icon2 = options.icon, iconWidth = _options$icon2.width, iconHeight = _options$icon2.height, iconLineWidth = _options$icon2.lineWidth; context.arc(iconWidth / 2, iconHeight / 2, iconWidth / 2 - iconLineWidth / 2, this.arcDegrees['270'], progress * this.arcDegrees['360'] / 100 + this.arcDegrees['270']); }; _proto.drawRectProgressBar = function drawRectProgressBar(progress) { var options = this.options; var context = this.iconCanvasContext; var _options$icon3 = options.icon, iconWidth = _options$icon3.width, iconHeight = _options$icon3.height; var step = function step() { if (progress <= 25) { context.moveTo(0, 0); context.lineTo(iconWidth / 25 * progress, 0); } else if (progress > 25 && progress <= 50) { context.moveTo(0, 0); context.lineTo(iconWidth, 0); context.moveTo(iconWidth, 0); context.lineTo(iconWidth, iconHeight / 25 * (progress - 25)); } else if (progress > 50 && progress <= 75) { context.moveTo(0, 0); context.lineTo(iconWidth, 0); context.moveTo(iconWidth, 0); context.lineTo(iconWidth, iconHeight); context.moveTo(iconWidth, iconHeight); context.lineTo(-(iconWidth / 25 * (progress - 75)), iconHeight); } else if (progress > 75 && progress <= 100) { context.moveTo(0, 0); context.lineTo(iconWidth, 0); context.moveTo(iconWidth, 0); context.lineTo(iconWidth, iconHeight); context.moveTo(iconWidth, iconHeight); context.lineTo(0, iconHeight); context.moveTo(0, iconHeight); context.lineTo(0, -(iconHeight / 25 * (progress - 100))); } }; step(); }; _proto.setDebugger = function setDebugger() { var debugOptions = this.options.debug; if (debugOptions.enabled && this.debugElement) { this.debugElement.appendChild(this.iconCanvas); } }; return Favoritos; }();exports.default=Favoritos;