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