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 ($) { 7 var SmartBanner = function (options) { 8 this.origHtmlMargin = parseFloat($('html').css('margin-top')) // Get the original margin-top of the HTML element so we can take that into account 9 this.options = $.extend({}, $.smartbanner.defaults, options) 10 11 var standalone = navigator.standalone // Check if it's already a standalone web app or running within a webui view of an app (not mobile safari) 12 , UA = navigator.userAgent 13 14 // Detect banner type (iOS or Android) 15 if (this.options.force) { 16 this.type = this.options.force 17 } else if (UA.match(/Windows Phone 8/i) != null && UA.match(/Touch/i) !== null) { 18 this.type = 'windows' 19 } else if (UA.match(/iPhone|iPod/i) != null || (UA.match(/iPad/) && this.options.iOSUniversalApp)) { 20 if (UA.match(/Safari/i) != null && 21 (UA.match(/CriOS/i) != null || 22 window.Number(UA.substr(UA.indexOf('OS ') + 3, 3).replace('_', '.')) < 6)) this.type = 'ios' // Check webview and native smart banner support (iOS 6+) 23 } else if (UA.match(/\bSilk\/(.*\bMobile Safari\b)?/) || UA.match(/\bKF\w/) || UA.match('Kindle Fire')) { 24 this.type = 'kindle' 25 } else if (UA.match(/Android/i) != null) { 26 this.type = 'android' 27 } 28 29 // Don't show banner if device isn't iOS or Android, website is loaded in app or user dismissed banner 30 if (!this.type || standalone || this.getCookie('sb-closed') || this.getCookie('sb-installed')) { 31 return 32 } 33 34 // Calculate scale 35 this.scale = this.options.scale == 'auto' ? $(window).width() / window.screen.width : this.options.scale 36 if (this.scale < 1) this.scale = 1 37 38 // Get info from meta data 39 var meta = $(this.type == 'android' ? 'meta[name="google-play-app"]' : 40 this.type == 'ios' ? 'meta[name="apple-itunes-app"]' : 41 this.type == 'kindle' ? 'meta[name="kindle-fire-app"]' : 'meta[name="msApplication-ID"]'); 42 if (meta.length == 0) return 43 44 // For Windows Store apps, get the PackageFamilyName for protocol launch 45 if (this.type == 'windows') { 46 this.appId = $('meta[name="msApplication-PackageFamilyName"]').attr('content'); 47 } else { 48 // Try to pull the appId out of the meta tag and store the result 49 var parsedMetaContent = /app-id=([^\s,]+)/.exec(meta.attr('content')); 50 51 if(parsedMetaContent) { 52 this.appId = parsedMetaContent[1]; 53 } else { 54 return; 55 } 56 } 57 58 this.title = this.options.title ? this.options.title : meta.data('title') || $('title').text().replace(/\s*[|\-·].*$/, '') 59 this.author = this.options.author ? this.options.author : meta.data('author') || ($('meta[name="author"]').length ? $('meta[name="author"]').attr('content') : window.location.hostname) 60 this.iconUrl = meta.data('icon-url'); 61 this.price = meta.data('price'); 62 63 // Create banner 64 this.create() 65 this.show() 66 this.listen() 67 } 68 69 SmartBanner.prototype = { 70 71 constructor: SmartBanner 72 73 , create: function() { 74 var iconURL 75 , link=(this.options.url ? this.options.url : (this.type == 'windows' ? 'ms-windows-store:navigate?appid=' : (this.type == 'android' ? 'market://details?id=' : (this.type == 'kindle' ? 'amzn://apps/android?asin=' : 'https://itunes.apple.com/' + this.options.appStoreLanguage + '/app/id'))) + this.appId) 76 , price = this.price || this.options.price 77 , inStore=price ? price + ' - ' + (this.type == 'android' ? this.options.inGooglePlay : this.type == 'kindle' ? this.options.inAmazonAppStore : this.type == 'ios' ? this.options.inAppStore : this.options.inWindowsStore) : '' 78 , gloss=this.options.iconGloss === null ? (this.type=='ios') : this.options.iconGloss 79
80 if (this.type == 'android' && this.options.GooglePlayParams) { 81 link = link + '&referrer=' + this.options.GooglePlayParams; 82 } 83 84 var banner = '<div id="smartbanner" class="'+this.type+'"><div class="sb-container"><a href="#" class="sb-close">×</a><span class="sb-icon"></span><div class="sb-info"><strong>'+this.title+'</strong><span>'+this.author+'</span><span>'+inStore+'</span></div><a href="'+link+'" class="sb-button"><span>'+this.options.button+'</span></a></div></div>'; 85 (this.options.layer) ? $(this.options.appendToSelector).append(banner) : $(this.options.appendToSelector).prepend(banner); 86 87 if (this.options.icon) { 88 iconURL = this.options.icon 89 } else if(this.iconUrl) { 90 iconURL = this.iconUrl; 91 } else if ($('link[rel="apple-touch-icon-precomposed"]').length > 0) { 92 iconURL = $('link[rel="apple-touch-icon-precomposed"]').attr('href') 93 if (this.options.iconGloss === null) gloss = false 94 } else if ($('link[rel="apple-touch-icon"]').length > 0) { 95 iconURL = $('link[rel="apple-touch-icon"]').attr('href') 96 } else if ($('meta[name="msApplication-TileImage"]').length > 0) { 97 iconURL = $('meta[name="msApplication-TileImage"]').attr('content') 98 } else if ($('meta[name="msapplication-TileImage"]').length > 0) { /* redundant because ms docs show two case usages */ 99 iconURL = $('meta[name="msapplication-TileImage"]').attr('content') 100 } 101 102 if (iconURL) { 103 $('#smartbanner .sb-icon').css('background-image','url('+iconURL+')') 104 if (gloss) $('#smartbanner .sb-icon').addClass('gloss') 105 } else{ 106 $('#smartbanner').addClass('no-icon') 107 } 108 109 this.bannerHeight = $('#smartbanner').outerHeight() + 2 110 111 if (this.scale > 1) { 112 $('#smartbanner') 113 .css('top', parseFloat($('#smartbanner').css('top')) * this.scale) 114 .css('height', parseFloat($('#smartbanner').css('height')) * this.scale) 115 .hide() 116 $('#smartbanner .sb-container') 117 .css('-webkit-transform', 'scale('+this.scale+')') 118 .css('-msie-transform', 'scale('+this.scale+')') 119 .css('-moz-transform', 'scale('+this.scale+')') 120 .css('width', $(window).width() / this.scale) 121 } 122 $('#smartbanner').css('position', (this.options.layer) ? 'absolute' : 'static') 123 } 124 125 , listen: function () { 126 $('#smartbanner .sb-close').on('click',$.proxy(this.close, this)) 127 $('#smartbanner .sb-button').on('click',$.proxy(this.install, this)) 128 } 129 130 , show: function(callback) { 131 var banner = $('#smartbanner'); 132 banner.stop(); 133 134 if (this.options.layer) { 135 banner.animate({top: 0, display: 'block'}, this.options.speedIn).addClass('shown').show(); 136 $(this.pushSelector).animate({paddingTop: this.origHtmlMargin + (this.bannerHeight * this.scale)}, this.options.speedIn, 'swing', callback); 137 } else { 138 if ($.support.transition) { 139 banner.animate({top:0},this.options.speedIn).addClass('shown'); 140 var transitionCallback = function() { 141 $('html').removeClass('sb-animation'); 142 if (callback) { 143 callback(); 144 } 145 }; 146 $(this.pushSelector).addClass('sb-animation').one($.support.transition.end, transitionCallback).emulateTransitionEnd(this.options.speedIn).css('margin-top', this.origHtmlMargin+(this.bannerHeight*this.scale)); 147 } else { 148 banner.slideDown(this.options.speedIn).addClass('shown'); 149 } 150 } 151 } 152 153 , hide: function(callback) { 154 var banner = $('#smartbanner'); 155 banner.stop(); 156 157 if (this.options.layer) { 158 banner.animate({top: -1 * this.bannerHeight * this.scale, display: 'block'}, this.options.speedIn).removeClass('shown'); 159 $(this.pushSelector).animate({paddingTop: this.origHtmlMargin}, this.options.speedIn, 'swing', callback); 160 } else {
161 if ($.support.transition) { 162 if ( this.type !== 'android' ) 163 banner.css('top', -1*this.bannerHeight*this.scale).removeClass('shown'); 164 else 165 banner.css({display:'none'}).removeClass('shown'); 166 var transitionCallback = function() { 167 $('html').removeClass('sb-animation'); 168 if (callback) { 169 callback(); 170 } 171 }; 172 $(this.pushSelector).addClass('sb-animation').one($.support.transition.end, transitionCallback).emulateTransitionEnd(this.options.speedOut).css('margin-top', this.origHtmlMargin); 173 } else { 174 banner.slideUp(this.options.speedOut).removeClass('shown'); 175 } 176 } 177 } 178 179 , close: function(e) { 180 e.preventDefault() 181 this.hide() 182 this.setCookie('sb-closed','true',this.options.daysHidden); 183 } 184 185 , install: function(e) { 186 if (this.options.hideOnInstall) { 187 this.hide() 188 } 189 this.setCookie('sb-installed','true',this.options.daysReminder) 190 } 191 192 , setCookie: function(name, value, exdays) { 193 var exdate = new Date() 194 exdate.setDate(exdate.getDate()+exdays) 195 value=encodeURI(value)+((exdays==null)?'':'; expires='+exdate.toUTCString()) 196 document.cookie=name+'='+value+'; path=/;' 197 } 198 199 , getCookie: function(name) { 200 var i,x,y,ARRcookies = document.cookie.split(";") 201 for(i=0;i<ARRcookies.length;i++) { 202 x = ARRcookies[i].substr(0,ARRcookies[i].indexOf("=")) 203 y = ARRcookies[i].substr(ARRcookies[i].indexOf("=")+1) 204 x = x.replace(/^\s+|\s+$/g,"") 205 if (x==name) { 206 return decodeURI(y) 207 } 208 } 209 return null 210 } 211 212 // Demo only 213 , switchType: function() { 214 var that = this 215 216 this.hide(function () { 217 that.type = that.type == 'android' ? 'ios' : 'android' 218 var meta = $(that.type == 'android' ? 'meta[name="google-play-app"]' : 'meta[name="apple-itunes-app"]').attr('content') 219 that.appId = /app-id=([^\s,]+)/.exec(meta)[1] 220 221 $('#smartbanner').detach() 222 that.create() 223 that.show() 224 }) 225 } 226 } 227 228 $.smartbanner = function (option) { 229 var $window = $(window) 230 , data = $window.data('smartbanner') 231 , options = typeof option == 'object' && option 232 if (!data) $window.data('smartbanner', (data = new SmartBanner(options))) 233 if (typeof option == 'string') data[option]() 234 } 235 236 // override these globally if you like (they are all optional) 237 $.smartbanner.defaults = { 238 title: null, // What the title of the app should be in the banner (defaults to <title>) 239 author: null, // What the author of the app should be in the banner (defaults to <meta name="author"> or hostname) 240 price: 'FREE', // Price of the app 241 appStoreLanguage: 'us', // Language code for App Store 242 inAppStore: 'On the App Store', // Text of price for iOS 243 inGooglePlay: 'In Google Play', // Text of price for Android 244 inAmazonAppStore: 'In the Amazon Appstore', 245 inWindowsStore: 'In the Windows Store', //Text of price for Windows 246 GooglePlayParams: null, // Aditional parameters for the market 247 icon: null, // The URL of the icon (defaults to <meta name="apple-touch-icon">) 248 iconGloss: null, // Force gloss effect for iOS even for precomposed 249 button: 'VIEW', // Text for the install button 250 url: null, // The URL for the button. Keep null if you want the button to link to the app store. 251 scale: 'auto', // Scale based on viewport size (set to 1 to disable) 252 speedIn: 300, // Show animation speed of the banner 253 speedOut: 400, // Close animation speed of the banner 254 daysHidden: 15, // Duration to hide the banner after being closed (0 = always show banner) 255 daysReminder: 90, // Duration to hide the banner after "VIEW" is clicked *separate from when the close button is clicked* (0 = always show banner) 256 force: null, // Choose 'ios', 'android' or 'windows'. Don't do a browser check, just always show this banner 257 hideOnInstall: true, // Hide the banner after "VIEW" is clicked. 258 layer: false, // Display as overlay layer or slide down the page 259 iOSUniversalApp: true, // If the iOS App is a universal app for both iPad and iPhone, display Smart Banner to iPad users, too. 260 appendToSelector: 'body', //Append the banner to a specific selector 261 pushSelector: 'html' // What element is going to push the site content down; this is where the banner append animation will start. 262 } 263 264 $.smartbanner.Constructor = SmartBanner; 265 266 267 // ============================================================ 268 // Bootstrap transition 269 // Copyright 2011-2014 Twitter, Inc. 270 // Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 271 272 function transitionEnd() { 273 var el = document.createElement('smartbanner') 274 275 var transEndEventNames = { 276 WebkitTransition: 'webkitTransitionEnd', 277 MozTransition: 'transitionend', 278 OTransition: 'oTransitionEnd otransitionend', 279 transition: 'transitionend' 280 } 281 282 for (var name in transEndEventNames) { 283 if (el.style[name] !== undefined) { 284 return {end: transEndEventNames[name]} 285 } 286 } 287 288 return false // explicit for ie8 ( ._.) 289 } 290
vendor: 680 bytes, lines 291-312
291 if ($.support.transition !== undefined) 292 return // Prevent conflict with Twitter Bootstrap 293 294 // http://blog.alexmaccaw.com/css-transitions 295 $.fn.emulateTransitionEnd = function(duration) { 296 var called = false, $el = this 297 $(this).one($.support.transition.end, function() { 298 called = true 299 }) 300 var callback = function() { 301 if (!called) $($el).trigger($.support.transition.end) 302 } 303 setTimeout(callback, duration) 304 return this 305 } 306 307 $(function() { 308 $.support.transition = transitionEnd() 309 }) 310 // ============================================================ 311 312}(window.jQuery);
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.