Skip to content

Instantly share code, notes, and snippets.

@taylor8294
Last active June 10, 2020 15:10
Show Gist options
  • Select an option

  • Save taylor8294/ad3551afa1539356193e9c3e56294146 to your computer and use it in GitHub Desktop.

Select an option

Save taylor8294/ad3551afa1539356193e9c3e56294146 to your computer and use it in GitHub Desktop.
Small utility to produce native-looking app install banners (like https://i.imgur.com/Qu3hoGh.png) in mobile browsers. Relies on jQuery being present in the page. Initiate like `new AppBanner({ios:{id:'1500000000',title:'My App',store:'gb'},android:{id:'com.example.app',title:'My App'},showAfter:30});`
/* Native App Install Banner v1 ~ (c) 2020 Alex Taylor */
(function (window, document, $, undefined) {
var AppBanner = function (options) {
var OS = AppBanner.OS;
// merge default options with user config
this.options = $.extend(true, {}, AppBanner.defaults, options);
if (this.options.debug) {
AppBanner.isCompatible = true;
AppBanner.OS = typeof this.options.debug == 'string' ? this.options.debug : AppBanner.OS == 'unsupported' ? 'android' : AppBanner.OS;
AppBanner.OSVersion = AppBanner.OS == 'ios' ? '13' : '10';
}
// the element the message will be appended to
this.$container = this.options.container ? $(this.options.container) : $(document.body);
// check the device is supported
if (!AppBanner.isCompatible) {
console.warn("App banner: device not supported");
return;
}
// check if the app is in standalone mode
if (AppBanner.isStandalone) {
console.warn("App banner: in standalone mode");
//return;
}
$.extend(true, this, AppBanner[OS], this.options[OS]);
this.init();
};
//Device sniffing
var _ua = window.navigator.userAgent;
AppBanner.isRetina = window.devicePixelRatio && window.devicePixelRatio > 1;
AppBanner.isIDevice = (/iphone|ipod|ipad/i).test(_ua);
AppBanner.isMobileChrome = _ua.indexOf('Android') > -1 && (/Chrome\/[.0-9]*/).test(_ua) && _ua.indexOf("Version") == -1;
AppBanner.isMobileIE = _ua.indexOf('Windows Phone') > -1;
AppBanner.isMobileSafari = AppBanner.isIDevice && _ua.indexOf('Safari') > -1 && _ua.indexOf('CriOS') < 0;
AppBanner.OS = AppBanner.isIDevice ? 'ios' : AppBanner.isMobileChrome ? 'android' : AppBanner.isMobileIE ? 'windows' : 'unsupported';
var OSv = _ua.match(/(OS|Android) (\d+[_\.]\d+)/);
AppBanner.OSVersion = OSv && OSv[2] ? +OSv[2].replace('_', '.') : 0;
AppBanner.isStandalone = 'standalone' in window.navigator && window.navigator.standalone,
AppBanner.isTablet = (AppBanner.isMobileSafari && _ua.indexOf('iPad') > -1) || (AppBanner.isMobileChrome && _ua.indexOf('Mobile') < 0),
AppBanner.isCompatible = (AppBanner.isMobileSafari && (AppBanner.OSVersion >= 6 || AppBanner.OSVersion == 0)) || AppBanner.isMobileChrome; // TODO: add winphone
AppBanner.defaults = {
showAfter: 30,
ios: {
id: '',
url: 'https://apps.apple.com/\${store}/app/id\${id}',
icon: '',
title: '',
developer: '',
rating: 4.5,
desc: 'GET - On the App Store',
button: 'View',
store: 'us'
},
android: {
id: '',
url: 'https://play.google.com/store/apps/details?id=\${id}',
icon: '',
title: '',
rating: 4.5,
button: 'Install'
},
//windows: {}, //TODO
debug: false
};
AppBanner.waitForDom = function(){
return new Promise(function (resolve, reject) {
(function pollDom(){
if (window.jQuery.isReady) return resolve();
setTimeout(pollDom, 30);
})();
});
}
AppBanner.template = function(str,obj){
var keys = Object.keys(obj);
var vals = Object.keys(obj).map(function(key){ return obj[key] });
return new Function(...keys, `return \`${str}\`;`)(...vals);
}
AppBanner.preventDefault = function(e) {
e.preventDefault();
e.stopPropagation();
}
AppBanner.ios = {
html: `
<div class="app-banner ios">
<div class="app-banner-close">
<a href="javascript:">&#10005;</a>
</div>
<div class="app-banner-icon">
<img width="70" src="\${icon}" />
</div>
<div class="app-banner-content">
<div class="app-banner-title">
\${title}
</div>
<div class="app-banner-developer">
\${developer}
</div>
<div class="app-banner-rating">
<img width="8" src="\${rating > 0 ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAARUlEQVQoU2NkQAP/pjP/BwkxZf5lRJZC4YAkiFIIUwQzCdlUFBPxKkSXRHczjA82kZBikBPgVuNSDHMnjRQiWwuzCl0MAG7zKqLf346TAAAAAElFTkSuQmCC' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAARUlEQVQoU2NkQAMnTpz4DxKysLBgRJZC4YAkiFIIUwQzCdlUFBPxKkSXRHczjA82kZBikBPgVuNSDHMnjRQiWwuzCl0MAKkHK2I7iNRuAAAAAElFTkSuQmCC' }" />
<img width="8" src="\${rating > 1 ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAARUlEQVQoU2NkQAP/pjP/BwkxZf5lRJZC4YAkiFIIUwQzCdlUFBPxKkSXRHczjA82kZBikBPgVuNSDHMnjRQiWwuzCl0MAG7zKqLf346TAAAAAElFTkSuQmCC' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAARUlEQVQoU2NkQAMnTpz4DxKysLBgRJZC4YAkiFIIUwQzCdlUFBPxKkSXRHczjA82kZBikBPgVuNSDHMnjRQiWwuzCl0MAKkHK2I7iNRuAAAAAElFTkSuQmCC' }" />
<img width="8" src="\${rating > 2 ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAARUlEQVQoU2NkQAP/pjP/BwkxZf5lRJZC4YAkiFIIUwQzCdlUFBPxKkSXRHczjA82kZBikBPgVuNSDHMnjRQiWwuzCl0MAG7zKqLf346TAAAAAElFTkSuQmCC' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAARUlEQVQoU2NkQAMnTpz4DxKysLBgRJZC4YAkiFIIUwQzCdlUFBPxKkSXRHczjA82kZBikBPgVuNSDHMnjRQiWwuzCl0MAKkHK2I7iNRuAAAAAElFTkSuQmCC' }" />
<img width="8" src="\${rating > 3 ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAARUlEQVQoU2NkQAP/pjP/BwkxZf5lRJZC4YAkiFIIUwQzCdlUFBPxKkSXRHczjA82kZBikBPgVuNSDHMnjRQiWwuzCl0MAG7zKqLf346TAAAAAElFTkSuQmCC' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAARUlEQVQoU2NkQAMnTpz4DxKysLBgRJZC4YAkiFIIUwQzCdlUFBPxKkSXRHczjA82kZBikBPgVuNSDHMnjRQiWwuzCl0MAKkHK2I7iNRuAAAAAElFTkSuQmCC' }" />
<img width="8" src="\${rating > 4 ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAARUlEQVQoU2NkQAP/pjP/BwkxZf5lRJZC4YAkiFIIUwQzCdlUFBPxKkSXRHczjA82kZBikBPgVuNSDHMnjRQiWwuzCl0MAG7zKqLf346TAAAAAElFTkSuQmCC' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAARUlEQVQoU2NkQAMnTpz4DxKysLBgRJZC4YAkiFIIUwQzCdlUFBPxKkSXRHczjA82kZBikBPgVuNSDHMnjRQiWwuzCl0MAKkHK2I7iNRuAAAAAElFTkSuQmCC' }" />
</div>
<div class="app-banner-desc">
\${desc}
</div>
</div>
<div class="app-banner-button">
<a href="\${url}" target="_blank">\${button}</a>
</div>
</div>`,
css: `
body {
position: relative;
min-height: 100vh;
}
.app-banner {
display: table;
position: fixed;
z-index: 9999999;
left: 0;
box-sizing: border-box;
width: 100%;
margin: 0;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.app-banner.ios {
table-layout: fixed;
top: -90px;
background-color: #f2f2f2;
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
padding: 5px 0;
}
.app-banner.ios > div {
display: table-cell;
vertical-align: middle;
text-align: center;
padding: 0;
margin: 0;
}
.app-banner.ios .app-banner-close {
width: 30px;
}
.app-banner.ios .app-banner-close a, .app-banner.ios .app-banner-close a:hover, .app-banner.ios .app-banner-close a:active, .app-banner.ios .app-banner-close a:visited, .app-banner.ios .app-banner-close a:focus {
color: #6f6f6f;
text-decoration: none;
}
.app-banner.ios .app-banner-icon {
text-align: left;
width: 80px;
}
.app-banner.ios .app-banner-icon img {
border-radius: 22.5%;
}
.app-banner.ios .app-banner-content {
text-align: left;
color: #0f0f0f;
}
.app-banner.ios .app-banner-content .app-banner-title {
font-size: 14px;
font-weight: 500;
color: #010101;
margin-bottom: 2px;
}
.app-banner.ios .app-banner-content .app-banner-desc {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.app-banner.ios .app-banner-button {
width: 90px;
}
.app-banner.ios .app-banner-button a, .app-banner.ios .app-banner-button a:hover, .app-banner.ios .app-banner-button a:active, .app-banner.ios .app-banner-button a:visited, .app-banner.ios .app-banner-button a:focus {
font-size: 16px;
color: #0078ff;
text-decoration: none;
}`
}
AppBanner.android = {
html: `
<div class="app-banner android">
<div class="app-banner-row">
<div class="app-banner-icon">
<img width="70" src="\${icon}" />
</div>
<div class="app-banner-content" colspan="2">
<div class="app-banner-title">
\${title}
</div>
<div class="app-banner-rating">
<img width="16" src="\${rating > 0.5 ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAfCAYAAAAfrhY5AAADqElEQVRYR8WVfUzMcRzH3+eOuuNqOosopVNLpDKLkMdj1i1Km9nRg4exmWebhpGx9TBGD39gniajUWkqFblqRSd6sNx1SUp5WMgzPxV3X/u1dXVc3X1/Z/P79/t+v1+fz/f3+X1+PPzHh2cN+47mEpFNjuCcwdlY0VJ6taj+9MpYeTrnDM7Gy6pEUtGUjag5exEwYTmnHE4m9lUlFm4iLe01cHH0wP7gK5xyOJlK67NIjjoFDMNALB6Bo+ElnHI4ma5VpZJidZphVpf6RiHUfwt1FrWBJcYXKkhr+1MD3GPsVOxeco46i9rAEnelLyJM1xcD3NHeFYfDMqmzqA3lT2+RjMo4dP9iDHAbGxHCA3ZirjSUKo9KzNIyaxNIcV029ERvtJ9mSkMQHXSQKo9KzNLiciNI2/sGI7CO6CAZ6YyE0ByqvB7xqdI9pLVDC/JHN3+uXvb8a+cH6PXGXbM6Pp8Psa3E7LZmdWPs3LFVlsQzVHpcuYM0vrhn1mytQOrkgzC/PZg42qsPzoYWazKI8slFvP/yxlqGkZ/H42Gsgxumu4ZglocM9kKnnqaN3lFnN5Pc1FG5Tam+Du3r+/+sAO/xgZD7RSukDtPS+4eaHBCGYWIK6s8mqJpv4BvT9z3TViMebocFnhEI9o02yRl0Osuf5ZMKbRbaPmqg0+ksZg8dKoCjeAJme8qx0Gv1gAyLPo1U5VaiffXA5JSbqijAPRiLvKPgOsp90HyL4Jk1iaRYfd0iONt1oHsoFIExZrPNCtjOjuVvJE1vay2+dieJG2JDMsxmmxU0vislp0pie/7dNI9i5gHM9Vpm3bVnVCaTovo0DOEN+YvNbquBBnGGVI61QYesgx/JXUnaOprA5/ENcKHQFlKJPzzGeaGupRYvPmvR3dVlVNxI8WjEh+dxh1e3KHckKTefGC6w6/t92tpiw7y9kNrPTxGJRNvZg7vN10j2wzP4/uOTQTdMIETKmjJucOYnE5NZk5RQor4CG74QAoEAbg5TsdhHAV+XeX+Fal6pSKHmPJrf1EGn04NdqYGe4YgcZOoHrSw+L4K0djT0gP1dZVgfdMTsgKZXxZF72nz80nXBRTIJ+0PS6JdM1fOyaRdU+6qH6QWQ+6+DzDvSLLj3zssa8khBQyqYTgbJq8rp4TcfnyQvPzViU9AJi6G9cKa7M1nTdneF6nmWs0TkhtWzTC+cAYPV7bcfTRmzxI/m2zalffyyiPFxXiwydUbdlbXF9Pf/BkFmRi9vqxubAAAAAElFTkSuQmCC' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAfCAYAAAAfrhY5AAAEWElEQVRYR72WfUwbdRjHn1/vSs+7Ky1XaKVrSQgvcwvZNBp5SbbMP7YFp0t8ISEp6vxjxqhgdInTJUa3GWMWg6ZuvqNxAzYwGGaULXMi4NbChsMNXWGWt1HelJdee6MtPfiZY8IGtLZ3uv3+a+95vp/n9XeHQOHBGOfywnirjk3OQAj1KpFBSpwkH39wuvTKaLN9tbmgnE3Q7VKioxjeO+msvNh33rY5pyTEajjqtsExxty3Fz5t+9M/lvno+lLg9PoNCKEzcgNQlDl/bbDww5ZXG7TEnbBtdSmYUow1NE0X3xb4xYGf36zpeOsNM50DhRnPg9GYAlqtVnYish2k7Jq6jzdUO/cVrjNvmoezLAMGg6GYJMkaOdnLhgcxn3m09UDbT5fruPut2+bhajUpwc8wDLPhlsL/4odtn7W8Vukaa4XctIcW4RzHAcMwFEIoFG8AsjPvHGmx1zjfLx3xuuEey+ZFOE3TkJSUtIsgiPJbBnf0HW095jiUOxMOQY554yJcrVZLpe9NSEjIiBs+5r2KA6IQl30oGIIfug7Db57rK30zXPptMBiAJMm4tCQj5B51YWfPN9A9dg7EWTGmoz84AbOzsxHhBEEAQrE7KdlpNBpAGGObZ7zffqLrS+7X3lMwh68Lx3OWZx6Pj2TDcdwkwzBl82FK16UwE7S7h9ttDb9/BJ4Jt/RfTC05cKkiFEUBy7JVFEWVIYQml9RIFEXbEO+yN7pquTb3yf8VrtPp5rMlSbJqQXhFg6QqBINB+9me47bGPw7DJD8u1SZiIPFk/s8KVqlUqvlsbxaKOh0iFm2u4Tb79x1fcEO8C8Lh8IoAYsEZhpnU6XRLso0LvjALnomr9uYrVbZzA9/BTGhpANHg0s7r9frF3kbrX+y9AIDzA/X4iOOALHhqampM7ZgGPp/vrsoLb7su9f8oq+xGozGLoij3v01tTPiQ99Jue+PL7/A+Xhacpumy5OTkD/4T/GRntaOu/d18AhFLdIKz1yAvfTtsSd8JJKGOxDiRlpb2oGK4gAVTRdPu0c6+s0CqbgCkgUpNzIKt64vBSt8NwemVmyBBrVZrMkJoQtHAdQ92PHOw5cVPwuHAor90L2+/78ne/FUl+xITE7/CGD/N8/xen89nXQ7RaDSPm0ymOkXw013HauvbDxaJYghUKhWk6jJg01pbxb2ZD+ylET24IIoxTg8EAvv9fr8tFLrxLaHRaA6ZTKYXZMMxxgmfN+/p+aX/tIVQkbBmVcHQljVPvZJtXlcdTUwUxZ1er3f/9PS0SbJBCLktFstahFDEvkSd9hGvp+i9xmdrBf84bMx+rOLhvB2vMyhlJNaFjzG28DxfLghC0dzcnPQG28qy7KlIflHhbX315Y6+hicKrI88l5tV+HUs6PLngUBgx9TUlPRJ9bHZbN4jC355rOmI/o7sl8yJZunNouhgjI2CIJRrtdqSuOEY43yEkFMRMYITxjgPIdS6/NHfsBG1QLyoq3AAAAAASUVORK5CYII=' }" />
<img width="16" src="\${rating > 1.5 ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAfCAYAAAAfrhY5AAADqElEQVRYR8WVfUzMcRzH3+eOuuNqOosopVNLpDKLkMdj1i1Km9nRg4exmWebhpGx9TBGD39gniajUWkqFblqRSd6sNx1SUp5WMgzPxV3X/u1dXVc3X1/Z/P79/t+v1+fz/f3+X1+PPzHh2cN+47mEpFNjuCcwdlY0VJ6taj+9MpYeTrnDM7Gy6pEUtGUjag5exEwYTmnHE4m9lUlFm4iLe01cHH0wP7gK5xyOJlK67NIjjoFDMNALB6Bo+ElnHI4ma5VpZJidZphVpf6RiHUfwt1FrWBJcYXKkhr+1MD3GPsVOxeco46i9rAEnelLyJM1xcD3NHeFYfDMqmzqA3lT2+RjMo4dP9iDHAbGxHCA3ZirjSUKo9KzNIyaxNIcV029ERvtJ9mSkMQHXSQKo9KzNLiciNI2/sGI7CO6CAZ6YyE0ByqvB7xqdI9pLVDC/JHN3+uXvb8a+cH6PXGXbM6Pp8Psa3E7LZmdWPs3LFVlsQzVHpcuYM0vrhn1mytQOrkgzC/PZg42qsPzoYWazKI8slFvP/yxlqGkZ/H42Gsgxumu4ZglocM9kKnnqaN3lFnN5Pc1FG5Tam+Du3r+/+sAO/xgZD7RSukDtPS+4eaHBCGYWIK6s8mqJpv4BvT9z3TViMebocFnhEI9o02yRl0Osuf5ZMKbRbaPmqg0+ksZg8dKoCjeAJme8qx0Gv1gAyLPo1U5VaiffXA5JSbqijAPRiLvKPgOsp90HyL4Jk1iaRYfd0iONt1oHsoFIExZrPNCtjOjuVvJE1vay2+dieJG2JDMsxmmxU0vislp0pie/7dNI9i5gHM9Vpm3bVnVCaTovo0DOEN+YvNbquBBnGGVI61QYesgx/JXUnaOprA5/ENcKHQFlKJPzzGeaGupRYvPmvR3dVlVNxI8WjEh+dxh1e3KHckKTefGC6w6/t92tpiw7y9kNrPTxGJRNvZg7vN10j2wzP4/uOTQTdMIETKmjJucOYnE5NZk5RQor4CG74QAoEAbg5TsdhHAV+XeX+Fal6pSKHmPJrf1EGn04NdqYGe4YgcZOoHrSw+L4K0djT0gP1dZVgfdMTsgKZXxZF72nz80nXBRTIJ+0PS6JdM1fOyaRdU+6qH6QWQ+6+DzDvSLLj3zssa8khBQyqYTgbJq8rp4TcfnyQvPzViU9AJi6G9cKa7M1nTdneF6nmWs0TkhtWzTC+cAYPV7bcfTRmzxI/m2zalffyyiPFxXiwydUbdlbXF9Pf/BkFmRi9vqxubAAAAAElFTkSuQmCC' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAfCAYAAAAfrhY5AAAEWElEQVRYR72WfUwbdRjHn1/vSs+7Ky1XaKVrSQgvcwvZNBp5SbbMP7YFp0t8ISEp6vxjxqhgdInTJUa3GWMWg6ZuvqNxAzYwGGaULXMi4NbChsMNXWGWt1HelJdee6MtPfiZY8IGtLZ3uv3+a+95vp/n9XeHQOHBGOfywnirjk3OQAj1KpFBSpwkH39wuvTKaLN9tbmgnE3Q7VKioxjeO+msvNh33rY5pyTEajjqtsExxty3Fz5t+9M/lvno+lLg9PoNCKEzcgNQlDl/bbDww5ZXG7TEnbBtdSmYUow1NE0X3xb4xYGf36zpeOsNM50DhRnPg9GYAlqtVnYish2k7Jq6jzdUO/cVrjNvmoezLAMGg6GYJMkaOdnLhgcxn3m09UDbT5fruPut2+bhajUpwc8wDLPhlsL/4odtn7W8Vukaa4XctIcW4RzHAcMwFEIoFG8AsjPvHGmx1zjfLx3xuuEey+ZFOE3TkJSUtIsgiPJbBnf0HW095jiUOxMOQY554yJcrVZLpe9NSEjIiBs+5r2KA6IQl30oGIIfug7Db57rK30zXPptMBiAJMm4tCQj5B51YWfPN9A9dg7EWTGmoz84AbOzsxHhBEEAQrE7KdlpNBpAGGObZ7zffqLrS+7X3lMwh68Lx3OWZx6Pj2TDcdwkwzBl82FK16UwE7S7h9ttDb9/BJ4Jt/RfTC05cKkiFEUBy7JVFEWVIYQml9RIFEXbEO+yN7pquTb3yf8VrtPp5rMlSbJqQXhFg6QqBINB+9me47bGPw7DJD8u1SZiIPFk/s8KVqlUqvlsbxaKOh0iFm2u4Tb79x1fcEO8C8Lh8IoAYsEZhpnU6XRLso0LvjALnomr9uYrVbZzA9/BTGhpANHg0s7r9frF3kbrX+y9AIDzA/X4iOOALHhqampM7ZgGPp/vrsoLb7su9f8oq+xGozGLoij3v01tTPiQ99Jue+PL7/A+Xhacpumy5OTkD/4T/GRntaOu/d18AhFLdIKz1yAvfTtsSd8JJKGOxDiRlpb2oGK4gAVTRdPu0c6+s0CqbgCkgUpNzIKt64vBSt8NwemVmyBBrVZrMkJoQtHAdQ92PHOw5cVPwuHAor90L2+/78ne/FUl+xITE7/CGD/N8/xen89nXQ7RaDSPm0ymOkXw013HauvbDxaJYghUKhWk6jJg01pbxb2ZD+ylET24IIoxTg8EAvv9fr8tFLrxLaHRaA6ZTKYXZMMxxgmfN+/p+aX/tIVQkbBmVcHQljVPvZJtXlcdTUwUxZ1er3f/9PS0SbJBCLktFstahFDEvkSd9hGvp+i9xmdrBf84bMx+rOLhvB2vMyhlJNaFjzG28DxfLghC0dzcnPQG28qy7KlIflHhbX315Y6+hicKrI88l5tV+HUs6PLngUBgx9TUlPRJ9bHZbN4jC355rOmI/o7sl8yJZunNouhgjI2CIJRrtdqSuOEY43yEkFMRMYITxjgPIdS6/NHfsBG1QLyoq3AAAAAASUVORK5CYII=' }" />
<img width="16" src="\${rating > 2.5 ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAfCAYAAAAfrhY5AAADqElEQVRYR8WVfUzMcRzH3+eOuuNqOosopVNLpDKLkMdj1i1Km9nRg4exmWebhpGx9TBGD39gniajUWkqFblqRSd6sNx1SUp5WMgzPxV3X/u1dXVc3X1/Z/P79/t+v1+fz/f3+X1+PPzHh2cN+47mEpFNjuCcwdlY0VJ6taj+9MpYeTrnDM7Gy6pEUtGUjag5exEwYTmnHE4m9lUlFm4iLe01cHH0wP7gK5xyOJlK67NIjjoFDMNALB6Bo+ElnHI4ma5VpZJidZphVpf6RiHUfwt1FrWBJcYXKkhr+1MD3GPsVOxeco46i9rAEnelLyJM1xcD3NHeFYfDMqmzqA3lT2+RjMo4dP9iDHAbGxHCA3ZirjSUKo9KzNIyaxNIcV029ERvtJ9mSkMQHXSQKo9KzNLiciNI2/sGI7CO6CAZ6YyE0ByqvB7xqdI9pLVDC/JHN3+uXvb8a+cH6PXGXbM6Pp8Psa3E7LZmdWPs3LFVlsQzVHpcuYM0vrhn1mytQOrkgzC/PZg42qsPzoYWazKI8slFvP/yxlqGkZ/H42Gsgxumu4ZglocM9kKnnqaN3lFnN5Pc1FG5Tam+Du3r+/+sAO/xgZD7RSukDtPS+4eaHBCGYWIK6s8mqJpv4BvT9z3TViMebocFnhEI9o02yRl0Osuf5ZMKbRbaPmqg0+ksZg8dKoCjeAJme8qx0Gv1gAyLPo1U5VaiffXA5JSbqijAPRiLvKPgOsp90HyL4Jk1iaRYfd0iONt1oHsoFIExZrPNCtjOjuVvJE1vay2+dieJG2JDMsxmmxU0vislp0pie/7dNI9i5gHM9Vpm3bVnVCaTovo0DOEN+YvNbquBBnGGVI61QYesgx/JXUnaOprA5/ENcKHQFlKJPzzGeaGupRYvPmvR3dVlVNxI8WjEh+dxh1e3KHckKTefGC6w6/t92tpiw7y9kNrPTxGJRNvZg7vN10j2wzP4/uOTQTdMIETKmjJucOYnE5NZk5RQor4CG74QAoEAbg5TsdhHAV+XeX+Fal6pSKHmPJrf1EGn04NdqYGe4YgcZOoHrSw+L4K0djT0gP1dZVgfdMTsgKZXxZF72nz80nXBRTIJ+0PS6JdM1fOyaRdU+6qH6QWQ+6+DzDvSLLj3zssa8khBQyqYTgbJq8rp4TcfnyQvPzViU9AJi6G9cKa7M1nTdneF6nmWs0TkhtWzTC+cAYPV7bcfTRmzxI/m2zalffyyiPFxXiwydUbdlbXF9Pf/BkFmRi9vqxubAAAAAElFTkSuQmCC' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAfCAYAAAAfrhY5AAAEWElEQVRYR72WfUwbdRjHn1/vSs+7Ky1XaKVrSQgvcwvZNBp5SbbMP7YFp0t8ISEp6vxjxqhgdInTJUa3GWMWg6ZuvqNxAzYwGGaULXMi4NbChsMNXWGWt1HelJdee6MtPfiZY8IGtLZ3uv3+a+95vp/n9XeHQOHBGOfywnirjk3OQAj1KpFBSpwkH39wuvTKaLN9tbmgnE3Q7VKioxjeO+msvNh33rY5pyTEajjqtsExxty3Fz5t+9M/lvno+lLg9PoNCKEzcgNQlDl/bbDww5ZXG7TEnbBtdSmYUow1NE0X3xb4xYGf36zpeOsNM50DhRnPg9GYAlqtVnYish2k7Jq6jzdUO/cVrjNvmoezLAMGg6GYJMkaOdnLhgcxn3m09UDbT5fruPut2+bhajUpwc8wDLPhlsL/4odtn7W8Vukaa4XctIcW4RzHAcMwFEIoFG8AsjPvHGmx1zjfLx3xuuEey+ZFOE3TkJSUtIsgiPJbBnf0HW095jiUOxMOQY554yJcrVZLpe9NSEjIiBs+5r2KA6IQl30oGIIfug7Db57rK30zXPptMBiAJMm4tCQj5B51YWfPN9A9dg7EWTGmoz84AbOzsxHhBEEAQrE7KdlpNBpAGGObZ7zffqLrS+7X3lMwh68Lx3OWZx6Pj2TDcdwkwzBl82FK16UwE7S7h9ttDb9/BJ4Jt/RfTC05cKkiFEUBy7JVFEWVIYQml9RIFEXbEO+yN7pquTb3yf8VrtPp5rMlSbJqQXhFg6QqBINB+9me47bGPw7DJD8u1SZiIPFk/s8KVqlUqvlsbxaKOh0iFm2u4Tb79x1fcEO8C8Lh8IoAYsEZhpnU6XRLso0LvjALnomr9uYrVbZzA9/BTGhpANHg0s7r9frF3kbrX+y9AIDzA/X4iOOALHhqampM7ZgGPp/vrsoLb7su9f8oq+xGozGLoij3v01tTPiQ99Jue+PL7/A+Xhacpumy5OTkD/4T/GRntaOu/d18AhFLdIKz1yAvfTtsSd8JJKGOxDiRlpb2oGK4gAVTRdPu0c6+s0CqbgCkgUpNzIKt64vBSt8NwemVmyBBrVZrMkJoQtHAdQ92PHOw5cVPwuHAor90L2+/78ne/FUl+xITE7/CGD/N8/xen89nXQ7RaDSPm0ymOkXw013HauvbDxaJYghUKhWk6jJg01pbxb2ZD+ylET24IIoxTg8EAvv9fr8tFLrxLaHRaA6ZTKYXZMMxxgmfN+/p+aX/tIVQkbBmVcHQljVPvZJtXlcdTUwUxZ1er3f/9PS0SbJBCLktFstahFDEvkSd9hGvp+i9xmdrBf84bMx+rOLhvB2vMyhlJNaFjzG28DxfLghC0dzcnPQG28qy7KlIflHhbX315Y6+hicKrI88l5tV+HUs6PLngUBgx9TUlPRJ9bHZbN4jC355rOmI/o7sl8yJZunNouhgjI2CIJRrtdqSuOEY43yEkFMRMYITxjgPIdS6/NHfsBG1QLyoq3AAAAAASUVORK5CYII=' }" />
<img width="16" src="\${rating > 3.5 ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAfCAYAAAAfrhY5AAADqElEQVRYR8WVfUzMcRzH3+eOuuNqOosopVNLpDKLkMdj1i1Km9nRg4exmWebhpGx9TBGD39gniajUWkqFblqRSd6sNx1SUp5WMgzPxV3X/u1dXVc3X1/Z/P79/t+v1+fz/f3+X1+PPzHh2cN+47mEpFNjuCcwdlY0VJ6taj+9MpYeTrnDM7Gy6pEUtGUjag5exEwYTmnHE4m9lUlFm4iLe01cHH0wP7gK5xyOJlK67NIjjoFDMNALB6Bo+ElnHI4ma5VpZJidZphVpf6RiHUfwt1FrWBJcYXKkhr+1MD3GPsVOxeco46i9rAEnelLyJM1xcD3NHeFYfDMqmzqA3lT2+RjMo4dP9iDHAbGxHCA3ZirjSUKo9KzNIyaxNIcV029ERvtJ9mSkMQHXSQKo9KzNLiciNI2/sGI7CO6CAZ6YyE0ByqvB7xqdI9pLVDC/JHN3+uXvb8a+cH6PXGXbM6Pp8Psa3E7LZmdWPs3LFVlsQzVHpcuYM0vrhn1mytQOrkgzC/PZg42qsPzoYWazKI8slFvP/yxlqGkZ/H42Gsgxumu4ZglocM9kKnnqaN3lFnN5Pc1FG5Tam+Du3r+/+sAO/xgZD7RSukDtPS+4eaHBCGYWIK6s8mqJpv4BvT9z3TViMebocFnhEI9o02yRl0Osuf5ZMKbRbaPmqg0+ksZg8dKoCjeAJme8qx0Gv1gAyLPo1U5VaiffXA5JSbqijAPRiLvKPgOsp90HyL4Jk1iaRYfd0iONt1oHsoFIExZrPNCtjOjuVvJE1vay2+dieJG2JDMsxmmxU0vislp0pie/7dNI9i5gHM9Vpm3bVnVCaTovo0DOEN+YvNbquBBnGGVI61QYesgx/JXUnaOprA5/ENcKHQFlKJPzzGeaGupRYvPmvR3dVlVNxI8WjEh+dxh1e3KHckKTefGC6w6/t92tpiw7y9kNrPTxGJRNvZg7vN10j2wzP4/uOTQTdMIETKmjJucOYnE5NZk5RQor4CG74QAoEAbg5TsdhHAV+XeX+Fal6pSKHmPJrf1EGn04NdqYGe4YgcZOoHrSw+L4K0djT0gP1dZVgfdMTsgKZXxZF72nz80nXBRTIJ+0PS6JdM1fOyaRdU+6qH6QWQ+6+DzDvSLLj3zssa8khBQyqYTgbJq8rp4TcfnyQvPzViU9AJi6G9cKa7M1nTdneF6nmWs0TkhtWzTC+cAYPV7bcfTRmzxI/m2zalffyyiPFxXiwydUbdlbXF9Pf/BkFmRi9vqxubAAAAAElFTkSuQmCC' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAfCAYAAAAfrhY5AAAEWElEQVRYR72WfUwbdRjHn1/vSs+7Ky1XaKVrSQgvcwvZNBp5SbbMP7YFp0t8ISEp6vxjxqhgdInTJUa3GWMWg6ZuvqNxAzYwGGaULXMi4NbChsMNXWGWt1HelJdee6MtPfiZY8IGtLZ3uv3+a+95vp/n9XeHQOHBGOfywnirjk3OQAj1KpFBSpwkH39wuvTKaLN9tbmgnE3Q7VKioxjeO+msvNh33rY5pyTEajjqtsExxty3Fz5t+9M/lvno+lLg9PoNCKEzcgNQlDl/bbDww5ZXG7TEnbBtdSmYUow1NE0X3xb4xYGf36zpeOsNM50DhRnPg9GYAlqtVnYish2k7Jq6jzdUO/cVrjNvmoezLAMGg6GYJMkaOdnLhgcxn3m09UDbT5fruPut2+bhajUpwc8wDLPhlsL/4odtn7W8Vukaa4XctIcW4RzHAcMwFEIoFG8AsjPvHGmx1zjfLx3xuuEey+ZFOE3TkJSUtIsgiPJbBnf0HW095jiUOxMOQY554yJcrVZLpe9NSEjIiBs+5r2KA6IQl30oGIIfug7Db57rK30zXPptMBiAJMm4tCQj5B51YWfPN9A9dg7EWTGmoz84AbOzsxHhBEEAQrE7KdlpNBpAGGObZ7zffqLrS+7X3lMwh68Lx3OWZx6Pj2TDcdwkwzBl82FK16UwE7S7h9ttDb9/BJ4Jt/RfTC05cKkiFEUBy7JVFEWVIYQml9RIFEXbEO+yN7pquTb3yf8VrtPp5rMlSbJqQXhFg6QqBINB+9me47bGPw7DJD8u1SZiIPFk/s8KVqlUqvlsbxaKOh0iFm2u4Tb79x1fcEO8C8Lh8IoAYsEZhpnU6XRLso0LvjALnomr9uYrVbZzA9/BTGhpANHg0s7r9frF3kbrX+y9AIDzA/X4iOOALHhqampM7ZgGPp/vrsoLb7su9f8oq+xGozGLoij3v01tTPiQ99Jue+PL7/A+Xhacpumy5OTkD/4T/GRntaOu/d18AhFLdIKz1yAvfTtsSd8JJKGOxDiRlpb2oGK4gAVTRdPu0c6+s0CqbgCkgUpNzIKt64vBSt8NwemVmyBBrVZrMkJoQtHAdQ92PHOw5cVPwuHAor90L2+/78ne/FUl+xITE7/CGD/N8/xen89nXQ7RaDSPm0ymOkXw013HauvbDxaJYghUKhWk6jJg01pbxb2ZD+ylET24IIoxTg8EAvv9fr8tFLrxLaHRaA6ZTKYXZMMxxgmfN+/p+aX/tIVQkbBmVcHQljVPvZJtXlcdTUwUxZ1er3f/9PS0SbJBCLktFstahFDEvkSd9hGvp+i9xmdrBf84bMx+rOLhvB2vMyhlJNaFjzG28DxfLghC0dzcnPQG28qy7KlIflHhbX315Y6+hicKrI88l5tV+HUs6PLngUBgx9TUlPRJ9bHZbN4jC355rOmI/o7sl8yJZunNouhgjI2CIJRrtdqSuOEY43yEkFMRMYITxjgPIdS6/NHfsBG1QLyoq3AAAAAASUVORK5CYII=' }" />
<img width="16" src="\${rating > 4.5 ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAfCAYAAAAfrhY5AAADqElEQVRYR8WVfUzMcRzH3+eOuuNqOosopVNLpDKLkMdj1i1Km9nRg4exmWebhpGx9TBGD39gniajUWkqFblqRSd6sNx1SUp5WMgzPxV3X/u1dXVc3X1/Z/P79/t+v1+fz/f3+X1+PPzHh2cN+47mEpFNjuCcwdlY0VJ6taj+9MpYeTrnDM7Gy6pEUtGUjag5exEwYTmnHE4m9lUlFm4iLe01cHH0wP7gK5xyOJlK67NIjjoFDMNALB6Bo+ElnHI4ma5VpZJidZphVpf6RiHUfwt1FrWBJcYXKkhr+1MD3GPsVOxeco46i9rAEnelLyJM1xcD3NHeFYfDMqmzqA3lT2+RjMo4dP9iDHAbGxHCA3ZirjSUKo9KzNIyaxNIcV029ERvtJ9mSkMQHXSQKo9KzNLiciNI2/sGI7CO6CAZ6YyE0ByqvB7xqdI9pLVDC/JHN3+uXvb8a+cH6PXGXbM6Pp8Psa3E7LZmdWPs3LFVlsQzVHpcuYM0vrhn1mytQOrkgzC/PZg42qsPzoYWazKI8slFvP/yxlqGkZ/H42Gsgxumu4ZglocM9kKnnqaN3lFnN5Pc1FG5Tam+Du3r+/+sAO/xgZD7RSukDtPS+4eaHBCGYWIK6s8mqJpv4BvT9z3TViMebocFnhEI9o02yRl0Osuf5ZMKbRbaPmqg0+ksZg8dKoCjeAJme8qx0Gv1gAyLPo1U5VaiffXA5JSbqijAPRiLvKPgOsp90HyL4Jk1iaRYfd0iONt1oHsoFIExZrPNCtjOjuVvJE1vay2+dieJG2JDMsxmmxU0vislp0pie/7dNI9i5gHM9Vpm3bVnVCaTovo0DOEN+YvNbquBBnGGVI61QYesgx/JXUnaOprA5/ENcKHQFlKJPzzGeaGupRYvPmvR3dVlVNxI8WjEh+dxh1e3KHckKTefGC6w6/t92tpiw7y9kNrPTxGJRNvZg7vN10j2wzP4/uOTQTdMIETKmjJucOYnE5NZk5RQor4CG74QAoEAbg5TsdhHAV+XeX+Fal6pSKHmPJrf1EGn04NdqYGe4YgcZOoHrSw+L4K0djT0gP1dZVgfdMTsgKZXxZF72nz80nXBRTIJ+0PS6JdM1fOyaRdU+6qH6QWQ+6+DzDvSLLj3zssa8khBQyqYTgbJq8rp4TcfnyQvPzViU9AJi6G9cKa7M1nTdneF6nmWs0TkhtWzTC+cAYPV7bcfTRmzxI/m2zalffyyiPFxXiwydUbdlbXF9Pf/BkFmRi9vqxubAAAAAElFTkSuQmCC' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAfCAYAAAAfrhY5AAAEWElEQVRYR72WfUwbdRjHn1/vSs+7Ky1XaKVrSQgvcwvZNBp5SbbMP7YFp0t8ISEp6vxjxqhgdInTJUa3GWMWg6ZuvqNxAzYwGGaULXMi4NbChsMNXWGWt1HelJdee6MtPfiZY8IGtLZ3uv3+a+95vp/n9XeHQOHBGOfywnirjk3OQAj1KpFBSpwkH39wuvTKaLN9tbmgnE3Q7VKioxjeO+msvNh33rY5pyTEajjqtsExxty3Fz5t+9M/lvno+lLg9PoNCKEzcgNQlDl/bbDww5ZXG7TEnbBtdSmYUow1NE0X3xb4xYGf36zpeOsNM50DhRnPg9GYAlqtVnYish2k7Jq6jzdUO/cVrjNvmoezLAMGg6GYJMkaOdnLhgcxn3m09UDbT5fruPut2+bhajUpwc8wDLPhlsL/4odtn7W8Vukaa4XctIcW4RzHAcMwFEIoFG8AsjPvHGmx1zjfLx3xuuEey+ZFOE3TkJSUtIsgiPJbBnf0HW095jiUOxMOQY554yJcrVZLpe9NSEjIiBs+5r2KA6IQl30oGIIfug7Db57rK30zXPptMBiAJMm4tCQj5B51YWfPN9A9dg7EWTGmoz84AbOzsxHhBEEAQrE7KdlpNBpAGGObZ7zffqLrS+7X3lMwh68Lx3OWZx6Pj2TDcdwkwzBl82FK16UwE7S7h9ttDb9/BJ4Jt/RfTC05cKkiFEUBy7JVFEWVIYQml9RIFEXbEO+yN7pquTb3yf8VrtPp5rMlSbJqQXhFg6QqBINB+9me47bGPw7DJD8u1SZiIPFk/s8KVqlUqvlsbxaKOh0iFm2u4Tb79x1fcEO8C8Lh8IoAYsEZhpnU6XRLso0LvjALnomr9uYrVbZzA9/BTGhpANHg0s7r9frF3kbrX+y9AIDzA/X4iOOALHhqampM7ZgGPp/vrsoLb7su9f8oq+xGozGLoij3v01tTPiQ99Jue+PL7/A+Xhacpumy5OTkD/4T/GRntaOu/d18AhFLdIKz1yAvfTtsSd8JJKGOxDiRlpb2oGK4gAVTRdPu0c6+s0CqbgCkgUpNzIKt64vBSt8NwemVmyBBrVZrMkJoQtHAdQ92PHOw5cVPwuHAor90L2+/78ne/FUl+xITE7/CGD/N8/xen89nXQ7RaDSPm0ymOkXw013HauvbDxaJYghUKhWk6jJg01pbxb2ZD+ylET24IIoxTg8EAvv9fr8tFLrxLaHRaA6ZTKYXZMMxxgmfN+/p+aX/tIVQkbBmVcHQljVPvZJtXlcdTUwUxZ1er3f/9PS0SbJBCLktFstahFDEvkSd9hGvp+i9xmdrBf84bMx+rOLhvB2vMyhlJNaFjzG28DxfLghC0dzcnPQG28qy7KlIflHhbX315Y6+hicKrI88l5tV+HUs6PLngUBgx9TUlPRJ9bHZbN4jC355rOmI/o7sl8yJZunNouhgjI2CIJRrtdqSuOEY43yEkFMRMYITxjgPIdS6/NHfsBG1QLyoq3AAAAAASUVORK5CYII=' }" />
</div>
</div>
<div class="app-banner-close">
<a href="javascript:">&#10005;</a>
</div>
</div>
<div class="app-banner-row">
<div class="app-banner-spacer">&nbsp;</div>
<div class="app-banner-play">
<img height="24" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJkAAAAkCAYAAACaCJgRAAALi0lEQVR4Xu3ciXMUVRcF8BdFERXFBdxQNlFcEAERF1BBNhdU/lDjrqggRNBCg0spiID7CoKKAipLjPV79d2pTts9M0km9QmZW5WqZOb16/fuO/fcc2839AwODg6mlNLPP/+cLr/88tTT0+PPrnU90DEP9PT39w9eccUV6eqrr059fX3pzjvvzL93reuBTnmg55lnnhn8+++/M7iuvfba9Nprr6ULLrggLVmyJF122WWduk93nnHsgZ6nn346p0u2YMGCdN1112WgnT59Ol155ZXprrvuShdeeOE4dlF366P1wBCQmeyOO+5I119/fXr11Vcz0BiGW7x4cTrvvPNGe7/u9ePQA/8CGR/Mnz8/3XDDDUOA5vM5c+bk784555xx6KrulkfqgUqQmez2229PM2fOzEA7depUY/5zzz033XLLLWnu3LndSnSkXh9n19WCjB9uvfXWNHv27KzRTp48OcQ1EyZMSAsXLsyptWtdDzTzQFOQufCmm27KP2VGi0knTZqU9dq0adO6nu56oNIDLUEWQLv55psz0MqMFrNeeuml6e67706TJ0/uurrrgSEeaAtkIfqlz9dffz399ddflW708ACj6bHptY2F/f777+nLL79MBw8ebGhFbKpQoSGl8TPBjhw5kr766qvcHpIpOmHvvvtu5dmcf/75uTXlp1i07d+/Px0/fjxLHg35sbK2QWYB9JmCYNOmTemPP/5ouqbp06dnzdaptgdgb9++PQFZ2P+eiDUKEI/EZs2alRvL/3X79ttvU39/fz7cBx98sCPL3bhxY9NzUbTdf//9uf/Jtm7dmn755Zcsd2bMmNGRNVRNMiyQmcAh6qUBmihoZqJGmlWNjsZ+++23tGXLluTJBLvmmmty9F988cUZYAD4+eefZ2YINl22bNlobjnm1wbIPC9+6KGHOnK/AJmm+iWXXNKY89ixY2n37t0NqbN27dp00UUX/XdBZuVS06JFi9LmzZvT0aNHax0EAKJUBH333XcZbMPtsdGAL7/8cgaYFOxA6p5AYNc333wzjxmrdN0RNKSUxhJkK1euTFOmTBmy1IGBgSx1+Gjq1Klp+fLl/22QWb08jmbfeOONISksdgZMNuupgYMHEmDDgrRTu297vPXWW1l/SbuPPPJIS82F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" alt="" />
</div>
<div class="app-banner-button" colspan="2">
<a href="\${url}" target="_blank">\${button}</a>
</div>
</div>
</div>`,
css: `
body {
position: relative;
min-height: 100vh;
}
.app-banner {
display: table;
position: fixed;
z-index: 9999999;
left: 0;
box-sizing: border-box;
width: 100%;
margin: 0;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.app-banner.android {
bottom: -170px;
background-color: #ffffff;
box-shadow: rgba(0, 0, 0, 0.33) 0px 0px 14px 0px;
font-family: Roboto, RobotoDraft, Helvetica, Arial, sans-serif;
font-size: 16px;
padding: 20px;
}
.app-banner.android .app-banner-row {
display: table-row;
}
.app-banner.android .app-banner-row > div {
display: table-cell;
vertical-align: middle;
text-align: left;
padding: 0;
margin: 0;
}
.app-banner.android .app-banner-row .app-banner-icon {
text-align: center;
width: 16.6666%;
}
.app-banner.android .app-banner-row .app-banner-icon img {
max-width: 100%;
}
.app-banner.android .app-banner-row .app-banner-content {
padding-left: 10px;
}
.app-banner.android .app-banner-row .app-banner-content .app-banner-title {
font-weight: 600;
color: #30302e;
margin-bottom: 2px;
}
.app-banner.android .app-banner-row .app-banner-close {
width: 16.6666%;
text-align: right;
vertical-align: top;
font-size: 24px;
}
.app-banner.android .app-banner-row .app-banner-close a, .app-banner.android .app-banner-row .app-banner-close a:hover, .app-banner.android .app-banner-row .app-banner-close a:active, .app-banner.android .app-banner-row .app-banner-close a:visited, .app-banner.android .app-banner-row .app-banner-close a:focus {
color: #666;
font-weight: 600;
text-decoration: none;
}
.app-banner.android .app-banner-row .app-banner-spacer {
width: 16.6666%;
}
.app-banner.android .app-banner-row .app-banner-play {
padding-left: 10px;
}
.app-banner.android .app-banner-row .app-banner-button {
width: 33.3333%;
text-align: right;
}
.app-banner.android .app-banner-row .app-banner-button a, .app-banner.android .app-banner-row .app-banner-button a:hover, .app-banner.android .app-banner-row .app-banner-button a:active, .app-banner.android .app-banner-row .app-banner-button a:visited, .app-banner.android .app-banner-row .app-banner-button a:focus {
background-color: #689f38;
box-shadow: rgba(0, 0, 0, 0.05) 0px 1px 0px 0px;
border-radius: 4px;
color: white;
cursor: pointer;
font-weight: 600;
height: auto;
line-height: 54px;
padding: 12px 16px;
text-align: center;
text-decoration: none;
text-transform: uppercase;
}`
}
AppBanner.prototype = {
init: async function() {
var OS = AppBanner.OS;
// set URL
this.url = this.url.replace('${store}',encodeURI(this.store)).replace('${id}',encodeURI(this.id));
if(!(this.options.ajax === false)){
// use AJAX to lookup from ID
var that = this;
$.ajax({
dataType: OS == 'ios' ? 'json' : '',
url: OS == 'ios' ?
//'http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/wa/wsLookup?country=gb&id='+this.id :
'https://itunes.apple.com/lookup?id='+this.id :
//'https://play.google.com/store/apps/details?id='+this.id,
//'https://cors-anywhere.herokuapp.com/https://play.google.com/store/apps/details?id='+this.id,
//'https://crossorigin.me/https://play.google.com/store/apps/details?id='+this.id,
//'http://www.whateverorigin.org/get?url=' + encodeURIComponent('https://play.google.com/store/apps/details?id='+this.id) + '&callback=?',
'https://api.allorigins.win/get?url=' + encodeURIComponent('https://play.google.com/store/apps/details?id='+this.id),
success: function(data, textStatus, jqXHR){
if(OS == 'ios'){
if(data && data.results && data.results.length){
that.icon = that.icon || data.results[0].artworkUrl512.replace(/source\/.*/,'source/100x100-75.png');
that.rating = that.rating || data.results[0].averageUserRating;
that.title = that.title || data.results[0].trackName;
}
} else {
var $doc = $($.parseHTML(data.contents)); //$($.parseHTML(data));
that.icon = that.icon || $doc.find('img[alt="Cover art"]').attr('src')
if(!that.icon) that.icon = $doc.find('[itemprop=image]').eq(0).attr('src')
that.title = that.title || $doc.find('[itemprop=name]').eq(0).text()
that.rating = parseFloat($doc.find('[href^="https://play.google.com/about/comment-posting-policy"]').parent().parent().find('[aria-label^=Rated]').eq(0).text())
}
},
error: function(){
console.error('App banner: AJAX error',arguments);
},
complete: function(){
that.init2()
}
});
} else {
this.init2()
}
},
init2: async function(){
var OS = AppBanner.OS;
// Check we have icon from AJAX
if(!this.icon){
if (this.options.icon) {
if(typeof this.options.icon == 'string')
this.icon = this.options.icon;
else if(typeof this.options.icon == 'object' && this.options.icon[OS])
this.icon = this.options.icon[OS]
}
}
if(!this.icon){
// try to get the highest resolution application icon from tags in head of current page
if (OS == 'ios') {
var appleLinks = $('head link[rel^=apple-touch-icon]').map(function(i,link){
return {n:link, s: $(link).attr('sizes') ? parseInt($(link).attr('sizes').split('x')[0]) : -1}
}).sort(function(l1, l2) { return l1.s > l2.s ? -1 : l1.s < l2.s ? 1 : 0; });
if(appleLinks.length) this.icon = appleLinks[0].n.href;
} else {
var androidLinks = $('head link[rel^="shortcut icon"], head link[rel^=apple-touch-icon]').map(function(i,link){
return {n:link, s: $(link).attr('sizes') ? parseInt($(link).attr('sizes').split('x')[0]) : -1}
}).sort(function(l1, l2) { return l1.s > l2.s ? -1 : l1.s < l2.s ? 1 : 0; });
if(androidLinks.length) this.icon = androidLinks[0].n.href;
}
}
if (this.options.onInit) {
this.options.onInit.call(this);
}
var that = this;
this.addToDom().then(function(){
if (that.options.showAfter == 0) {
console.log("App banner: displaying banner immediately");
that.show();
} else if (that.options.showAfter > 0) {
setTimeout(that.show.bind(that), that.options.showAfter * 1000);
}
});
},
getHtml: function(){
return AppBanner.template(this.html, this);
},
isInDom: function(){
return this.$el.parents('body').length > 0
},
isShowing: function(){
return this.isInDom() && parseFloat(this.$el.css(AppBanner.OS == 'ios' ? 'top' : 'bottom')) > -80
},
addToDom: function () {
var that = this;
return new Promise(function (resolve, reject) {
AppBanner.waitForDom().then(function(){
// add css
if(!$('#app-banner-style').length)
$('head').append('<style type="text/css" id="app-banner-css">'+that.css+'</style>');
if(!that.$el){
// create the element
that.$el = $(that.getHtml());
}
if(!that.isInDom()){
// attach all elements to the DOM
that.$container.append(that.$el);
var eventsNeedAdding = true, keys = Object.keys(that.$el[0]).filter(function(k){return k.substr(0,6) == 'jQuery'})
if(keys.length){
keys.forEach(function(k){
if(that.$el[0][k] && that.$el[0][k].events && Object.keys(that.$el[0][k].events).length > 0) eventsNeedAdding = false;
})
}
if(eventsNeedAdding) that.addEventListeners();
} else console.warn("App banner: banner already in DOM");
that.waitForIcon().then(function(){
that.isReady = true;
resolve();
})
})
})
},
addEventListeners: function () {
this.$el.on('touchmove', AppBanner.preventDefault);
this.$el.on('click', '.app-banner-close a', this.hide.bind(this));
this.$el.find('.app-banner-icon img').on('load', this.setIconLoaded.bind(this));
},
removeEventListeners: function () {
this.$el.off('touchmove', AppBanner.preventDefault);
this.$el.off('click', '.app-banner-close a');
this.$el.find('.app-banner-icon img').off('load');
},
setIconLoaded: function(){
this.iconLoaded = true;
},
waitForIcon: function(){
var that = this;
return new Promise(function (resolve, reject) {
if(that.iconLoaded || $('.app-banner-icon img')[0].complete){
that.iconLoaded = true;
resolve()
} else {
(function pollIcon(){
if (that.iconLoaded) return resolve();
setTimeout(pollIcon, 30);
})();
}
});
},
waitForReady: function(){
var that = this;
return new Promise(function (resolve, reject) {
(function pollReady(){
if (that.isReady) return resolve();
setTimeout(pollReady, 30);
})();
});
},
show: function(){
var that = this, OS = AppBanner.OS;
if(this.isShowing()){
console.warn('App banner: already showing');
} else if(this.isAnimating){
console.warn('App banner: already animating');
} else if(!this.isInDom()){
this.addToDom().then(function(){
that.show();
});
} else if(!this.isReady){
console.warn('App banner: not ready, waiting before show');
this.waitForReady().then(function(){ that.show() })
} else {
this.isAnimating = true;
if(OS == 'ios'){
this.$el.animate({
top: '0px'
}, 1000, function(){ that.isAnimating = false; console.log('App banner: showing'); })
$('body').animate({
paddingTop: '+=90px'
}, 1000)
} else {
this.$el.animate({
bottom: '0px'
}, 1000, function(){ that.isAnimating = false; console.log('App banner: showing'); })
}
}
},
hide: function(){
var that = this, doRemove = this.options.removeOnHide !== false;
if(!this.isShowing()){
console.warn('App banner: already hidden');
return;
}
if(this.isAnimating){
console.warn('App banner: already animating');
return;
}
// fire the custom onRemove event
if (this.options.onHide) this.options.onHide.call(this);
this.isAnimating = true;
if(AppBanner.OS == 'ios'){
this.$el.animate({
top: '-90px'
}, 1000, function(){ that.isAnimating = false; if(doRemove) that.remove(); else console.log('App banner: hidden'); })
$('body').animate({
paddingTop: '-=90px'
}, 1000)
} else {
this.$el.animate({
bottom: '-170px'
}, 1000, function(){ that.isAnimating = false; if(doRemove) that.remove(); else console.log('App banner: hidden'); })
}
},
remove: function () {
var that = this, removeOnHide = this.options.removeOnHide !== false;
if(this.isShowing()) {
if(removeOnHide) this.hide()
else this.hide().then(function(){ that.remove(); })
return;
}
if(!this.isInDom()) {
console.warn('App banner: already removed');
return;
}
this.removeEventListeners();
this.$el.remove();
this.isReady = this.iconLoaded = false;
// fire the custom onRemove event
if (this.options.onRemove) this.options.onRemove.call(this);
console.log('App banner: removed');
}
};
// expose to the world
window.AppBanner = AppBanner;
})(this, this.document, this.jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment