UNPKG

@gravatar-com/hovercards

Version:

Add profile hovercards to Gravatar images.

559 lines (539 loc) 25.1 kB
/******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ // The require scope /******/ var __webpack_require__ = {}; /******/ /************************************************************************/ /******/ /* webpack/runtime/define property getters */ /******/ (() => { /******/ // define getter functions for harmony exports /******/ __webpack_require__.d = (exports, definition) => { /******/ for(var key in definition) { /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); /******/ } /******/ } /******/ }; /******/ })(); /******/ /******/ /* webpack/runtime/hasOwnProperty shorthand */ /******/ (() => { /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) /******/ })(); /******/ /******/ /* webpack/runtime/make namespace object */ /******/ (() => { /******/ // define __esModule on exports /******/ __webpack_require__.r = (exports) => { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ })(); /******/ /************************************************************************/ var __webpack_exports__ = {}; // ESM COMPAT FLAG __webpack_require__.r(__webpack_exports__); // EXPORTS __webpack_require__.d(__webpack_exports__, { Hovercards: () => (/* reexport */ Hovercards) }); ;// CONCATENATED MODULE: ./src/compute-position.ts var paddingMap = { top: 'paddingBottom', bottom: 'paddingTop', left: 'paddingRight', right: 'paddingLeft' }; /** * Computes the position of a card relative to a ref element. * * @param {HTMLElement} ref - The ref element. * @param {HTMLDivElement} card - The card element. * @param {Options} [options={}] - The placement, offset, and auto-flip options. * @return {ReturnValues} - The computed position values. */ function computingPosition(ref, card, _temp) { var _ref = _temp === void 0 ? {} : _temp, _ref$placement = _ref.placement, placement = _ref$placement === void 0 ? 'right' : _ref$placement, _ref$offset = _ref.offset, offset = _ref$offset === void 0 ? 0 : _ref$offset, _ref$autoFlip = _ref.autoFlip, autoFlip = _ref$autoFlip === void 0 ? true : _ref$autoFlip; var refRect = ref.getBoundingClientRect(); var cardRect = card.getBoundingClientRect(); var refScrollT = refRect.top + scrollY; var refScrollB = refRect.bottom + scrollY; var refScrollR = refRect.right + scrollX; var refScrollL = refRect.left + scrollX; var x = 0; var y = 0; var _placement$split = placement.split('-'), dir = _placement$split[0], align = _placement$split[1]; offset = Math.max(0, offset); // Auto flip the card if there's not enough space // If both sides have not enough space, then the card will be placed on the side with more space if (autoFlip) { var topSpace = refRect.top; var bottomSpace = innerHeight - refRect.bottom; var leftSpace = refRect.left; var rightSpace = innerWidth - refRect.right; var floatingSpaceV = cardRect.height + offset; var floatingSpaceH = cardRect.width + offset; if (dir === 'top' && topSpace < floatingSpaceV && bottomSpace > topSpace) { dir = 'bottom'; } if (dir === 'bottom' && bottomSpace < floatingSpaceV && topSpace > bottomSpace) { dir = 'top'; } if (dir === 'left' && leftSpace < floatingSpaceH && rightSpace > leftSpace) { dir = 'right'; } if (dir === 'right' && rightSpace < floatingSpaceH && leftSpace > rightSpace) { dir = 'left'; } } // Calculate the position of the card if (dir === 'top' || dir === 'bottom') { x = refScrollL + refRect.width / 2 - cardRect.width / 2; // The bottom offset will be filled with the card's padding y = dir === 'top' ? refScrollT - cardRect.height - offset : refScrollB; if (align === 'start') { x = refScrollL; } if (align === 'end') { x = refScrollR - cardRect.width; } } else { // The right offset will be filled with the card's padding x = dir === 'right' ? refScrollR : refScrollL - cardRect.width - offset; y = refScrollT + refRect.height / 2 - cardRect.height / 2; if (align === 'start') { y = refScrollT; } if (align === 'end') { y = refScrollB - cardRect.height; } } return { x: x, y: y, padding: paddingMap[dir], paddingValue: offset }; } ;// CONCATENATED MODULE: ./src/sanitizer.ts function escHtml(str) { var htmlEntities = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;', '`': '&#x60;' }; // Don't escape if already escaped. return str.replace(/&(amp|lt|gt|quot|#39|x60);|[\&<>"'`]/g, function (match) { return match[0] === '&' ? match : htmlEntities[match]; }); } function escUrl(url) { return encodeURI(url); } ;// CONCATENATED MODULE: ./src/i18n.ts function __(i18n, key) { return i18n[key] || key; } ;// CONCATENATED MODULE: ./src/core.ts function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } var BASE_API_URL = 'https://secure.gravatar.com'; var socialLinksOrder = ['wordpress', 'mastodon', 'tumblr', 'github', 'twitter']; var dc = document; var Hovercards = /*#__PURE__*/function () { function Hovercards(_temp) { var _this = this; var _ref = _temp === void 0 ? {} : _temp, _ref$placement = _ref.placement, placement = _ref$placement === void 0 ? 'right' : _ref$placement, _ref$autoFlip = _ref.autoFlip, autoFlip = _ref$autoFlip === void 0 ? true : _ref$autoFlip, _ref$offset = _ref.offset, offset = _ref$offset === void 0 ? 10 : _ref$offset, _ref$delayToShow = _ref.delayToShow, delayToShow = _ref$delayToShow === void 0 ? 500 : _ref$delayToShow, _ref$delayToHide = _ref.delayToHide, delayToHide = _ref$delayToHide === void 0 ? 300 : _ref$delayToHide, _ref$additionalClass = _ref.additionalClass, additionalClass = _ref$additionalClass === void 0 ? '' : _ref$additionalClass, _ref$myHash = _ref.myHash, myHash = _ref$myHash === void 0 ? '' : _ref$myHash, _ref$onQueryHovercard = _ref.onQueryHovercardRef, onQueryHovercardRef = _ref$onQueryHovercard === void 0 ? function (ref) { return ref; } : _ref$onQueryHovercard, _ref$onFetchProfileSt = _ref.onFetchProfileStart, onFetchProfileStart = _ref$onFetchProfileSt === void 0 ? function () {} : _ref$onFetchProfileSt, _ref$onFetchProfileSu = _ref.onFetchProfileSuccess, onFetchProfileSuccess = _ref$onFetchProfileSu === void 0 ? function () {} : _ref$onFetchProfileSu, _ref$onFetchProfileFa = _ref.onFetchProfileFailure, onFetchProfileFailure = _ref$onFetchProfileFa === void 0 ? function () {} : _ref$onFetchProfileFa, _ref$onHovercardShown = _ref.onHovercardShown, onHovercardShown = _ref$onHovercardShown === void 0 ? function () {} : _ref$onHovercardShown, _ref$onHovercardHidde = _ref.onHovercardHidden, onHovercardHidden = _ref$onHovercardHidde === void 0 ? function () {} : _ref$onHovercardHidde, _ref$i18n = _ref.i18n, i18n = _ref$i18n === void 0 ? {} : _ref$i18n; // Options this._i18n = {}; // Variables this._hovercardRefs = []; this._showHovercardTimeoutIds = new Map(); this._hideHovercardTimeoutIds = new Map(); this._cachedProfiles = new Map(); /** * Attaches event listeners on or within the target element. * * @param {HTMLElement} target - The target element to set. * @param {Object} [options={}] - The optional parameters. * @param options.dataAttributeName - Data attribute name associated with Gravatar hashes. * @param options.ignoreSelector - The selector to ignore certain elements. * @return {void} */ this.attach = function (target, _temp2) { var _ref2 = _temp2 === void 0 ? {} : _temp2, _ref2$dataAttributeNa = _ref2.dataAttributeName, dataAttributeName = _ref2$dataAttributeNa === void 0 ? 'gravatar-hash' : _ref2$dataAttributeNa, ignoreSelector = _ref2.ignoreSelector; if (!target) { return; } _this.detach(); _this._queryHovercardRefs(target, dataAttributeName, ignoreSelector).forEach(function (hovercardRef) { hovercardRef.ref.addEventListener('mouseenter', function (e) { return _this._handleMouseEnter(e, hovercardRef); }); hovercardRef.ref.addEventListener('mouseleave', function (e) { return _this._handleMouseLeave(e, hovercardRef); }); }); }; /** * Removes event listeners from hovercard refs and resets the stored list of these refs. * * @return {void} */ this.detach = function () { if (!_this._hovercardRefs.length) { return; } _this._hovercardRefs.forEach(function (_ref3) { var ref = _ref3.ref; ref.removeEventListener('mouseenter', function () { return _this._handleMouseEnter; }); ref.removeEventListener('mouseleave', function () { return _this._handleMouseLeave; }); }); _this._hovercardRefs = []; }; this._placement = placement; this._autoFlip = autoFlip; this._offset = offset; this._delayToShow = delayToShow; this._delayToHide = delayToHide; this._additionalClass = additionalClass; this._myHash = myHash; this._onQueryHovercardRef = onQueryHovercardRef; this._onFetchProfileStart = onFetchProfileStart; this._onFetchProfileSuccess = onFetchProfileSuccess; this._onFetchProfileFailure = onFetchProfileFailure; this._onHovercardShown = onHovercardShown; this._onHovercardHidden = onHovercardHidden; this._i18n = i18n; } /** * Queries hovercard refs on or within the target element * * @param {HTMLElement} target - The element to query. * @param {string} dataAttributeName - Data attribute name associated with Gravatar hashes. * @param {string} [ignoreSelector] - The selector to ignore certain elements. * @return {HTMLElement[]} - The queried hovercard refs. * @private */ var _proto = Hovercards.prototype; _proto._queryHovercardRefs = function _queryHovercardRefs(target, dataAttributeName, ignoreSelector) { var _this2 = this; var refs = []; var camelAttrName = dataAttributeName.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); }); var ignoreRefs = ignoreSelector ? Array.from(dc.querySelectorAll(ignoreSelector)) : []; var matchPath = 'gravatar.com/avatar/'; if (target.dataset[camelAttrName] || target.tagName === 'IMG' && target.src.includes(matchPath)) { refs = [target]; } else { refs = Array.from(target.querySelectorAll("img[src*=\"" + matchPath + "\"]")); if (dataAttributeName) { refs = [].concat(refs.filter(function (img) { return !img.hasAttribute("data-" + dataAttributeName); }), Array.from(target.querySelectorAll("[data-" + dataAttributeName + "]"))); } } this._hovercardRefs = refs.map(function (ref, idx) { if (ignoreRefs.includes(ref)) { return null; } var hash; var params; var dataAttrValue = ref.dataset[camelAttrName]; if (dataAttrValue) { hash = dataAttrValue.split('?')[0]; params = dataAttrValue; } else if (ref.tagName === 'IMG') { hash = ref.src.split('/').pop().split('?')[0]; params = ref.src; } if (!hash) { return null; } var p = new URLSearchParams(params); var d = p.get('d') || p.get('default'); var f = p.get('f') || p.get('forcedefault'); var r = p.get('r') || p.get('rating'); params = [d && "d=" + d, f && "f=" + f, r && "r=" + r].filter(Boolean).join('&'); return { id: "gravatar-hovercard-" + hash + "-" + idx, hash: hash, params: params ? "?" + params : '', ref: _this2._onQueryHovercardRef(ref) || ref }; }).filter(Boolean); return this._hovercardRefs; } /** * Creates a skeleton hovercard element. * * @return {HTMLDivElement} The created skeleton hovercard element. */; _proto._createHovercardSkeleton = function _createHovercardSkeleton() { var hovercard = dc.createElement('div'); hovercard.className = "gravatar-hovercard gravatar-hovercard--skeleton" + (this._additionalClass ? " " + this._additionalClass : ''); hovercard.innerHTML = "\n\t\t\t<div class=\"gravatar-hovercard__inner\">\n\t\t\t\t<div class=\"gravatar-hovercard__header\">\n\t\t\t\t\t<div class=\"gravatar-hovercard__avatar-link\"></div>\n\t\t\t\t\t<div class=\"gravatar-hovercard__name-location-link\"></div>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"gravatar-hovercard__footer\">\n\t\t\t\t\t<div class=\"gravatar-hovercard__social-link\"></div>\n\t\t\t\t\t<div class=\"gravatar-hovercard__profile-link\"\"></div>\n\t\t\t\t</div>\n\t\t\t</div>\n "; return hovercard; } /** * Creates a hovercard element with the provided profile data. * * @param {ProfileData} profileData - The profile data to populate the hovercard. * @param {Object} [options] - Optional parameters for the hovercard. * @param {string} [options.additionalClass] - Additional CSS class for the hovercard. * @param {string} [options.myHash] - The hash of the current user. * @param {Object} [options.i18n] - The i18n object. * @return {HTMLDivElement} - The created hovercard element. */; /** * Waits for a specified delay and fetches the user's profile data, * then shows the hovercard relative to the ref element. * * @param {HovercardRef} hovercardRef - The hovercard ref object. * @return {void} * @private */ _proto._showHovercard = function _showHovercard(_ref4) { var _this3 = this; var id = _ref4.id, hash = _ref4.hash, params = _ref4.params, ref = _ref4.ref; var timeoutId = setTimeout(function () { if (dc.getElementById(id)) { return; } var hovercard; if (_this3._cachedProfiles.has(hash)) { var profile = _this3._cachedProfiles.get(hash); hovercard = Hovercards.createHovercard(_extends({}, profile, { thumbnailUrl: profile.thumbnailUrl + params }), { additionalClass: _this3._additionalClass, myHash: _this3._myHash, i18n: _this3._i18n }); } else { hovercard = _this3._createHovercardSkeleton(); _this3._onFetchProfileStart(hash); fetch(BASE_API_URL + "/" + hash + ".json").then(function (res) { return res.json(); }).then(function (data) { // API error handling if (!(data != null && data.entry)) { // The data will be an error message throw new Error(data); } var _data$entry$ = data.entry[0], fetchedHash = _data$entry$.hash, thumbnailUrl = _data$entry$.thumbnailUrl, preferredUsername = _data$entry$.preferredUsername, displayName = _data$entry$.displayName, currentLocation = _data$entry$.currentLocation, aboutMe = _data$entry$.aboutMe, accounts = _data$entry$.accounts; _this3._cachedProfiles.set(hash, { hash: fetchedHash, thumbnailUrl: thumbnailUrl, preferredUsername: preferredUsername, displayName: displayName, currentLocation: currentLocation, aboutMe: aboutMe, accounts: accounts == null ? void 0 : accounts.map(function (_ref5) { var url = _ref5.url, shortname = _ref5.shortname, iconUrl = _ref5.iconUrl, name = _ref5.name; return { url: url, shortname: shortname, iconUrl: iconUrl, name: name }; }) }); var profile = _this3._cachedProfiles.get(hash); var hovercardInner = Hovercards.createHovercard(_extends({}, profile, { thumbnailUrl: profile.thumbnailUrl + params }), { additionalClass: _this3._additionalClass, myHash: _this3._myHash, i18n: _this3._i18n }).firstElementChild; hovercard.classList.remove('gravatar-hovercard--skeleton'); hovercard.replaceChildren(hovercardInner); _this3._onFetchProfileSuccess(hash, _this3._cachedProfiles.get(hash)); })["catch"](function (error) { hovercard.firstElementChild.classList.add('gravatar-hovercard__inner--error'); hovercard.firstElementChild.innerHTML = "\n\t\t\t\t\t\t\t<img class=\"gravatar-hovercard__avatar\" src=\"https://2.gravatar.com/avatar/" + hash + params + "\" width=\"56\" height=\"56\" alt=\"Avatar\" />\n\t\t\t\t\t\t\t<i class=\"gravatar-hovercard__error-message\">" + (error.message === 'User not found' ? __(_this3._i18n, 'Sorry, we are unable to load this Gravatar profile.') : __(_this3._i18n, 'Sorry, we are unable to load this Gravatar profile. Please check your internet connection.')) + "</i>\n\t\t\t\t\t\t"; _this3._onFetchProfileFailure(hash, error); }); } // Set the hovercard ID here to avoid the show / hide side effect hovercard.id = id; // Don't hide the hovercard when the mouse is over the hovercard from the ref hovercard.addEventListener('mouseenter', function () { return clearInterval(_this3._hideHovercardTimeoutIds.get(id)); }); hovercard.addEventListener('mouseleave', function () { return _this3._hideHovercard(id); }); // Placing the hovercard at the top-level of the dc to avoid being clipped by overflow dc.body.appendChild(hovercard); var _computePosition = computingPosition(ref, hovercard, { placement: _this3._placement, offset: _this3._offset, autoFlip: _this3._autoFlip }), x = _computePosition.x, y = _computePosition.y, padding = _computePosition.padding, paddingValue = _computePosition.paddingValue; hovercard.style.position = 'absolute'; hovercard.style.left = x + "px"; hovercard.style.top = y + "px"; // To bridge the gap between the ref and the hovercard, // ensuring that the hovercard remains visible when the mouse hovers over the gap hovercard.style[padding] = paddingValue + "px"; _this3._onHovercardShown(hash, hovercard); }, this._delayToShow); this._showHovercardTimeoutIds.set(id, timeoutId); } /** * Waits for a specified delay and hides the hovercard. * * @param {string} id - The ID associated with the hovercard. * @return {void} * @private */; _proto._hideHovercard = function _hideHovercard(id) { var _this4 = this; var timeoutId = setTimeout(function () { var hovercard = dc.getElementById(id); if (hovercard) { hovercard.remove(); _this4._onHovercardHidden(id, hovercard); } }, this._delayToHide); this._hideHovercardTimeoutIds.set(id, timeoutId); } /** * Handles the mouseenter event for hovercard refs. * * @param {MouseEvent} e - The mouseenter event object. * @param hovercardRef - The hovercard ref object. * @return {void} * @private */; _proto._handleMouseEnter = function _handleMouseEnter(e, hovercardRef) { e.stopImmediatePropagation(); // Don't hide the hovercard when the mouse is over the ref from the hovercard clearInterval(this._hideHovercardTimeoutIds.get(hovercardRef.id)); this._showHovercard(hovercardRef); } /** * Handles the mouseleave event for hovercard refs. * * @param {MouseEvent} e - The mouseleave event object. * @param hovercardRef - The hovercard ref object. * @param hovercardRef.id - The ID associated with the hovercard. * @return {void} * @private */; _proto._handleMouseLeave = function _handleMouseLeave(e, _ref6) { var id = _ref6.id; e.stopImmediatePropagation(); clearInterval(this._showHovercardTimeoutIds.get(id)); this._hideHovercard(id); }; return Hovercards; }(); Hovercards.createHovercard = function (profileData, _temp3) { var _ref7 = _temp3 === void 0 ? {} : _temp3, additionalClass = _ref7.additionalClass, myHash = _ref7.myHash, _ref7$i18n = _ref7.i18n, i18n = _ref7$i18n === void 0 ? {} : _ref7$i18n; var hash = profileData.hash, thumbnailUrl = profileData.thumbnailUrl, preferredUsername = profileData.preferredUsername, displayName = profileData.displayName, currentLocation = profileData.currentLocation, aboutMe = profileData.aboutMe, _profileData$accounts = profileData.accounts, accounts = _profileData$accounts === void 0 ? [] : _profileData$accounts; var hovercard = dc.createElement('div'); hovercard.className = "gravatar-hovercard" + (additionalClass ? " " + additionalClass : ''); var profileUrl = escUrl("https://gravatar.com/" + preferredUsername + "?utm_source=hovercard"); var username = escHtml(displayName); var isEditProfile = !aboutMe && myHash === hash; var renderSocialLinks = accounts.reduce(function (links, _ref8) { var url = _ref8.url, shortname = _ref8.shortname, iconUrl = _ref8.iconUrl, name = _ref8.name; var idx = socialLinksOrder.indexOf(shortname); if (idx !== -1) { links[idx] = "\n\t\t\t\t\t\t<a class=\"gravatar-hovercard__social-link\" href=\"" + escUrl(url) + "\" target=\"_blank\" data-service-name=\"" + shortname + "\">\n\t\t\t\t\t\t\t<img class=\"gravatar-hovercard__social-icon\" src=\"" + escUrl(iconUrl) + "\" width=\"32\" height=\"32\" alt=\"" + escHtml(name) + "\" />\n\t\t\t\t\t\t</a>\n\t\t\t\t\t"; } return links; }, []).join(''); hovercard.innerHTML = "\n\t\t\t<div class=\"gravatar-hovercard__inner\">\n\t\t\t\t<div class=\"gravatar-hovercard__header\">\n\t\t\t\t\t<a class=\"gravatar-hovercard__avatar-link\" href=\"" + profileUrl + "\" target=\"_blank\">\n\t\t\t\t\t\t<img class=\"gravatar-hovercard__avatar\" src=\"" + escUrl(thumbnailUrl) + "\" width=\"56\" height=\"56\" alt=\"" + username + "\" />\n\t\t\t\t\t</a>\n\t\t\t\t\t<a class=\"gravatar-hovercard__name-location-link\" href=\"" + profileUrl + "\" target=\"_blank\">\n\t\t\t\t\t\t<h4 class=\"gravatar-hovercard__name\">" + username + "</h4>\n\t\t\t\t\t\t" + (currentLocation ? "<p class=\"gravatar-hovercard__location\">" + escHtml(currentLocation) + "</p>" : '') + "\n\t\t\t\t\t</a>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"gravatar-hovercard__body\">\n\t\t\t\t\t" + (aboutMe ? "<p class=\"gravatar-hovercard__about\">" + escHtml(aboutMe) + "</p>" : '') + "\n\t\t\t\t</div>\n\t\t\t\t<div class=\"gravatar-hovercard__footer\">\n\t\t\t\t\t<div class=\"gravatar-hovercard__social-links\">\n\t\t\t\t\t\t<a class=\"gravatar-hovercard__social-link\" href=\"" + profileUrl + "\" target=\"_blank\" data-service-name=\"gravatar\">\n\t\t\t\t\t\t\t<img class=\"gravatar-hovercard__social-icon\" src=\"https://secure.gravatar.com/icons/gravatar.svg\" width=\"32\" height=\"32\" alt=\"Gravatar\" />\n\t\t\t\t\t\t</a>\n\t\t\t\t\t\t" + renderSocialLinks + "\n\t\t\t\t\t</div>\n\t\t\t\t\t<a\n\t\t\t\t\t\tclass=\"gravatar-hovercard__profile-link" + (isEditProfile ? ' gravatar-hovercard__profile-link--edit' : '') + "\"\n\t\t\t\t\t\thref=\"" + (isEditProfile ? 'https://gravatar.com/profiles/edit?utm_source=hovercard' : profileUrl) + "\"\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<span class=\"gravatar-hovercard__profile-link-text\">\n\t\t\t\t\t\t\t" + (isEditProfile ? __(i18n, 'Edit your profile') : __(i18n, 'View profile')) + "\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\">\n\t\t\t\t\t\t\t<path d=\"M12.6667 8.33338L9.16666 12.1667M12.6667 8.33338L2.66666 8.33338M12.6667 8.33338L9.16666 4.83338\" stroke-width=\"1.5\"/>\n\t\t\t\t\t\t</svg>\n\t\t\t\t\t</a>\n\t\t\t\t</div>\n\t\t\t</div>\n "; return hovercard; }; ;// CONCATENATED MODULE: ./src/index.ts module.exports = __webpack_exports__; /******/ })() ; //# sourceMappingURL=index.js.map