@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
JavaScript
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