├── .gitignore ├── package.json ├── LICENSE ├── windowstest.html ├── README.md ├── jquery.smartbanner.css └── jquery.smartbanner.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "smartbanner", 3 | "version": "1.0.0", 4 | "main": "./jquery.smartbanner.js", 5 | "dependencies": { 6 | "jquery": "^2.1.3" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+ssh://git@github.com/tes/jquery.smartbanner.git" 11 | }, 12 | "engines": { 13 | "node": ">=0.8.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Arnold Daniels 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /windowstest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hulu Plus 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | ... 17 | 18 | 19 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jQuery Smart Banner 2 | =================== 3 | 4 | [Smart Banners][1] are a new feature in iOS 6 to promote apps on the App Store from a website. This jQuery plugin 5 | brings this feature to older iOS versions, Android devices and for Windows Store apps. 6 | 7 | ## Usage ## 8 | 9 | 10 | YouTube 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ... 21 | 22 | 23 | 26 | 27 | 28 | 29 | ## Options ## 30 | $.smartbanner({ 31 | title: null, // What the title of the app should be in the banner (defaults to ) 32 | author: null, // What the author of the app should be in the banner (defaults to <meta name="author"> or hostname) 33 | price: 'FREE', // Price of the app 34 | appStoreLanguage: 'us', // Language code for App Store 35 | inAppStore: 'On the App Store', // Text of price for iOS 36 | inGooglePlay: 'In Google Play', // Text of price for Android 37 | inAmazonAppStore: 'In the Amazon Appstore', 38 | inWindowsStore: 'In the Windows Store', // Text of price for Windows 39 | GooglePlayParams: null, // Aditional parameters for the market 40 | icon: null, // The URL of the icon (defaults to <meta name="apple-touch-icon">) 41 | iconGloss: null, // Force gloss effect for iOS even for precomposed 42 | url: null, // The URL for the button. Keep null if you want the button to link to the app store. 43 | button: 'VIEW', // Text for the install button 44 | scale: 'auto', // Scale based on viewport size (set to 1 to disable) 45 | speedIn: 300, // Show animation speed of the banner 46 | speedOut: 400, // Close animation speed of the banner 47 | daysHidden: 15, // Duration to hide the banner after being closed (0 = always show banner) 48 | daysReminder: 90, // Duration to hide the banner after "VIEW" is clicked *separate from when the close button is clicked* (0 = always show banner) 49 | force: null, // Choose 'ios', 'android' or 'windows'. Don't do a browser check, just always show this banner 50 | hideOnInstall: true, // Hide the banner after "VIEW" is clicked. 51 | layer: false, // Display as overlay layer or slide down the page 52 | iOSUniversalApp: true, // If the iOS App is a universal app for both iPad and iPhone, display Smart Banner to iPad users, too. 53 | appendToSelector: 'body', //Append the banner to a specific selector 54 | onInstall: function() { 55 | // alert('Click install'); 56 | }, 57 | onClose: function() { 58 | // alert('Click close'); 59 | } 60 | }) 61 | 62 | ## Contributors 63 | 64 | [![Arnold Daniels](https://avatars3.githubusercontent.com/u/100821?v=2&s=64)](https://github.com/jasny) 65 | [![Thomas De Laet](https://avatars1.githubusercontent.com/u/5644283?v=2&s=64)](https://github.com/delaetthomas) 66 | 67 | [1]: http://developer.apple.com/library/ios/#documentation/AppleApplications/Reference/SafariWebContent/PromotingAppswithAppBanners/PromotingAppswithAppBanners.html 68 | -------------------------------------------------------------------------------- /jquery.smartbanner.css: -------------------------------------------------------------------------------- 1 | #smartbanner { position:absolute; left:0; top:-82px; border-bottom:1px solid #e8e8e8; width:100%; height:78px; font-family:'Helvetica Neue',sans-serif; background:-webkit-linear-gradient(top, #f4f4f4 0%,#cdcdcd 100%); background-image: -ms-linear-gradient(top, #F4F4F4 0%, #CDCDCD 100%); background-image: -moz-linear-gradient(top, #F4F4F4 0%, #CDCDCD 100%); box-shadow:0 1px 2px rgba(0,0,0,0.5); z-index:9998; -webkit-font-smoothing:antialiased; overflow:hidden; -webkit-text-size-adjust:none; } 2 | #smartbanner, html.sb-animation {-webkit-transition: all .3s ease;} 3 | #smartbanner .sb-container { margin: 0 auto; } 4 | #smartbanner .sb-close { position:absolute; left:5px; top:5px; display:block; border:2px solid #fff; width:14px; height:14px; font-family:'ArialRoundedMTBold',Arial; font-size:15px; line-height:15px; text-align:center; color:#fff; background:#070707; text-decoration:none; text-shadow:none; border-radius:14px; box-shadow:0 2px 3px rgba(0,0,0,0.4); -webkit-font-smoothing:subpixel-antialiased; } 5 | #smartbanner .sb-close:active { font-size:13px; color:#aaa; } 6 | #smartbanner .sb-icon { position:absolute; left:30px; top:10px; display:block; width:57px; height:57px; background:rgba(0,0,0,0.6); background-size:cover; border-radius:10px; box-shadow:0 1px 3px rgba(0,0,0,0.3); } 7 | #smartbanner.no-icon .sb-icon { display:none; } 8 | #smartbanner .sb-info { position:absolute; left:98px; top:18px; width:44%; font-size:11px; line-height:1.2em; font-weight:bold; color:#6a6a6a; text-shadow:0 1px 0 rgba(255,255,255,0.8); } 9 | #smartbanner #smartbanner.no-icon .sb-info { left:34px; } 10 | #smartbanner .sb-info strong { display:block; font-size:13px; color:#4d4d4d; line-height: 18px; } 11 | #smartbanner .sb-info > span { display:block; } 12 | #smartbanner .sb-info em { font-style:normal; text-transform:uppercase; } 13 | #smartbanner .sb-button { position:absolute; right:20px; top:24px; border:1px solid #bfbfbf; padding: 0 10px; min-width: 10%; height:24px; font-size:14px; line-height:24px; text-align:center; font-weight:bold; color:#6a6a6a; background:-webkit-linear-gradient(top, #efefef 0%,#dcdcdc 100%); text-transform:uppercase; text-decoration:none; text-shadow:0 1px 0 rgba(255,255,255,0.8); border-radius:3px; box-shadow:0 1px 0 rgba(255,255,255,0.6),0 1px 0 rgba(255,255,255,0.7) inset; } 14 | #smartbanner .sb-button:active, #smartbanner .sb-button:hover { background:-webkit-linear-gradient(top, #dcdcdc 0%,#efefef 100%); } 15 | 16 | #smartbanner .sb-icon.gloss:after { content:''; position:absolute; left:0; top:-1px; border-top:1px solid rgba(255,255,255,0.8); width:100%; height:50%; background:-webkit-linear-gradient(top, rgba(255,255,255,0.7) 0%,rgba(255,255,255,0.2) 100%); border-radius:10px 10px 12px 12px; } 17 | 18 | #smartbanner.android { border-color:#212228; background: #3d3d3d url('data:image/gif;base64,R0lGODlhCAAIAIABAFVVVf///yH5BAEHAAEALAAAAAAIAAgAAAINRG4XudroGJBRsYcxKAA7'); border-top: 5px solid #88B131; box-shadow: none; } 19 | #smartbanner.android .sb-close { border:0; width:17px; height:17px; line-height:17px; color:#b1b1b3; background:#1c1e21; text-shadow:0 1px 1px #000; box-shadow:0 1px 2px rgba(0,0,0,0.8) inset,0 1px 1px rgba(255,255,255,0.3); } 20 | #smartbanner.android .sb-close:active { color:#eee; } 21 | #smartbanner.android .sb-info { color:#ccc; text-shadow:0 1px 2px #000; } 22 | #smartbanner.android .sb-info strong { color:#fff; } 23 | #smartbanner.android .sb-button { min-width: 12%; border:1px solid #DDDCDC; padding:1px; color:#d1d1d1; background: none; border-radius: 0; box-shadow: none; min-height:28px} 24 | #smartbanner.android .sb-button span { text-align: center; display: block; padding: 0 10px; background-color: #42B6C9; background-image: -webkit-gradient(linear,0 0,0 100%,from(#42B6C9),to(#39A9BB)); background-image: -moz-linear-gradient(top,#42B6C9,#39A9BB); text-transform:none; text-shadow:none; box-shadow:none; } 25 | #smartbanner.android .sb-button:active, #smartbanner.android .sb-button:hover { background: none; } 26 | #smartbanner.android .sb-button:active span, #smartbanner.android .sb-button:hover span { background:#2AC7E1; } 27 | 28 | #smartbanner.windows .sb-icon { border-radius: 0px; } 29 | -------------------------------------------------------------------------------- /jquery.smartbanner.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery Smart Banner 3 | * Copyright (c) 2012 Arnold Daniels <arnold@jasny.net> 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 | // Set default onInstall callback if not set in options 64 | if (typeof this.options.onInstall === 'function') { 65 | this.options.onInstall = this.options.onInstall; 66 | } else { 67 | this.options.onInstall = function() {}; 68 | } 69 | 70 | // Set default onClose callback if not set in options 71 | if (typeof this.options.onClose === 'function') { 72 | this.options.onClose = this.options.onClose; 73 | } else { 74 | this.options.onClose = function() {}; 75 | } 76 | 77 | // Create banner 78 | this.create() 79 | this.show() 80 | this.listen() 81 | } 82 | 83 | SmartBanner.prototype = { 84 | 85 | constructor: SmartBanner 86 | 87 | , create: function() { 88 | var iconURL 89 | , 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) 90 | , price = this.price || this.options.price 91 | , inStore=price ? price + ' - ' + (this.type == 'android' ? this.options.inGooglePlay : this.type == 'kindle' ? this.options.inAmazonAppStore : this.type == 'ios' ? this.options.inAppStore : this.options.inWindowsStore) : '' 92 | , gloss=this.options.iconGloss === null ? (this.type=='ios') : this.options.iconGloss 93 | 94 | if (this.type == 'android' && this.options.GooglePlayParams) { 95 | link = link + '&referrer=' + this.options.GooglePlayParams; 96 | } 97 | 98 | 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>'; 99 | (this.options.layer) ? $(this.options.appendToSelector).append(banner) : $(this.options.appendToSelector).prepend(banner); 100 | 101 | if (this.options.icon) { 102 | iconURL = this.options.icon 103 | } else if(this.iconUrl) { 104 | iconURL = this.iconUrl; 105 | } else if ($('link[rel="apple-touch-icon-precomposed"]').length > 0) { 106 | iconURL = $('link[rel="apple-touch-icon-precomposed"]').attr('href') 107 | if (this.options.iconGloss === null) gloss = false 108 | } else if ($('link[rel="apple-touch-icon"]').length > 0) { 109 | iconURL = $('link[rel="apple-touch-icon"]').attr('href') 110 | } else if ($('meta[name="msApplication-TileImage"]').length > 0) { 111 | iconURL = $('meta[name="msApplication-TileImage"]').attr('content') 112 | } else if ($('meta[name="msapplication-TileImage"]').length > 0) { /* redundant because ms docs show two case usages */ 113 | iconURL = $('meta[name="msapplication-TileImage"]').attr('content') 114 | } 115 | 116 | if (iconURL) { 117 | $('#smartbanner .sb-icon').css('background-image','url('+iconURL+')') 118 | if (gloss) $('#smartbanner .sb-icon').addClass('gloss') 119 | } else{ 120 | $('#smartbanner').addClass('no-icon') 121 | } 122 | 123 | this.bannerHeight = $('#smartbanner').outerHeight() + 2 124 | 125 | if (this.scale > 1) { 126 | $('#smartbanner') 127 | .css('top', parseFloat($('#smartbanner').css('top')) * this.scale) 128 | .css('height', parseFloat($('#smartbanner').css('height')) * this.scale) 129 | .hide() 130 | $('#smartbanner .sb-container') 131 | .css('-webkit-transform', 'scale('+this.scale+')') 132 | .css('-msie-transform', 'scale('+this.scale+')') 133 | .css('-moz-transform', 'scale('+this.scale+')') 134 | .css('width', $(window).width() / this.scale) 135 | } 136 | $('#smartbanner').css('position', (this.options.layer) ? 'absolute' : 'static') 137 | } 138 | 139 | , listen: function () { 140 | $('#smartbanner .sb-close').on('click',$.proxy(this.close, this)) 141 | $('#smartbanner .sb-button').on('click',$.proxy(this.install, this)) 142 | } 143 | 144 | , show: function(callback) { 145 | var banner = $('#smartbanner'); 146 | banner.stop(); 147 | 148 | if (this.options.layer) { 149 | banner.animate({top: 0, display: 'block'}, this.options.speedIn).addClass('shown').show(); 150 | $(this.pushSelector).animate({paddingTop: this.origHtmlMargin + (this.bannerHeight * this.scale)}, this.options.speedIn, 'swing', callback); 151 | } else { 152 | if ($.support.transition) { 153 | banner.animate({top:0},this.options.speedIn).addClass('shown'); 154 | var transitionCallback = function() { 155 | $('html').removeClass('sb-animation'); 156 | if (callback) { 157 | callback(); 158 | } 159 | }; 160 | $(this.pushSelector).addClass('sb-animation').one($.support.transition.end, transitionCallback).emulateTransitionEnd(this.options.speedIn).css('margin-top', this.origHtmlMargin+(this.bannerHeight*this.scale)); 161 | } else { 162 | banner.slideDown(this.options.speedIn).addClass('shown'); 163 | } 164 | } 165 | } 166 | 167 | , hide: function(callback) { 168 | var banner = $('#smartbanner'); 169 | banner.stop(); 170 | 171 | if (this.options.layer) { 172 | banner.animate({top: -1 * this.bannerHeight * this.scale, display: 'block'}, this.options.speedIn).removeClass('shown'); 173 | $(this.pushSelector).animate({paddingTop: this.origHtmlMargin}, this.options.speedIn, 'swing', callback); 174 | } else { 175 | if ($.support.transition) { 176 | if ( this.type !== 'android' ) 177 | banner.css('top', -1*this.bannerHeight*this.scale).removeClass('shown'); 178 | else 179 | banner.css({display:'none'}).removeClass('shown'); 180 | var transitionCallback = function() { 181 | $('html').removeClass('sb-animation'); 182 | if (callback) { 183 | callback(); 184 | } 185 | }; 186 | $(this.pushSelector).addClass('sb-animation').one($.support.transition.end, transitionCallback).emulateTransitionEnd(this.options.speedOut).css('margin-top', this.origHtmlMargin); 187 | } else { 188 | banner.slideUp(this.options.speedOut).removeClass('shown'); 189 | } 190 | } 191 | } 192 | 193 | , close: function(e) { 194 | e.preventDefault() 195 | this.hide() 196 | this.setCookie('sb-closed','true',this.options.daysHidden); 197 | this.options.onClose(e); 198 | } 199 | 200 | , install: function(e) { 201 | if (this.options.hideOnInstall) { 202 | this.hide() 203 | } 204 | this.setCookie('sb-installed','true',this.options.daysReminder) 205 | this.options.onInstall(e); 206 | } 207 | 208 | , setCookie: function(name, value, exdays) { 209 | var exdate = new Date() 210 | exdate.setDate(exdate.getDate()+exdays) 211 | value=encodeURI(value)+((exdays==null)?'':'; expires='+exdate.toUTCString()) 212 | document.cookie=name+'='+value+'; path=/;' 213 | } 214 | 215 | , getCookie: function(name) { 216 | var i,x,y,ARRcookies = document.cookie.split(";") 217 | for(i=0;i<ARRcookies.length;i++) { 218 | x = ARRcookies[i].substr(0,ARRcookies[i].indexOf("=")) 219 | y = ARRcookies[i].substr(ARRcookies[i].indexOf("=")+1) 220 | x = x.replace(/^\s+|\s+$/g,"") 221 | if (x==name) { 222 | return decodeURI(y) 223 | } 224 | } 225 | return null 226 | } 227 | 228 | // Demo only 229 | , switchType: function() { 230 | var that = this 231 | 232 | this.hide(function () { 233 | that.type = that.type == 'android' ? 'ios' : 'android' 234 | var meta = $(that.type == 'android' ? 'meta[name="google-play-app"]' : 'meta[name="apple-itunes-app"]').attr('content') 235 | that.appId = /app-id=([^\s,]+)/.exec(meta)[1] 236 | 237 | $('#smartbanner').detach() 238 | that.create() 239 | that.show() 240 | }) 241 | } 242 | } 243 | 244 | $.smartbanner = function (option) { 245 | var $window = $(window) 246 | , data = $window.data('smartbanner') 247 | , options = typeof option == 'object' && option 248 | if (!data) $window.data('smartbanner', (data = new SmartBanner(options))) 249 | if (typeof option == 'string') data[option]() 250 | } 251 | 252 | // override these globally if you like (they are all optional) 253 | $.smartbanner.defaults = { 254 | title: null, // What the title of the app should be in the banner (defaults to <title>) 255 | author: null, // What the author of the app should be in the banner (defaults to <meta name="author"> or hostname) 256 | price: 'FREE', // Price of the app 257 | appStoreLanguage: 'us', // Language code for App Store 258 | inAppStore: 'On the App Store', // Text of price for iOS 259 | inGooglePlay: 'In Google Play', // Text of price for Android 260 | inAmazonAppStore: 'In the Amazon Appstore', 261 | inWindowsStore: 'In the Windows Store', //Text of price for Windows 262 | GooglePlayParams: null, // Aditional parameters for the market 263 | icon: null, // The URL of the icon (defaults to <meta name="apple-touch-icon">) 264 | iconGloss: null, // Force gloss effect for iOS even for precomposed 265 | button: 'VIEW', // Text for the install button 266 | url: null, // The URL for the button. Keep null if you want the button to link to the app store. 267 | scale: 'auto', // Scale based on viewport size (set to 1 to disable) 268 | speedIn: 300, // Show animation speed of the banner 269 | speedOut: 400, // Close animation speed of the banner 270 | daysHidden: 15, // Duration to hide the banner after being closed (0 = always show banner) 271 | daysReminder: 90, // Duration to hide the banner after "VIEW" is clicked *separate from when the close button is clicked* (0 = always show banner) 272 | force: null, // Choose 'ios', 'android' or 'windows'. Don't do a browser check, just always show this banner 273 | hideOnInstall: true, // Hide the banner after "VIEW" is clicked. 274 | layer: false, // Display as overlay layer or slide down the page 275 | iOSUniversalApp: true, // If the iOS App is a universal app for both iPad and iPhone, display Smart Banner to iPad users, too. 276 | appendToSelector: 'body', //Append the banner to a specific selector 277 | pushSelector: 'html' // What element is going to push the site content down; this is where the banner append animation will start. 278 | } 279 | 280 | $.smartbanner.Constructor = SmartBanner; 281 | 282 | 283 | // ============================================================ 284 | // Bootstrap transition 285 | // Copyright 2011-2014 Twitter, Inc. 286 | // Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 287 | 288 | function transitionEnd() { 289 | var el = document.createElement('smartbanner') 290 | 291 | var transEndEventNames = { 292 | WebkitTransition: 'webkitTransitionEnd', 293 | MozTransition: 'transitionend', 294 | OTransition: 'oTransitionEnd otransitionend', 295 | transition: 'transitionend' 296 | } 297 | 298 | for (var name in transEndEventNames) { 299 | if (el.style[name] !== undefined) { 300 | return {end: transEndEventNames[name]} 301 | } 302 | } 303 | 304 | return false // explicit for ie8 ( ._.) 305 | } 306 | 307 | if ($.support.transition !== undefined) 308 | return // Prevent conflict with Twitter Bootstrap 309 | 310 | // http://blog.alexmaccaw.com/css-transitions 311 | $.fn.emulateTransitionEnd = function(duration) { 312 | var called = false, $el = this 313 | $(this).one($.support.transition.end, function() { 314 | called = true 315 | }) 316 | var callback = function() { 317 | if (!called) $($el).trigger($.support.transition.end) 318 | } 319 | setTimeout(callback, duration) 320 | return this 321 | } 322 | 323 | $(function() { 324 | $.support.transition = transitionEnd() 325 | }) 326 | // ============================================================ 327 | 328 | }(window.jQuery); 329 | --------------------------------------------------------------------------------