1/*! 2 * jQuery Smart Banner 3 * Copyright (c) 2012 Arnold Daniels <[email protected]> 4 * Based on 'jQuery Smart Web App Banner' by Kurt Zenisek @ kzeni.com 5 */ 6(function (root, factory) { 7 if (typeof define == "function" && define.amd) { 8 define(["jquery"], factory); 9 } else { 10 factory(root.jQuery); 11 } 12})(this, function ($) { 13 var UA = navigator.userAgent; 14 var isEdge = /Edge/i.test(UA); 15 16 var SmartBanner = function (options) { 17 // Get the original margin-top of the HTML element so we can take that into account. 18 this.origHtmlMargin = parseFloat($("html").css("margin-top")); 19 this.options = $.extend({}, $.smartbanner.defaults, options); 20 21 // Check if it's already a standalone web app or running within a webui view of an app (not mobile safari). 22 var standalone = navigator.standalone; 23 24 // Detect banner type (iOS or Android). 25 if (this.options.force) { 26 this.type = this.options.force; 27 } else if ( 28 UA.match(/Windows Phone/i) !== null && 29 UA.match(/Edge|Touch/i) !== null 30 ) { 31 this.type = "windows"; 32 } else if ( 33 UA.match(/iPhone|iPod/i) !== null || 34 (UA.match(/iPad/) && this.options.iOSUniversalApp) 35 ) { 36 if ( 37 UA.match(/Safari/i) !== null || 38 UA.match(/CriOS/i) !== null || 39 UA.match(/FxiOS/i) != null 40 ) { 41 // Check webview and native smart banner support (iOS 6+). 42 this.type = "ios"; 43 } 44 } else if ( 45 UA.match(/\bSilk\/(.*\bMobile Safari\b)?/) || 46 UA.match(/\bKF\w/) || 47 UA.match("Kindle Fire") 48 ) { 49 this.type = "kindle"; 50 } else if (UA.match(/Android/i) !== null) { 51 this.type = "android"; 52 } 53 // Don't show banner if device isn't iOS or Android, website is loaded in app or user dismissed banner. 54 if (
55 !this.type || 56 standalone || 57 this.getCookie("sb-closed") || 58 this.getCookie("sb-installed") 59 ) { 60 return; 61 } 62 // Calculate scale. 63 this.scale = 64 this.options.scale == "auto" 65 ? $(window).width() / window.screen.width 66 : this.options.scale; 67 if (this.scale < 1) { 68 this.scale = 1; 69 } 70 // Get info from meta data. 71 var meta = $( 72 this.type == "android" 73 ? 'meta[name="google-play-app"]' 74 : this.type == "ios" 75 ? 'meta[name="apple-app-store"]' 76 : this.type == "kindle" 77 ? 'meta[name="kindle-fire-app"]' 78 : 'meta[name="msApplication-ID"]' 79 ); 80 81 if (!meta.length) { 82 return; 83 } 84 // For Windows Store apps, get the PackageFamilyName for protocol launch. 85 if (this.type == "windows") { 86 if (isEdge) { 87 this.appId = $('meta[name="msApplication-PackageEdgeName"]').attr( 88 "content" 89 ); 90 } 91 if (!this.appId) { 92 this.appId = $('meta[name="msApplication-PackageFamilyName"]').attr( 93 "content" 94 ); 95 } 96 } else { 97 // Try to pull the appId out of the meta tag and store the result. 98 var parsedMetaContent = /app-id=([^\s,]+)/.exec(meta.attr("content")); 99 if (parsedMetaContent) { 100 this.appId = parsedMetaContent[1]; 101 } else { 102 return; 103 } 104 } 105 this.title = this.options.title 106 ? this.options.title 107 : meta.data("title") || 108 $("title") 109 .text() 110 .replace(/\s*[|\-·].*$/, ""); 111 112 this.author = this.options.author 113 ? this.options.author 114 : meta.data("author") || 115 ($('meta[name="author"]').length 116 ? $('meta[name="author"]').attr("content") 117 : window.location.hostname); 118 119 this.iconUrl = meta.data("icon-url"); 120 this.price = meta.data("price"); 121 122 // Set default onInstall callback if not set in options. 123 if (typeof this.options.onInstall == "function") { 124 this.options.onInstall = this.options.onInstall; 125 } else { 126 this.options.onInstall = function () {}; 127 } 128 // Set default onClose callback if not set in options. 129 if (typeof this.options.onClose == "function") { 130 this.options.onClose = this.options.onClose; 131 } else { 132 this.options.onClose = function () {}; 133 } 134 // Create banner. 135 this.create(); 136 this.show(); 137 this.listen(); 138 }; 139 140 SmartBanner.prototype = { 141 constructor: SmartBanner, 142 143 create: function () { 144 var iconURL; 145 var price = this.price || this.options.price; 146 147 var link = 148 this.options.url || 149 function () { 150 switch (this.type) { 151 case "android": 152 return "market://details?id="; 153 case "kindle": 154 return "amzn://apps/android?asin="; 155 case "windows": 156 return isEdge 157 ? "ms-windows-store://pdp/?productid=" 158 : "ms-windows-store:navigate?appid="; 159 } 160 return ( 161 "https://itunes.apple.com/" + 162 this.options.appStoreLanguage + 163 "/app/id" 164 ); 165 }.call(this) + this.appId; 166 167 var inStore = !price 168 ? "" 169 : function () { 170 var result = price + " - "; 171 switch (this.type) { 172 case "android": 173 return result + this.options.inGooglePlay; 174 case "kindle": 175 return result + this.options.inAmazonAppStore; 176 case "windows": 177 return result + this.options.inWindowsStore; 178 } 179 return result + this.options.inAppStore; 180 }.call(this); 181 182 var gloss = 183 this.options.iconGloss == null 184 ? this.type == "ios" 185 : this.options.iconGloss; 186 187 if (this.type == "android" && this.options.GooglePlayParams) { 188 link += "&referrer=" + this.options.GooglePlayParams; 189 } 190 var banner = 191 '<div id="smartbanner" class="' + 192 this.type + 193 '">' + 194 '<div class="sb-container">' + 195 '<button type="button" class="sb-close"><img src="/file_source/assets/icons/icon-close.svg"></button>' + 196 '<span class="sb-icon"></span>' + 197 '<div class="sb-info">' + 198 '<span class="sb-info__title">' + 199 this.title + 200 "</span>" + 201 '<span class="sb-info__author">' + 202 this.author + 203 "</span>" + 204 //'<span class="sb-info__price">' + inStore + '</span>' + 205 "</div>" + 206 '<a href="' + 207 link + 208 '" class="sb-button button button--small" data-id="link_content_app smart banner" data-text="view button click">' + 209 "<span>" + 210 this.options.button + 211 "</span>" + 212 "</a>" + 213 "</div>" + 214 "</div>"; 215 if (this.options.layer) { 216 $(this.options.appendToSelector).append(banner); 217 } else { 218 $(this.options.appendToSelector).prepend(banner); 219 } 220 if (this.options.icon) { 221 iconURL = this.options.icon; 222 } else if (this.iconUrl) { 223 iconURL = this.iconUrl; 224 } else if ($('link[rel="apple-touch-icon-precomposed"]').length > 0) { 225 iconURL = $('link[rel="apple-touch-icon-precomposed"]').attr("href");
226 if (this.options.iconGloss == null) { 227 gloss = false; 228 } 229 } else if ($('link[rel="apple-touch-icon"]').length > 0) { 230 iconURL = $('link[rel="apple-touch-icon"]').attr("href"); 231 } else if ($('meta[name="msApplication-TileImage"]').length > 0) { 232 iconURL = $('meta[name="msApplication-TileImage"]').attr("content"); 233 } else if ($('meta[name="msapplication-TileImage"]').length > 0) { 234 // Redundant because ms docs show two case usages. 235 iconURL = $('meta[name="msapplication-TileImage"]').attr("content"); 236 } 237 if (iconURL) { 238 $("#smartbanner .sb-icon").css( 239 "background-image", 240 "url(" + iconURL + ")" 241 ); 242 if (gloss) { 243 $("#smartbanner .sb-icon").addClass("gloss"); 244 } 245 } else { 246 $("#smartbanner").addClass("no-icon"); 247 } 248 this.bannerHeight = $("#smartbanner").outerHeight() + 2; 249 250 if (this.scale > 1) { 251 $("#smartbanner") 252 .css("top", parseFloat($("#smartbanner").css("top")) * this.scale) 253 .css( 254 "height", 255 parseFloat($("#smartbanner").css("height")) * this.scale 256 ) 257 .hide(); 258 $("#smartbanner .sb-container") 259 .css("-webkit-transform", "scale(" + this.scale + ")") 260 .css("-msie-transform", "scale(" + this.scale + ")") 261 .css("-moz-transform", "scale(" + this.scale + ")") 262 .css("width", $(window).width() / this.scale); 263 } 264 $("#smartbanner").css( 265 "position", 266 this.options.layer ? "absolute" : "static" 267 ); 268 }, 269 270 listen: function () { 271 $("#smartbanner .sb-close").on("click", $.proxy(this.close, this)); 272 $("#smartbanner .sb-button").on("click", $.proxy(this.install, this)); 273 }, 274 275 show: function (callback) { 276 var banner = $("#smartbanner"); 277 banner.stop(); 278 279 if (this.options.layer) { 280 banner 281 .animate( 282 { 283 top: 0, 284 display: "block", 285 }, 286 this.options.speedIn 287 ) 288 .addClass("shown") 289 .show(); 290 $(this.pushSelector).animate( 291 { 292 paddingTop: this.origHtmlMargin + this.bannerHeight * this.scale, 293 }, 294 this.options.speedIn, 295 "swing", 296 callback 297 ); 298 } else { 299 if ($.support.transition) { 300 banner 301 .animate( 302 { 303 top: 0, 304 }, 305 this.options.speedIn 306 ) 307 .addClass("shown"); 308 var transitionCallback = function () { 309 $("html").removeClass("sb-animation"); 310 if (callback) { 311 callback(); 312 } 313 }; 314 $(this.pushSelector) 315 .addClass("sb-animation") 316 .one($.support.transition.end, transitionCallback) 317 .emulateTransitionEnd(this.options.speedIn) 318 .css( 319 "margin-top", 320 this.origHtmlMargin + this.bannerHeight * this.scale 321 ); 322 } else { 323 banner.slideDown(this.options.speedIn).addClass("shown"); 324 } 325 } 326 }, 327 328 hide: function (callback) { 329 var banner = $("#smartbanner"); 330 banner.stop(); 331 332 if (this.options.layer) { 333 banner 334 .animate( 335 { 336 top: -1 * this.bannerHeight * this.scale, 337 display: "block", 338 }, 339 this.options.speedIn 340 ) 341 .removeClass("shown"); 342 343 $(this.pushSelector).animate( 344 { 345 paddingTop: this.origHtmlMargin, 346 }, 347 this.options.speedIn, 348 "swing", 349 callback 350 ); 351 } else { 352 if ($.support.transition) { 353 if (this.type !== "android") { 354 banner 355 .css({ 356 display: "none", 357 }) 358 .removeClass("shown"); 359 } else { 360 banner 361 .css({ 362 display: "none", 363 }) 364 .removeClass("shown"); 365 } 366 var transitionCallback = function () { 367 $("html").removeClass("sb-animation"); 368 if (callback) { 369 callback(); 370 } 371 }; 372 $(this.pushSelector) 373 .addClass("sb-animation") 374 .one($.support.transition.end, transitionCallback) 375 .emulateTransitionEnd(this.options.speedOut) 376 .css("margin-top", this.origHtmlMargin); 377 } else { 378 banner.slideUp(this.options.speedOut).removeClass("shown"); 379 } 380 } 381 }, 382 383 close: function (e) { 384 e.preventDefault(); 385 this.hide();
386 this.setCookie("sb-closed", "true", this.options.daysHidden); 387 this.options.onClose(e); 388 }, 389 390 install: function (e) { 391 if (this.options.hideOnInstall) { 392 this.hide(); 393 } 394 this.setCookie("sb-installed", "true", this.options.daysReminder); 395 this.options.onInstall(e); 396 }, 397 398 setCookie: function (name, value, exdays) { 399 var exdate = new Date(); 400 exdate.setDate(exdate.getDate() + exdays); 401 value = 402 encodeURI(value) + 403 (exdays == null ? "" : "; expires=" + exdate.toUTCString()); 404 document.cookie = name + "=" + value + "; path=/;"; 405 }, 406 407 getCookie: function (name) { 408 var i, 409 x, 410 y, 411 ARRcookies = document.cookie.split(";"); 412 for (i = 0; i < ARRcookies.length; i++) { 413 x = ARRcookies[i].substr(0, ARRcookies[i].indexOf("=")); 414 y = ARRcookies[i].substr(ARRcookies[i].indexOf("=") + 1); 415 x = x.replace(/^\s+|\s+$/g, ""); 416 if (x == name) { 417 return decodeURI(y); 418 } 419 } 420 return null; 421 }, 422 423 // Demo only. 424 switchType: function () { 425 var that = this; 426 427 this.hide(function () { 428 that.type = that.type == "android" ? "ios" : "android"; 429 var meta = $( 430 that.type == "android" 431 ? 'meta[name="google-play-app"]' 432 : 'meta[name="apple-app-store"]' 433 ).attr("content"); 434 that.appId = /app-id=([^\s,]+)/.exec(meta)[1]; 435 436 $("#smartbanner").detach(); 437 that.create(); 438 that.show(); 439 }); 440 }, 441 }; 442 443 $.smartbanner = function (option) { 444 var $window = $(window); 445 var data = $window.data("smartbanner"); 446 var options = typeof option == "object" && option; 447 if (!data) { 448 $window.data("smartbanner", (data = new SmartBanner(options))); 449 } 450 if (typeof option == "string") { 451 data[option](); 452 } 453 }; 454 455 // override these globally if you like (they are all optional) 456 $.smartbanner.defaults = { 457 title: "Standard Bank / Stanbic Bank App", // What the title of the app should be in the banner (defaults to <title>) 458 author: "Standard Bank Group", // What the author of the app should be in the banner (defaults to <meta name="author"> or hostname) 459 price: "FREE", // Price of the app 460 appStoreLanguage: "us", // Language code for App Store 461 inAppStore: "On the App Store", // Text of price for iOS 462 inGooglePlay: "In Google Play", // Text of price for Android 463 inAmazonAppStore: "In the Amazon Appstore", 464 inWindowsStore: "In the Windows Store", //Text of price for Windows 465 GooglePlayParams: null, // Aditional parameters for the market 466 icon: "https://www.standardbank.co.za/static_file/assets/favicons/apple-icon-180x180.png", // The URL of the icon (defaults to <meta name="apple-touch-icon">) 467 iconGloss: null, // Force gloss effect for iOS even for precomposed 468 button: "OPEN", // Text for the install button 469 url: null, // The URL for the button. Keep null if you want the button to link to the app store. 470 scale: "auto", // Scale based on viewport size (set to 1 to disable) 471 speedIn: 300, // Show animation speed of the banner 472 speedOut: 400, // Close animation speed of the banner 473 daysHidden: 15, // Duration to hide the banner after being closed (0 = always show banner) 474 daysReminder: 90, // Duration to hide the banner after "VIEW" is clicked *separate from when the close button is clicked* (0 = always show banner) 475 force: null, // Choose 'ios', 'android' or 'windows'. Don't do a browser check, just always show this banner 476 hideOnInstall: true, // Hide the banner after "VIEW" is clicked. 477 layer: false, // Display as overlay layer or slide down the page 478 iOSUniversalApp: true, // If the iOS App is a universal app for both iPad and iPhone, display Smart Banner to iPad users, too. 479 appendToSelector: "body", //Append the banner to a specific selector 480 pushSelector: "html", // What element is going to push the site content down; this is where the banner append animation will start. 481 }; 482 483 $.smartbanner.Constructor = SmartBanner; 484 485 // ============================================================ 486 // Bootstrap transition 487 // Copyright 2011-2014 Twitter, Inc. 488 // Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 489 490 function transitionEnd() { 491 var el = document.createElement("smartbanner"); 492 493 var transEndEventNames = { 494 WebkitTransition: "webkitTransitionEnd", 495 MozTransition: "transitionend", 496 OTransition: "oTransitionEnd otransitionend", 497 transition: "transitionend", 498 }; 499 500 for (var name in transEndEventNames) { 501 if (el.style[name] !== undefined) { 502 return { 503 end: transEndEventNames[name], 504 }; 505 } 506 } 507 // Explicit for ie8. 508 return false; 509 }
510 if ($.support.transition !== undefined) { 511 // Prevent conflict with Twitter Bootstrap. 512 return; 513 } 514 515 // http://blog.alexmaccaw.com/css-transitions 516 $.fn.emulateTransitionEnd = function (duration) { 517 var called = false, 518 $el = this; 519 $(this).one($.support.transition.end, function () { 520 called = true; 521 }); 522 var callback = function () { 523 if (!called) { 524 $($el).trigger($.support.transition.end); 525 } 526 }; 527 setTimeout(callback, duration); 528 return this; 529 }; 530 531 $(function () { 532 $.support.transition = transitionEnd(); 533 }); 534 // ============================================================ 535}); 536 537$(function () { 538 // if (!(/(iPad|iPhone|iPod).*OS [6-7].*AppleWebKit.*Mobile.*Safari/.test(navigator.userAgent))) { 539 $.smartbanner(); 540 // } 541});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.