UNPKG

@easy-smart-app-banner/core

Version:

An Easy Smart App Banner for promoting mobile app installs based on the Safari Apple Experience. Includes support for Safari too.

397 lines (396 loc) 14.7 kB
function T(e) { return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, "default") ? e.default : e; } var E = { exports: {} }; /*! * js-logger - http://github.com/jonnyreeves/js-logger * Jonny Reeves, http://jonnyreeves.co.uk/ * js-logger may be freely distributed under the MIT license. */ var F = E.exports, P; function R() { return P || (P = 1, function(e) { (function(n) { var t = {}; t.VERSION = "1.6.1"; var i, u = {}, c = function(r, s) { return function() { return s.apply(r, arguments); }; }, d = function() { var r = arguments, s = r[0], v, l; for (l = 1; l < r.length; l++) for (v in r[l]) !(v in s) && r[l].hasOwnProperty(v) && (s[v] = r[l][v]); return s; }, f = function(r, s) { return { value: r, name: s }; }; t.TRACE = f(1, "TRACE"), t.DEBUG = f(2, "DEBUG"), t.INFO = f(3, "INFO"), t.TIME = f(4, "TIME"), t.WARN = f(5, "WARN"), t.ERROR = f(8, "ERROR"), t.OFF = f(99, "OFF"); var O = function(r) { this.context = r, this.setLevel(r.filterLevel), this.log = this.info; }; O.prototype = { // Changes the current logging level for the logging instance. setLevel: function(r) { r && "value" in r && (this.context.filterLevel = r); }, // Gets the current logging level for the logging instance getLevel: function() { return this.context.filterLevel; }, // Is the logger configured to output messages at the supplied level? enabledFor: function(r) { var s = this.context.filterLevel; return r.value >= s.value; }, trace: function() { this.invoke(t.TRACE, arguments); }, debug: function() { this.invoke(t.DEBUG, arguments); }, info: function() { this.invoke(t.INFO, arguments); }, warn: function() { this.invoke(t.WARN, arguments); }, error: function() { this.invoke(t.ERROR, arguments); }, time: function(r) { typeof r == "string" && r.length > 0 && this.invoke(t.TIME, [r, "start"]); }, timeEnd: function(r) { typeof r == "string" && r.length > 0 && this.invoke(t.TIME, [r, "end"]); }, // Invokes the logger callback if it's not being filtered. invoke: function(r, s) { i && this.enabledFor(r) && i(s, d({ level: r }, this.context)); } }; var o = new O({ filterLevel: t.OFF }); (function() { var r = t; r.enabledFor = c(o, o.enabledFor), r.trace = c(o, o.trace), r.debug = c(o, o.debug), r.time = c(o, o.time), r.timeEnd = c(o, o.timeEnd), r.info = c(o, o.info), r.warn = c(o, o.warn), r.error = c(o, o.error), r.log = r.info; })(), t.setHandler = function(r) { i = r; }, t.setLevel = function(r) { o.setLevel(r); for (var s in u) u.hasOwnProperty(s) && u[s].setLevel(r); }, t.getLevel = function() { return o.getLevel(); }, t.get = function(r) { return u[r] || (u[r] = new O(d({ name: r }, o.context))); }, t.createDefaultHandler = function(r) { r = r || {}, r.formatter = r.formatter || function(p, m) { m.name && p.unshift("[" + m.name + "]"); }; var s = {}, v = function(l, p) { Function.prototype.apply.call(l, console, p); }; return typeof console > "u" ? function() { } : function(l, p) { l = Array.prototype.slice.call(l); var m = console.log, g; p.level === t.TIME ? (g = (p.name ? "[" + p.name + "] " : "") + l[0], l[1] === "start" ? console.time ? console.time(g) : s[g] = (/* @__PURE__ */ new Date()).getTime() : console.timeEnd ? console.timeEnd(g) : v(m, [g + ": " + ((/* @__PURE__ */ new Date()).getTime() - s[g]) + "ms"])) : (p.level === t.WARN && console.warn ? m = console.warn : p.level === t.ERROR && console.error ? m = console.error : p.level === t.INFO && console.info ? m = console.info : p.level === t.DEBUG && console.debug ? m = console.debug : p.level === t.TRACE && console.trace && (m = console.trace), r.formatter(l, p), v(m, l)); }; }, t.useDefaults = function(r) { t.setLevel(r && r.defaultLevel || t.DEBUG), t.setHandler(t.createDefaultHandler(r)); }, t.setDefaults = t.useDefaults, e.exports ? e.exports = t : (t._prevLogger = n.Logger, t.noConflict = function() { return n.Logger = t._prevLogger, t; }, n.Logger = t); })(F); }(E)), E.exports; } var S = R(); const a = /* @__PURE__ */ T(S); class A extends Event { static type; target; constructor(n, t = !1) { super(n, { cancelable: t }); } } class B extends EventTarget { dispatchEvent(n) { return super.dispatchEvent(n); } addEventListener(n, t, i) { super.addEventListener(n, t, i); } removeEventListener(n, t, i) { super.removeEventListener(n, t, i); } } class b extends A { } class w extends b { static type = "ready"; constructor() { super(w.type, !0); } } class L extends b { static type = "destroyed"; constructor() { super(L.type, !0); } } class y extends b { static type = "clicked-call-to-action"; constructor() { super(y.type, !0); } } class _ extends b { static type = "toggled-visibility"; constructor() { super(_.type, !0); } } const N = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({ __proto__: null, ClickedCallToAction: y, DestroyedEvent: L, ReadyEvent: w, SmartAppBannerEvent: b, ToggledVisibility: _ }, Symbol.toStringTag, { value: "Module" })); String.prototype.isFalsishOrEmpty = function() { return !this.trim(); }; class h extends Error { constructor(n, t) { super( t && t instanceof Error ? `${n}: ${t.message}` : n ), this.name = "SmartAppBanner", Error.captureStackTrace?.(this, h); } } function D(e, n) { const t = function(i) { const u = { ...e, ...i }; Object.keys(u).forEach((d) => { n && d in n || a.warn(`Unknown option ${d}`); }); const c = {}; return Object.entries(u).forEach(([d, f]) => { n && d in n && (c[d] = n[d](f, { rawOptions: u, defaultValue: e[d] })); }), a.debug(), c; }; return t.defaults = e, t.parsers = n || {}, t; } const I = { icon: null, platforms: ["android", "ios"], price: null, buttonLabel: "View", verbose: !1, dismissPath: "/", googlePlayStoreUrl: null, androidButtonLabel: null, androidIcon: null, androidPrice: "FREE - On the Google Play Store", appStoreUrl: null, appleButtonLabel: null, appleIcon: null, applePrice: "GET - On the App Store" }, U = { title: (e) => { if (e.isFalsishOrEmpty()) throw new h("No title has been configured."); return e; }, author: (e) => { if (e.isFalsishOrEmpty()) throw new h("No author has been configured."); return e; }, icon: (e) => e?.isFalsishOrEmpty() ? null : e, platforms: (e, { defaultValue: n }) => !e || e.length === 0 ? n : e, price: (e) => e?.isFalsishOrEmpty() ? null : e, buttonLabel: (e, { defaultValue: n }) => e?.isFalsishOrEmpty() ? n : e, verbose: (e, { defaultValue: n }) => e ? (a.debug("verbose logging enabled"), e) : n, dismissPath: (e, { defaultValue: n }) => e?.isFalsishOrEmpty() ? n : e, googlePlayStoreUrl: (e, { rawOptions: n }) => { if (!n.platforms?.includes("android")) return null; const t = `The Android platform was enabled but no valid Google Play Store URL has been configured. Provided URL was "${e}"`; if (e?.isFalsishOrEmpty()) throw new h(t); try { return new URL(e); } catch { throw new h(t); } }, androidButtonLabel: (e, { rawOptions: n }) => n.platforms?.includes("android") ? e ?? null : null, androidIcon: (e, { rawOptions: n }) => { if (!n.platforms?.includes("android")) return null; if (n.platforms?.includes("android") && n.icon?.isFalsishOrEmpty() && e?.isFalsishOrEmpty()) throw new h( "The Android platform was enabled but there is no icon defined at all" ); return e ?? null; }, androidPrice: (e, { defaultValue: n }) => e ?? n, appStoreUrl: (e, { rawOptions: n }) => { if (!n.platforms?.includes("ios")) return null; const t = `The iOS platform was enabled but no valid Apple App Store URL has been configured. Provided URL was "${e}"`; if (e?.isFalsishOrEmpty()) throw new h(t); try { return new URL(e); } catch { throw new h(t); } }, appleButtonLabel: (e, { rawOptions: n }) => n.platforms?.includes("ios") ? e ?? null : null, appleIcon: (e, { rawOptions: n }) => { if (!n.platforms?.includes("ios")) return null; if (n.platforms?.includes("ios") && n.icon?.isFalsishOrEmpty() && e?.isFalsishOrEmpty()) throw new h( "The iOS platform was enabled but there is no icon defined at all" ); return e ?? null; }, applePrice: (e, { defaultValue: n }) => e ?? n }, x = D(I, U); function k(e) { let n; a.debug("Current user agent: ", e); const t = window.navigator.maxTouchPoints > 2; if (!(/X11|Windows|Macintosh/i.test(e) && !t)) return /android|windows phone/i.test(e) && (n = "android"), /(?:iPhone|iPad|iPod)(?!.*(criOS|fxiOS|opiOS|chrome|android))/i.test( e ) ? n = "safari" : (/(?:iPhone|iPad|iPod)/i.test(e) || t && /Macintosh/i.test(e)) && (n = "ios"), !n && a.debug("The current platform is not supported"), a.debug("Resolved current platform as:", n), n; } function C() { const e = "smart-app-banner-dismissed"; function n(i) { const u = {}; return document.cookie.split(";").forEach((c) => { const [d, f] = c.split("="); u[d.trim()] = f ?? ""; }), u[i]; } function t(i, u) { document.cookie = `${e}=${i ? 1 : 0}; path=${u?.isFalsishOrEmpty() ? "/" : u}`; } return { isDismissed: () => n(e) === "1", dismiss: (i) => t(!0, i), show: (i) => t(!1, i) }; } class $ extends B { options; platform; bannerId = "smart-app-banner"; __bannerElement = null; constructor(n) { super(), this.options = x(n), this.options.verbose && (a.setLevel(a.DEBUG), window.getCurrentPlatform = void 0, window.getCurrentPlatform = k), this.platform = k(window.navigator.userAgent), a.info("successfully initialised"); } mount() { !this.platform || this.platform === "safari" || C().isDismissed() || (a.time("mounting banner"), this.__bannerElement = document.createElement("div"), this.__bannerElement.innerHTML = this.html, this.__bannerElement.id = this.bannerId, document.body.prepend(this.__bannerElement), this.onClickClose = this.onClickClose.bind(this), this.onClickCallToAction = this.onClickCallToAction.bind(this), document.querySelector(".smartappbanner__close")?.addEventListener("click", this.onClickClose, !1), document.querySelector(".smartappbanner__view")?.addEventListener("click", this.onClickCallToAction, !1), a.debug("mounted banner"), a.timeEnd("mounting banner"), this.dispatchEvent(new w())); } destroy() { !this.__bannerElement || !this.platform || this.platform === "safari" || (this.removeEventListeners(), this.__bannerElement.remove(), a.debug("destroyed banner"), this.options.verbose && (window.getCurrentPlatform = void 0), this.dispatchEvent(new L())); } setBannerVisibility(n) { const t = C(); n ? (a.debug("re-showing banner"), t.show(this.options.dismissPath)) : (a.debug("dismissing banner"), t.dismiss(this.options.dismissPath)), this.destroy(), this.mount(), this.dispatchEvent(new _()); } onClickClose(n) { n.preventDefault(), C().dismiss(this.options.dismissPath), this.destroy(); } onClickCallToAction(n) { this.dispatchEvent(new y()); } removeEventListeners() { document.querySelector(".smartappbanner__close")?.removeEventListener("click", this.onClickClose, !1), document.querySelector(".smartappbanner__view")?.removeEventListener("click", this.onClickCallToAction, !1); } get title() { if (this.platform !== "safari") return this.options.title; } get author() { if (this.platform !== "safari") return this.options.author; } get price() { if (this.platform !== "safari") return this.platform === "android" && this.options.androidPrice ? this.options.androidPrice : this.platform === "ios" && this.options.applePrice ? this.options.applePrice : this.options.price ?? ""; } get icon() { if (this.platform !== "safari") return this.platform === "android" && this.options.androidIcon ? this.options.androidIcon : this.platform === "ios" && this.options.appleIcon ? this.options.appleIcon : this.options.icon ?? ""; } get buttonUrl() { if (this.platform !== "safari") { if (this.platform === "android" && this.options.googlePlayStoreUrl) return this.options.googlePlayStoreUrl; if (this.platform === "ios" && this.options.appStoreUrl) return this.options.appStoreUrl; } } get buttonLabel() { if (this.platform !== "safari") return this.platform === "android" && this.options.androidButtonLabel ? this.options.androidButtonLabel : this.platform === "ios" && this.options.appleButtonLabel ? this.options.appleButtonLabel : this.options.buttonLabel; } get html() { if (this.platform === "safari") throw new h( `Attempted to render banner. However, the current platform is Safari. This should instead be handled via their recommended meta tag. See: https://developer.apple.com/documentation/webkit/promoting-apps-with-smart-app-banners` ); return ` <div class="smartappbanner"> <div class="smartappbanner__close"> <a href="#" href="nofollow" ></a> </div> <div class="smartappbanner__app-icon" style="background-image: url(${this.icon})" ></div> <div class="smartappbanner__description"> <div class="smartappbanner__description__title">${this.title}</div> <div class="smartappbanner__description__author">${this.author}</div> <div class="smartappbanner__description__price">${this.price}</div> </div> <a href="${this.buttonUrl}" target="_blank" rel="noopener" aria-label="${this.buttonLabel}" class="smartappbanner__view" > <span class="smartbanner__view__label">${this.buttonLabel}</span> </a> </div> `; } } a.useDefaults({ defaultLevel: a.WARN, formatter: function(e, n) { e.unshift("[🛍️ Smart App Banner]"); } }); export { I as DEFAULT_OPTIONS, U as OPTION_PARSERS, $ as SmartAppBanner, N as events }; //# sourceMappingURL=index.js.map