1/* Add to Homescreen v3.2.1 ~ (c) 2015 Matteo Spinelli ~ @license: http://cubiq.org/license */ 2(function (window, document) { 3/* 4 _ _ _____ _____ 5 ___ _| |_| |_ _|___| | |___ _____ ___ ___ ___ ___ ___ ___ ___ 6| .'| . | . | | | | . | | . | | -_|_ -| _| _| -_| -_| | 7|__,|___|___| |_| |___|__|__|___|_|_|_|___|___|___|_| |___|___|_|_| 8 by Matteo Spinelli ~ http://cubiq.org 9*/ 10 11// Check for addEventListener browser support (prevent errors in IE<9) 12var _eventListener = 'addEventListener' in window; 13 14// Check if document is loaded, needed by autostart 15var _DOMReady = false; 16if ( document.readyState === 'complete' ) { 17 _DOMReady = true; 18} else if ( _eventListener ) { 19 window.addEventListener('load', loaded, false); 20} 21 22function loaded () { 23 window.removeEventListener('load', loaded, false); 24 _DOMReady = true; 25} 26 27// regex used to detect if app has been added to the homescreen 28var _reSmartURL = /\/ath(\/)?$/; 29var _reQueryString = /([\?&]ath=[^&]*$|&ath=[^&]*(&))/; 30 31// singleton 32var _instance; 33function ath (options) { 34 _instance = _instance || new ath.Class(options); 35 36 return _instance; 37} 38 39// message in all supported languages 40ath.intl = { 41 de_de: { 42 ios: 'Um diese Web-App zum Home-Bildschirm hinzuzufügen, tippen Sie auf %icon und dann <strong>Zum Home-Bildschirm</strong>.', 43 android: 'Um diese Web-App zum Home-Bildschirm hinzuzufügen, öffnen Sie das Menü und tippen dann auf <strong>Zum Startbildschirm hinzufügen</strong>. <small>Wenn Ihr Gerät eine Menütaste hat, lässt sich das Browsermenü über diese öffnen. Ansonsten tippen Sie auf <span class="ath-action-icon">icon</span>.</small>', 44 }, 45 46 da_dk: { 47 ios: 'For at tilføje denne web app til hjemmeskærmen: Tryk %icon og derefter <strong>Føj til hjemmeskærm</strong>.', 48 android: 'For at tilføje denne web app til hjemmeskærmen, Ã¥bn browser egenskaber menuen og tryk pÃ¥ <strong>Føj til hjemmeskærm</strong>. <small>Denne menu kan tilgÃ¥s ved at trykke pÃ¥ menu knappen, hvis din enhed har en, eller ved at trykke pÃ¥ det øverste højre menu ikon <span class="ath-action-icon">icon</span>.</small>', 49 }, 50 51 en_us: { 52 ios: 'To add this web app to the home screen: tap %icon and then <strong>Add to Home Screen</strong>.', 53 android: 'To add this web app to the home screen open the browser option menu and tap on <strong>Add to homescreen</strong>. <small>The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon <span class="ath-action-icon">icon</span>.</small>', 54 }, 55 56 da_dk: { 57 ios: 'For at tilføje denne web app til din startskærm: tryk pÃ¥ %icon og vælg <strong>Føj til hjemmeskærm</strong>.', 58 android: 'For at tilføje denne web app til din startskærm, Ã¥ben browserens værktøjsmenuog tryk pÃ¥ <strong>Tilføj til hjemmeskærm</strong>. <small>Menuen kan aktiveres ved at trykke pÃ¥ den fysiske menuknap hvis din enhed har en, eller ved at trykke pÃ¥ menuikonet i øverste højre hjørne <span class="ath-action-icon">icon</span>.</small>', 59 }, 60 61 es_es: { 62 ios: 'Para añadir esta aplicación web a la pantalla de inicio: pulsa %icon y selecciona <strong>Añadir a pantalla de inicio</strong>.', 63 android: 'Para añadir esta aplicación web a la pantalla de inicio, abre las opciones y pulsa <strong>Añadir a pantalla inicio</strong>. <small>El menú se puede acceder pulsando el botón táctil en caso de tenerlo, o bien el icono de la parte superior derecha de la pantalla <span class="ath-action-icon">icon</span>.</small>', 64 }, 65 66 fi_fi: { 67 ios: 'Liitä tämä sovellus kotivalikkoon: klikkaa %icon ja tämän jälkeen <strong>Lisää kotivalikkoon</strong>.', 68 android: 'Lisätäksesi tämän sovelluksen aloitusnäytölle, avaa selaimen valikko ja klikkaa tähti -ikonia tai <strong>Lisää aloitusnäytölle tekstiä</strong>. <small>Valikkoon pääsee myös painamalla menuvalikkoa, jos laitteessasi on sellainen tai koskettamalla oikealla yläkulmassa menu ikonia <span class="ath-action-icon">icon</span>.</small>', 69 }, 70 71 fr_fr: { 72 ios: 'Pour ajouter cette application web sur l\'écran d\'accueil : Appuyez %icon et sélectionnez <strong>Ajouter sur l\'écran d\'accueil</strong>.', 73 android: 'Pour ajouter cette application web sur l\'écran d\'accueil : Appuyez sur le bouton "menu", puis sur <strong>Ajouter sur l\'écran d\'accueil</strong>. <small>Le menu peut-être accessible en appyant sur le bouton "menu" du téléphone s\'il en possède un <i class="fa fa-bars"></i>. Sinon, il se trouve probablement dans la coin supérieur droit du navigateur %icon.</small>', 74 }, 75 76 he_il: { 77 ios: '<span dir="rtl">×××ספת ××פ××קצ×× ×××¡× ×××ת: ×××××¥ ×¢× %icon ××× <strong>××סף ×××¡× ×××ת</strong>.</span>', 78 android: 'To add this web app to the home screen open the browser option menu and tap on <strong>Add to homescreen</strong>. <small>The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon <span class="ath-action-icon">icon</span>.</small>', 79 }, 80 81 it_it: { 82 ios: 'Per aggiungere questa web app alla schermata iniziale: premi %icon e poi <strong>Aggiungi a Home</strong>.', 83 android: 'Per aggiungere questa web app alla schermata iniziale, apri il menu opzioni del browser e premi su <strong>Aggiungi alla homescreen</strong>. <small>Puoi accedere al menu premendo il pulsante hardware delle opzioni se la tua device ne ha uno, oppure premendo l\'icona <span class="ath-action-icon">icon</span> in alto a destra.</small>', 84 }, 85 86 ja_jp: { 87 ios: 'ãã®ã¦ã§ãã¢ããªããã¼ã ç»é¢ã«è¿½å ããããã«%iconãæ¼ãã¦<strong>ãã¼ã ç»é¢ã«è¿½å </strong>ã', 88 android: 'To add this web app to the home screen open the browser option menu and tap on <strong>Add to homescreen</strong>. <small>The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon <span class="ath-action-icon">icon</span>.</small>', 89 }, 90 91 ko_kr: { 92 ios: 'í íë©´ì ë°ë¡ê°ê¸° ìì±: %icon ì í´ë¦í í <strong>í íë©´ì ì¶ê°</strong>.', 93 android: 'ë¸ë¼ì°ì ìµì ë©ë´ì <string>í íë©´ì ì¶ê°</string>를 í´ë¦íì¬ ííë©´ì ë°ë¡ê°ê¸°ë¥¼ ìì±í ì ììµëë¤. <small>ìµì ë©ë´ë ì¥ì¹ì ë©ë´ ë²í¼ì ë르거ë ì¤ë¥¸ìª½ ìë¨ì ë©ë´ ìì´ì½ <span class="ath-action-icon">icon</span>ì í´ë¦íì¬ ì ê·¼í ì ììµëë¤.</small>' 94 }, 95 96 nb_no: { 97 ios: 'For Ã¥ installere denne appen pÃ¥ hjem-skjermen: trykk pÃ¥ %icon og deretter <strong>Legg til pÃ¥ Hjem-skjerm</strong>.', 98 android: 'For Ã¥ legge til denne webappen pÃ¥ startsiden Ã¥pner en nettlesermenyen og velger <strong>Legg til pÃ¥ startsiden</strong>. <small>Menyen Ã¥pnes ved Ã¥ trykke pÃ¥ den fysiske menyknappen hvis enheten har det, eller ved Ã¥ trykke pÃ¥ menyikonet øverst til høyre <span class="ath-action-icon">icon</span>.</small>', 99 }, 100 101 pt_br: { 102 ios: 'Para adicionar este app à tela de inÃcio: clique %icon e então <strong>Tela de inÃcio</strong>.', 103 android: 'To add this web app to the home screen open the browser option menu and tap on <strong>Add to homescreen</strong>. <small>The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon <span class="ath-action-icon">icon</span>.</small>', 104 }, 105 106 pt_pt: { 107 ios: 'Para adicionar esta app ao ecrã principal: clique %icon e depois <strong>Ecrã principal</strong>.', 108 android: 'To add this web app to the home screen open the browser option menu and tap on <strong>Add to homescreen</strong>. <small>The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon <span class="ath-action-icon">icon</span>.</small>', 109 }, 110 111 nl_nl: { 112 ios: 'Om deze webapp op je telefoon te installeren, klik op %icon en dan <strong>Zet in beginscherm</strong>.', 113 android: 'To add this web app to the home screen open the browser option menu and tap on <strong>Add to homescreen</strong>. <small>The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon <span class="ath-action-icon">icon</span>.</small>', 114 }, 115 116 ru_ru: { 117 ios: 'ЧÑÐ¾Ð±Ñ Ð´Ð¾Ð±Ð°Ð²Ð¸ÑÑ ÑÑÐ¾Ñ ÑÐ°Ð¹Ñ Ð½Ð° Ñвой домаÑний ÑкÑан, нажмиÑе на Ð¸ÐºÐ¾Ð½ÐºÑ %icon и заÑем <strong>Ðа ÑкÑан "Ðомой"</strong>.', 118 android: 'ЧÑÐ¾Ð±Ñ Ð´Ð¾Ð±Ð°Ð²Ð¸ÑÑ ÑÐ°Ð¹Ñ Ð½Ð° Ñвой домаÑний ÑкÑан, оÑкÑойÑе Ð¼ÐµÐ½Ñ Ð±ÑаÑзеÑа и нажмиÑе на <strong>ÐобавиÑÑ Ð½Ð° главнÑй ÑкÑан</strong>. <small>ÐÐµÐ½Ñ Ð¼Ð¾Ð¶Ð½Ð¾ вÑзваÑÑ, нажав на ÐºÐ½Ð¾Ð¿ÐºÑ Ð¼ÐµÐ½Ñ Ð²Ð°Ñего ÑелеÑона, еÑли она еÑÑÑ. Ðли найдиÑе Ð¸ÐºÐ¾Ð½ÐºÑ ÑвеÑÑ Ñ ÑпÑава <span class="ath-action-icon">иконка</span>.</small>', 119 }, 120 121 sv_se: { 122 ios: 'För att lägga till denna webbapplikation pÃ¥ hemskärmen: tryck pÃ¥ %icon och därefter <strong>Lägg till pÃ¥ hemskärmen</strong>.', 123 android: 'För att lägga till den här webbappen pÃ¥ hemskärmen öppnar du webbläsarens alternativ-meny och väljer <strong>Lägg till pÃ¥ startskärmen</strong>. <small>
123Man hittar menyn genom att trycka pÃ¥ hÃ¥rdvaruknappen om din enhet har en sÃ¥dan, eller genom att trycka pÃ¥ menyikonen högst upp till höger <span class="ath-action-icon">icon</span>.</small>', 124 }, 125 126 zh_cn: { 127 ios: 'å¦è¦æåºç¨ç¨å¼å è³ä¸»å±å¹,请ç¹å»%icon, ç¶å<strong>å è³ä¸»å±å¹</strong>', 128 android: 'To add this web app to the home screen open the browser option menu and tap on <strong>Add to homescreen</strong>. <small>The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon <span class="ath-action-icon">icon</span>.</small>', 129 }, 130 131 zh_tw: { 132 ios: 'å¦è¦ææç¨ç¨å¼å è³ä¸»å±å¹, è«é»æ%icon, ç¶å¾<strong>å è³ä¸»å±å¹</strong>.', 133 android: 'To add this web app to the home screen open the browser option menu and tap on <strong>Add to homescreen</strong>. <small>The menu can be accessed by pressing the menu hardware button if your device has one, or by tapping the top right menu icon <span class="ath-action-icon">icon</span>.</small>', 134 }, 135 136}; 137 138// Add 2 characters language support (Android mostly) 139for ( var lang in ath.intl ) { 140 ath.intl[lang.substr(0, 2)] = ath.intl[lang]; 141} 142 143// default options 144ath.defaults = { 145 appID: 'org.cubiq.addtohome', // local storage name (no need to change) 146 fontSize: 15, // base font size, used to properly resize the popup based on viewport scale factor 147 debug: false, // override browser checks 148 logging: false, // log reasons for showing or not showing to js console; defaults to true when debug is true 149 modal: false, // prevent further actions until the message is closed 150 mandatory: false, // you can't proceed if you don't add the app to the homescreen 151 autostart: true, // show the message automatically 152 skipFirstVisit: false, // show only to returning visitors (ie: skip the first time you visit) 153 startDelay: 1, // display the message after that many seconds from page load 154 lifespan: 15, // life of the message in seconds 155 displayPace: 1440, // minutes before the message is shown again (0: display every time, default 24 hours) 156 maxDisplayCount: 0, // absolute maximum number of times the message will be shown to the user (0: no limit) 157 icon: true, // add touch icon to the message 158 message: '', // the message can be customized 159 validLocation: [], // list of pages where the message will be shown (array of regexes) 160 onInit: null, // executed on instance creation 161 onShow: null, // executed when the message is shown 162 onRemove: null, // executed when the message is removed 163 onAdd: null, // when the application is launched the first time from the homescreen (guesstimate) 164 onPrivate: null, // executed if user is in private mode 165 privateModeOverride: false, // show the message even in private mode (very rude) 166 detectHomescreen: false // try to detect if the site has been added to the homescreen (false | true | 'hash' | 'queryString' | 'smartURL') 167}; 168 169// browser info and capability 170var _ua = window.navigator.userAgent; 171 172var _nav = window.navigator; 173_extend(ath, { 174 hasToken: document.location.hash == '#ath' || _reSmartURL.test(document.location.href) || _reQueryString.test(document.location.search), 175 isRetina: window.devicePixelRatio && window.devicePixelRatio > 1, 176 isIDevice: (/iphone|ipod|ipad/i).test(_ua), 177 isMobileChrome: _ua.indexOf('Android') > -1 && (/Chrome\/[.0-9]*/).test(_ua) && _ua.indexOf("Version") == -1, 178 isMobileIE: _ua.indexOf('Windows Phone') > -1, 179 language: _nav.language && _nav.language.toLowerCase().replace('-', '_') || '' 180}); 181 182// falls back to en_us if language is unsupported 183ath.language = ath.language && ath.language in ath.intl ? ath.language : 'en_us'; 184 185ath.isMobileSafari = ath.isIDevice && _ua.indexOf('Safari') > -1 && _ua.indexOf('CriOS') < 0; 186ath.OS = ath.isIDevice ? 'ios' : ath.isMobileChrome ? 'android' : ath.isMobileIE ? 'windows' : 'unsupported'; 187 188ath.OSVersion = _ua.match(/(OS|Android) (\d+[_\.]\d+)/); 189ath.OSVersion = ath.OSVersion && ath.OSVersion[2] ? +ath.OSVersion[2].replace('_', '.') : 0; 190 191ath.isStandalone = 'standalone' in window.navigator && window.navigator.standalone; 192ath.isTablet = (ath.isMobileSafari && _ua.indexOf('iPad') > -1) || (ath.isMobileChrome && _ua.indexOf('Mobile') < 0); 193 194ath.isCompatible = (ath.isMobileSafari && ath.OSVersion >= 6) || ath.isMobileChrome; // TODO: add winphone 195 196var _defaultSession = { 197 lastDisplayTime: 0, // last time we displayed the message 198 returningVisitor: false, // is this the first time you visit 199 displayCount: 0, // number of times the message has been shown 200 optedout: false, // has the user opted out 201 added: false // has been actually added to the homescreen 202}; 203 204ath.removeSession = function (appID) { 205 try { 206 localStorage.removeItem(appID || ath.defaults.appID); 207 } catch (e) { 208 // we are most likely in private mode 209 } 210}; 211 212ath.doLog = function (logStr) { 213 if ( this.options.logging ) { 214 console.log(logStr); 215 } 216}; 217 218ath.Class = function (options) { 219 // class methods 220 this.doLog = ath.doLog; 221 222 // merge default options with user config 223 this.options = _extend({}, ath.defaults); 224 _extend(this.options, options); 225 // override defaults that are dependent on each other 226 if ( options.debug && (typeof options.logging === "undefined") ) { 227 this.options.logging = true; 228 } 229 230 // IE<9 so exit (I hate you, really) 231 if ( !_eventListener ) { 232 return; 233 } 234
235 // normalize some options 236 this.options.mandatory = this.options.mandatory && ( 'standalone' in window.navigator || this.options.debug ); 237 this.options.modal = this.options.modal || this.options.mandatory; 238 if ( this.options.mandatory ) { 239 this.options.startDelay = -0.5; // make the popup hasty 240 } 241 this.options.detectHomescreen = this.options.detectHomescreen === true ? 'hash' : this.options.detectHomescreen; 242 243 // setup the debug environment 244 if ( this.options.debug ) { 245 ath.isCompatible = true; 246 ath.OS = typeof this.options.debug == 'string' ? this.options.debug : ath.OS == 'unsupported' ? 'android' : ath.OS; 247 ath.OSVersion = ath.OS == 'ios' ? '8' : '4'; 248 } 249 250 // the element the message will be appended to 251 this.container = document.documentElement; 252 253 // load session 254 this.session = this.getItem(this.options.appID); 255 this.session = this.session ? JSON.parse(this.session) : undefined; 256 257 // user most likely came from a direct link containing our token, we don't need it and we remove it 258 if ( ath.hasToken && ( !ath.isCompatible || !this.session ) ) { 259 ath.hasToken = false; 260 _removeToken(); 261 } 262 263 // the device is not supported 264 if ( !ath.isCompatible ) { 265 this.doLog("Add to homescreen: not displaying callout because device not supported"); 266 return; 267 } 268 269 this.session = this.session || _defaultSession; 270 271 // check if we can use the local storage 272 try { 273 localStorage.setItem(this.options.appID, JSON.stringify(this.session)); 274 ath.hasLocalStorage = true; 275 } catch (e) { 276 // we are most likely in private mode 277 ath.hasLocalStorage = false; 278 279 if ( this.options.onPrivate ) { 280 this.options.onPrivate.call(this); 281 } 282 } 283 284 // check if this is a valid location 285 var isValidLocation = !this.options.validLocation.length; 286 for ( var i = this.options.validLocation.length; i--; ) { 287 if ( this.options.validLocation[i].test(document.location.href) ) { 288 isValidLocation = true; 289 break; 290 } 291 } 292 293 // check compatibility with old versions of add to homescreen. Opt-out if an old session is found 294 if ( this.getItem('addToHome') ) { 295 this.optOut(); 296 } 297 298 // critical errors: 299 if ( this.session.optedout ) { 300 this.doLog("Add to homescreen: not displaying callout because user opted out"); 301 return; 302 } 303 if ( this.session.added ) { 304 this.doLog("Add to homescreen: not displaying callout because already added to the homescreen"); 305 return; 306 } 307 if ( !isValidLocation ) { 308 this.doLog("Add to homescreen: not displaying callout because not a valid location"); 309 return; 310 } 311 312 // check if the app is in stand alone mode 313 if ( ath.isStandalone ) { 314 // execute the onAdd event if we haven't already 315 if ( !this.session.added ) { 316 this.session.added = true; 317 this.updateSession(); 318 319 if ( this.options.onAdd && ath.hasLocalStorage ) { // double check on localstorage to avoid multiple calls to the custom event 320 this.options.onAdd.call(this); 321 } 322 } 323 324 this.doLog("Add to homescreen: not displaying callout because in standalone mode"); 325 return; 326 } 327 328 // (try to) check if the page has been added to the homescreen 329 if ( this.options.detectHomescreen ) { 330 // the URL has the token, we are likely coming from the homescreen 331 if ( ath.hasToken ) { 332 _removeToken(); // we don't actually need the token anymore, we remove it to prevent redistribution 333 334 // this is called the first time the user opens the app from the homescreen 335 if ( !this.session.added ) { 336 this.session.added = true; 337 this.updateSession(); 338 339 if ( this.options.onAdd && ath.hasLocalStorage ) { // double check on localstorage to avoid multiple calls to the custom event 340 this.options.onAdd.call(this); 341 } 342 } 343 344 this.doLog("Add to homescreen: not displaying callout because URL has token, so we are likely coming from homescreen"); 345 return; 346 } 347 348 // URL doesn't have the token, so add it 349 if ( this.options.detectHomescreen == 'hash' ) { 350 history.replaceState('', window.document.title, document.location.href + '#ath'); 351 } else if ( this.options.detectHomescreen == 'smartURL' ) { 352 history.replaceState('', window.document.title, document.location.href.replace(/(\/)?$/, '/ath$1')); 353 } else { 354 history.replaceState('', window.document.title, document.location.href + (document.location.search ? '&' : '?' ) + 'ath='); 355 } 356 } 357 358 // check if this is a returning visitor 359 if ( !this.session.returningVisitor ) { 360 this.session.returningVisitor = true; 361 this.updateSession(); 362 363 // we do not show the message if this is your first visit 364 if ( this.options.skipFirstVisit ) { 365 this.doLog("Add to homescreen: not displaying callout because skipping first visit"); 366 return; 367 } 368 } 369 370 // we do no show the message in private mode 371 if ( !this.options.privateModeOverride && !ath.hasLocalStorage ) { 372 this.doLog("Add to homescreen: not displaying callout because browser is in private mode"); 373 return; 374 } 375 376 // all checks passed, ready to display 377 this.ready = true; 378 379 if ( this.options.onInit ) { 380 this.options.onInit.call(this); 381 } 382 383 if ( this.options.autostart ) { 384 this.doLog("Add to homescreen: autostart displaying callout"); 385 this.show(); 386 } 387}; 388 389ath.Class.prototype = { 390 // event type to method conversion 391 events: { 392 load: '_delayedShow', 393 error: '_delayedShow', 394 orientationchange: 'resize',
395 resize: 'resize', 396 scroll: 'resize', 397 click: 'remove', 398 touchmove: '_preventDefault', 399 transitionend: '_removeElements', 400 webkitTransitionEnd: '_removeElements', 401 MSTransitionEnd: '_removeElements' 402 }, 403 404 handleEvent: function (e) { 405 var type = this.events[e.type]; 406 if ( type ) { 407 this[type](e); 408 } 409 }, 410 411 show: function (force) { 412 // in autostart mode wait for the document to be ready 413 if ( this.options.autostart && !_DOMReady ) { 414 setTimeout(this.show.bind(this), 50); 415 // we are not displaying callout because DOM not ready, but don't log that because 416 // it would log too frequently 417 return; 418 } 419 420 // message already on screen 421 if ( this.shown ) { 422 this.doLog("Add to homescreen: not displaying callout because already shown on screen"); 423 return; 424 } 425 426 var now = Date.now(); 427 var lastDisplayTime = this.session.lastDisplayTime; 428 429 if ( force !== true ) { 430 // this is needed if autostart is disabled and you programmatically call the show() method 431 if ( !this.ready ) { 432 this.doLog("Add to homescreen: not displaying callout because not ready"); 433 return; 434 } 435 436 // we obey the display pace (prevent the message to popup too often) 437 if ( now - lastDisplayTime < this.options.displayPace * 60000 ) { 438 this.doLog("Add to homescreen: not displaying callout because displayed recently"); 439 return; 440 } 441 442 // obey the maximum number of display count 443 if ( this.options.maxDisplayCount && this.session.displayCount >= this.options.maxDisplayCount ) { 444 this.doLog("Add to homescreen: not displaying callout because displayed too many times already"); 445 return; 446 } 447 } 448 449 this.shown = true; 450 451 // increment the display count 452 this.session.lastDisplayTime = now; 453 this.session.displayCount++; 454 this.updateSession(); 455 456 // try to get the highest resolution application icon 457 if ( !this.applicationIcon ) { 458 if ( ath.OS == 'ios' ) { 459 this.applicationIcon = document.querySelector('head link[rel^=apple-touch-icon][sizes="152x152"],head link[rel^=apple-touch-icon][sizes="144x144"],head link[rel^=apple-touch-icon][sizes="120x120"],head link[rel^=apple-touch-icon][sizes="114x114"],head link[rel^=apple-touch-icon]'); 460 } else { 461 this.applicationIcon = document.querySelector('head link[rel^="shortcut icon"][sizes="196x196"],head link[rel^=apple-touch-icon]'); 462 } 463 } 464 465 var message = ''; 466 467 if ( typeof this.options.message == 'object' && ath.language in this.options.message ) { // use custom language message 468 message = this.options.message[ath.language][ath.OS]; 469 } else if ( typeof this.options.message == 'object' && ath.OS in this.options.message ) { // use custom os message 470 message = this.options.message[ath.OS]; 471 } else if ( this.options.message in ath.intl ) { // you can force the locale 472 message = ath.intl[this.options.message][ath.OS]; 473 } else if ( this.options.message !== '' ) { // use a custom message 474 message = this.options.message; 475 } else if ( ath.OS in ath.intl[ath.language] ) { // otherwise we use our message 476 message = ath.intl[ath.language][ath.OS]; 477 } 478 479 // add the action icon 480 message = '<p>' + message.replace('%icon', '<span class="ath-action-icon">icon</span>') + '</p>'; 481 482 // create the message container 483 this.viewport = document.createElement('div'); 484 this.viewport.className = 'ath-viewport'; 485 if ( this.options.modal ) { 486 this.viewport.className += ' ath-modal'; 487 } 488 if ( this.options.mandatory ) { 489 this.viewport.className += ' ath-mandatory'; 490 } 491 this.viewport.style.position = 'absolute'; 492 493 // create the actual message element 494 this.element = document.createElement('div'); 495 this.element.className = 'ath-container ath-' + ath.OS + ' ath-' + ath.OS + (ath.OSVersion + '').substr(0,1) + ' ath-' + (ath.isTablet ? 'tablet' : 'phone'); 496 this.element.style.cssText = '-webkit-transition-property:-webkit-transform,opacity;-webkit-transition-duration:0s;-webkit-transition-timing-function:ease-out;transition-property:transform,opacity;transition-duration:0s;transition-timing-function:ease-out;'; 497 this.element.style.webkitTransform = 'translate3d(0,-' + window.innerHeight + 'px,0)'; 498 this.element.style.transform = 'translate3d(0,-' + window.innerHeight + 'px,0)'; 499 500 // add the application icon 501 if ( this.options.icon && this.applicationIcon ) { 502 this.element.className += ' ath-icon'; 503 this.img = document.createElement('img'); 504 this.img.className = 'ath-application-icon'; 505 this.img.addEventListener('load', this, false); 506 this.img.addEventListener('error', this, false); 507 508 this.img.src = this.applicationIcon.href; 509 this.element.appendChild(this.img); 510 } 511 512 this.element.innerHTML += message; 513 514 // we are not ready to show, place the message out of sight 515 this.viewport.style.left = '-99999em'; 516 517 // attach all elements to the DOM 518 this.viewport.appendChild(this.element); 519 this.container.appendChild(this.viewport); 520 521 // if we don't have to wait for an image to load, show the message right away 522 if ( this.img ) { 523 this.doLog("Add to homescreen: not displaying callout because waiting for img to load"); 524 } else { 525 this._delayedShow(); 526 } 527 }, 528 529 _delayedShow: function (e) { 530 setTimeout(this._show.bind(this), this.options.startDelay * 1000 + 500); 531 }, 532 533 _show: function () { 534 var that = this; 535 536 // update the viewport size and orientation 537 this.updateViewport(); 538 539 // reposition/resize the message on orientation change 540 window.addEventListener('resize', this, false); 541 window.addEventListener('scroll', this, false); 542 window.addEventListener('orientationchange', this, false); 543 544 if ( this.options.modal ) { 545 // lock any other interaction 546 document.addEventListener('touchmove', this, true); 547 } 548 549 // Enable closing after 1 second 550 if ( !this.options.mandatory ) { 551 setTimeout(function () { 552 that.element.addEventListener('click', that, true); 553 }, 1000); 554 } 555 556 // kick the animation 557 setTimeout(function () { 558 that.element.style.webkitTransitionDuration = '1.2s'; 559 that.element.style.transitionDuration = '1.2s';
560 that.element.style.webkitTransform = 'translate3d(0,0,0)'; 561 that.element.style.transform = 'translate3d(0,0,0)'; 562 }, 0); 563 564 // set the destroy timer 565 if ( this.options.lifespan ) { 566 this.removeTimer = setTimeout(this.remove.bind(this), this.options.lifespan * 1000); 567 } 568 569 // fire the custom onShow event 570 if ( this.options.onShow ) { 571 this.options.onShow.call(this); 572 } 573 }, 574 575 remove: function () { 576 clearTimeout(this.removeTimer); 577 578 // clear up the event listeners 579 if ( this.img ) { 580 this.img.removeEventListener('load', this, false); 581 this.img.removeEventListener('error', this, false); 582 } 583 584 window.removeEventListener('resize', this, false); 585 window.removeEventListener('scroll', this, false); 586 window.removeEventListener('orientationchange', this, false); 587 document.removeEventListener('touchmove', this, true); 588 this.element.removeEventListener('click', this, true); 589 590 // remove the message element on transition end 591 this.element.addEventListener('transitionend', this, false); 592 this.element.addEventListener('webkitTransitionEnd', this, false); 593 this.element.addEventListener('MSTransitionEnd', this, false); 594 595 // start the fade out animation 596 this.element.style.webkitTransitionDuration = '0.3s'; 597 this.element.style.opacity = '0'; 598 }, 599 600 _removeElements: function () { 601 this.element.removeEventListener('transitionend', this, false); 602 this.element.removeEventListener('webkitTransitionEnd', this, false); 603 this.element.removeEventListener('MSTransitionEnd', this, false); 604 605 // remove the message from the DOM 606 this.container.removeChild(this.viewport); 607 608 this.shown = false; 609 610 // fire the custom onRemove event 611 if ( this.options.onRemove ) { 612 this.options.onRemove.call(this); 613 } 614 }, 615 616 updateViewport: function () { 617 if ( !this.shown ) { 618 return; 619 } 620 621 this.viewport.style.width = window.innerWidth + 'px'; 622 this.viewport.style.height = window.innerHeight + 'px'; 623 this.viewport.style.left = window.scrollX + 'px'; 624 this.viewport.style.top = window.scrollY + 'px'; 625 626 var clientWidth = document.documentElement.clientWidth; 627 628 this.orientation = clientWidth > document.documentElement.clientHeight ? 'landscape' : 'portrait'; 629 630 var screenWidth = ath.OS == 'ios' ? this.orientation == 'portrait' ? screen.width : screen.height : screen.width; 631 this.scale = screen.width > clientWidth ? 1 : screenWidth / window.innerWidth; 632 633 this.element.style.fontSize = this.options.fontSize / this.scale + 'px'; 634 }, 635 636 resize: function () { 637 clearTimeout(this.resizeTimer); 638 this.resizeTimer = setTimeout(this.updateViewport.bind(this), 100); 639 }, 640 641 updateSession: function () { 642 if ( ath.hasLocalStorage === false ) { 643 return; 644 } 645 646 localStorage.setItem(this.options.appID, JSON.stringify(this.session)); 647 }, 648 649 clearSession: function () { 650 this.session = _defaultSession; 651 this.updateSession(); 652 }, 653 654 getItem: function(item) { 655 try { 656 return localStorage.getItem(item); 657 } catch(e) { 658 // Preventing exception for some browsers when fetching localStorage key 659 ath.hasLocalStorage = false; 660 } 661 }, 662 663 optOut: function () { 664 this.session.optedout = true; 665 this.updateSession(); 666 }, 667 668 optIn: function () { 669 this.session.optedout = false; 670 this.updateSession(); 671 }, 672 673 clearDisplayCount: function () { 674 this.session.displayCount = 0; 675 this.updateSession(); 676 }, 677 678 _preventDefault: function (e) { 679 e.preventDefault(); 680 e.stopPropagation(); 681 } 682}; 683 684// utility 685function _extend (target, obj) { 686 for ( var i in obj ) { 687 target[i] = obj[i]; 688 } 689 690 return target; 691} 692 693function _removeToken () { 694 if ( document.location.hash == '#ath' ) { 695 history.replaceState('', window.document.title, document.location.href.split('#')[0]); 696 } 697 698 if ( _reSmartURL.test(document.location.href) ) { 699 history.replaceState('', window.document.title, document.location.href.replace(_reSmartURL, '$1')); 700 } 701 702 if ( _reQueryString.test(document.location.search) ) { 703 history.replaceState('', window.document.title, document.location.href.replace(_reQueryString, '$2')); 704 } 705} 706 707// expose to the world 708window.addToHomescreen = ath; 709 710})(window, document);
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.