1/* Add to Homescreen v3.2.3 ~ (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 cs_cs: { 42 ios: 'Pro pÅidáni této webové aplikace na úvodnà obrazovku: stlaÄte %icon a pak <strong>PÅidat na úvodnà obrazovku</strong>.', 43 android: 'Pro pÅidáni této webové aplikace na úvodnà obrazovku otevÅete menu nastavenà prohlÞeÄe a stlaÄte <strong>PÅidat na úvodnà obrazovku</strong>. <small>K menu se dostanete stlaÄenÃm hardwaroveho tlaÄÃtka, když ho vaÅ¡e zaÅÃzenà má, nebo stlaÄenÃm pravé hornà menu ikony <span class="ath-action-icon">icon</span>.</small>' 44 }, 45 46 de_de: { 47 ios: 'Um diese Web-App zum Home-Bildschirm hinzuzufügen, tippen Sie auf %icon und dann <strong>Zum Home-Bildschirm</strong>.', 48 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 %icon.</small>' 49 }, 50 51 da_dk: { 52 ios: 'For at tilføje denne web app til hjemmeskærmen: Tryk %icon og derefter <strong>Føj til hjemmeskærm</strong>.', 53 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 %icon.</small>' 54 }, 55 56 el_gr: { 57 ios: 'Îια να ÏÏοÏθÎÏεÏε Ïην εÏαÏμογή ÏÏην αÏÏική οθÏνη: ÏαÏήÏÏε Ïο %icon και μεÏά <strong>Î ÏÏÏθεÏε ÏÏην αÏÏική οθÏνη</strong>.', 58 android: 'Îια να ÏÏοÏθÎÏεÏε Ïην εÏαÏμογή ÏÏην αÏÏική οθÏνη, ανοίξÏε ÏÎ¹Ï ÎµÏιλογÎÏ ÏÎ¿Ï browser ÏÎ±Ï ÎºÎ±Î¹ ÏαÏήÏÏε Ïο <strong>Î ÏοÏθήκη ÏÏην αÏÏική οθÏνη</strong>. <small>ÎÏοÏείÏε να ÎÏεÏε ÏÏÏÏβαÏη ÏÏο μενοÏ, ÏαÏÏνÏÎ±Ï Ïο ÎºÎ¿Ï Î¼Ïί ÏÎ¿Ï Î¼ÎµÎ½Î¿Ï ÏÎ¿Ï ÎºÎ¹Î½Î·ÏÎ¿Ï ÏÎ±Ï Î® Ïο ÏÎ¬Î½Ï Î´ÎµÎ¾Î¹Î¬ ÎºÎ¿Ï Î¼Ïί ÏÎ¿Ï Î¼ÎµÎ½Î¿Ï %icon.</small>' 59 }, 60 61 en_us: { 62 ios: 'To add this web app to the home screen: tap %icon and then <strong>Add to Home Screen</strong>.', 63 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 %icon.</small>' 64 }, 65 66 es_es: { 67 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>.', 68 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 %icon.</small>' 69 }, 70 71 fi_fi: { 72 ios: 'Liitä tämä sovellus kotivalikkoon: klikkaa %icon ja tämän jälkeen <strong>Lisää kotivalikkoon</strong>.', 73 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 %icon.</small>' 74 }, 75 76 fr_fr: { 77 ios: 'Pour ajouter cette application web sur l\'écran d\'accueil : Appuyez %icon et sélectionnez <strong>Ajouter sur l\'écran d\'accueil</strong>.', 78 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 appuyant 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>' 79 }, 80 81 he_il: { 82 ios: '<span dir="rtl">×××ספת ××פ××קצ×× ×××¡× ×××ת: ×××××¥ ×¢× %icon ××× <strong>××סף ×××¡× ×××ת</strong>.</span>', 83 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 %icon.</small>' 84 }, 85 86 hu_hu: { 87 ios: 'Ha hozzá szeretné adni ezt az alkalmazást a kezdÅképernyÅjéhez, érintse meg a következÅ ikont: %icon , majd a <strong>Hozzáadás a kezdÅképernyÅhöz</strong> menüpontot.', 88 android: 'Ha hozzá szeretné adni ezt az alkalmazást a kezdÅképernyÅjéhez, a böngészÅ menüjében kattintson a <strong>Hozzáadás a kezdÅképernyÅhöz</strong> menüpontra. <small>A böngészÅ menüjét a következÅ ikon megérintésével tudja megnyitni: %icon.</small>' 89 }, 90 91 it_it: { 92 ios: 'Per aggiungere questa web app alla schermata iniziale: premi %icon e poi <strong>Aggiungi a Home</strong>.', 93 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 %icon in alto a destra.</small>' 94 }, 95 96 ja_jp: { 97 ios: 'ãã®ã¦ã§ãã¢ããªããã¼ã ç»é¢ã«è¿½å ããã«ã¯ã%iconãã¿ãããã¦<strong>ãã¼ã ç»é¢ã«è¿½å </strong>ãã¦ãã ããã', 98 android: 'ãã®ã¦ã§ãã¢ããªããã¼ã ç»é¢ã«è¿½å ããã«ã¯ããã©ã¦ã¶ã®ãªãã·ã§ã³ã¡ãã¥ã¼ãã<strong>ãã¼ã ç»é¢ã«è¿½å </strong>ãã¿ãããã¦ãã ããã<small>ãªãã·ã§ã³ã¡ãã¥ã¼ã¯ãä¸é¨ã®æ©ç¨®ã§ã¯ããã¤ã¹ã®ã¡ãã¥ã¼ãã¿ã³ããããã以å¤ã§ã¯ç»é¢å³ä¸ã®%iconããã¢ã¯ã»ã¹ã§ãã¾ãã</small>' 99 }, 100 101 ko_kr: { 102 ios: 'í íë©´ì ë°ë¡ê°ê¸° ìì±: %icon ì í´ë¦í í <strong>í íë©´ì ì¶ê°</strong>.', 103 android: 'ë¸ë¼ì°ì ìµì ë©ë´ì <string>í íë©´ì ì¶ê°</string>를 í´ë¦íì¬ ííë©´ì ë°ë¡ê°ê¸°ë¥¼ ìì±í ì ììµëë¤. <small>ìµì ë©ë´ë ì¥ì¹ì ë©ë´ ë²í¼ì ë르거ë ì¤ë¥¸ìª½ ìë¨ì ë©ë´ ìì´ì½ %iconì í´ë¦íì¬ ì ê·¼í ì ììµëë¤.</small>' 104 }, 105 106 nb_no: { 107 ios: 'For Ã¥ installere denne appen pÃ¥ hjem-skjermen: trykk pÃ¥ %icon og deretter <strong>Legg til pÃ¥ Hjem-skjerm</strong>.', 108 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 %icon.</small>' 109 }, 110 111 pt_br: { 112 ios: 'Para adicionar este app à tela de inÃcio: clique %icon e então <strong>Tela de inÃcio</strong>.', 113 android: 'Para adicionar este app à tela de inÃcio, abra o menu de opções do navegador e selecione <strong>Adicionar à tela inicial</strong>. <small>O menu pode ser acessado pressionando o "menu" button se o seu dispositivo tiver um, ou selecionando o Ãcone %icon no canto superior direito.</small>' 114 }, 115 116 pt_pt: { 117 ios: 'Para adicionar esta app ao ecrã principal: clique %icon e depois <strong>Ecrã principal</strong>.', 118 android: 'Para adicionar esta app web ecrã principal, abra o menu de opções do navegador e selecione <strong>Adicionar à tela inicial</strong>. <small>O menu pode ser acessado pressionando o "menu" button se o seu dispositivo tiver um, ou selecionando o Ãcone %icon no canto superior direito.</small>' 119 }, 120 121 nl_nl: { 122 ios: 'Om deze webapp aan je startscherm toe te voegen, klik op %icon en dan <strong>Zet in startscherm</strong>.', 123 android: 'Om deze webapp aan je startscherm toe te voegen, open de browserinstellingen en tik op <strong>Toevoegen aan startscherm</strong>. <small>Gebruik de "menu" knop als je telefoon die heeft, anders het menu-icoon rechtsbovenin %icon.</small>' 124 }, 125 126 ru_ru: { 127 ios: 'ЧÑÐ¾Ð±Ñ Ð´Ð¾Ð±Ð°Ð²Ð¸ÑÑ ÑÑÐ¾Ñ ÑÐ°Ð¹Ñ Ð½Ð° Ñвой домаÑний ÑкÑан, нажмиÑе на Ð¸ÐºÐ¾Ð½ÐºÑ %icon и заÑем <strong>Ðа ÑкÑан "Ðомой"</strong>.', 128 android: 'ЧÑÐ¾Ð±Ñ Ð´Ð¾Ð±Ð°Ð²Ð¸ÑÑ ÑÐ°Ð¹Ñ Ð½Ð° Ñвой домаÑний ÑкÑан, оÑкÑойÑе Ð¼ÐµÐ½Ñ Ð±ÑаÑзеÑа и нажмиÑе на <strong>ÐобавиÑÑ Ð½Ð° главнÑй ÑкÑан</strong>. <small>ÐÐµÐ½Ñ Ð¼Ð¾Ð¶Ð½Ð¾ вÑзваÑÑ, нажав на ÐºÐ½Ð¾Ð¿ÐºÑ Ð¼ÐµÐ½Ñ Ð²Ð°Ñего ÑелеÑона, еÑли она еÑÑÑ. Ðли найдиÑе Ð¸ÐºÐ¾Ð½ÐºÑ ÑвеÑÑ Ñ ÑпÑава %icon[иконка].</small>' 129 }, 130 131 sk_sk: { 132 ios: 'Pre pridanie tejto webovej aplikácie na úvodnú obrazovku: stlaÄte %icon a potom <strong>PridaÅ¥ na úvodnú obrazovku</strong>.', 133 android: 'Pre pridanie tejto webovej aplikácie na úvodnú obrazovku otvorte menu nastavenia prehlia
133daÄa a stlaÄte <strong>PridaÅ¥ na úvodnú obrazovku</strong>. <small>K menu sa dostanete stlaÄenÃm hardwaroveho tlaÄidla, ak ho vaÅ¡e zariadenie má, alebo stlaÄenÃm pravej hornej menu ikony <span class="ath-action-icon">icon</span>.</small>' 134 }, 135 136 sv_se: { 137 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>.', 138 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>Man 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 %icon.</small>' 139 }, 140 141 tr_tr: { 142 ios: 'Uygulamayı ana ekrana eklemek için, %icon ve ardından <strong>ana ekrana ekle</strong> butonunu tıklayın.', 143 android: 'Uygulamayı ana ekrana eklemek için, menüye girin ve <strong>ana ekrana ekle</strong> butonunu tıklayın. <small>Cihazınız menü tuÅuna sahip ise menüye girmek için menü tuÅunu tıklayın. Aksi takdirde %icon butonunu tıklayın.</small>' 144 }, 145 146 uk_ua: { 147 ios: 'Щоб додаÑи Ñей ÑÐ°Ð¹Ñ Ð½Ð° поÑаÑковий екÑан, наÑиÑнÑÑÑ %icon, а поÑÑм <strong>Ðа поÑаÑковий екÑан</strong>.', 148 android: 'Щоб додаÑи Ñей ÑÐ°Ð¹Ñ Ð½Ð° домаÑнÑй екÑан, вÑдкÑийÑе Ð¼ÐµÐ½Ñ Ð±ÑаÑзеÑа Ñа вибеÑÑÑÑ <strong>ÐодаÑи на головний екÑан</strong>. <small>Це можливо зÑобиÑи, наÑиÑнÑвÑи ÐºÐ½Ð¾Ð¿ÐºÑ Ð¼ÐµÐ½Ñ Ð½Ð° ваÑÐ¾Ð¼Ñ ÑмаÑÑÑонÑ, ÑкÑо Ñака Ñ. Ðбо ж на ÑконÑÑ Ð·Ð²ÐµÑÑ Ñ ÑпÑава %icon.</small>' 149 }, 150 151 zh_cn: { 152 ios: 'å¦è¦æåºç¨ç¨åºå è³ä¸»å±å¹,请ç¹å»%icon, ç¶å<strong>æ·»å å°ä¸»å±å¹</strong>', 153 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 %icon.</small>' 154 }, 155 156 zh_tw: { 157 ios: 'å¦è¦ææç¨ç¨å¼å è³ä¸»å±å¹, è«é»æ%icon, ç¶å¾<strong>å è³ä¸»å±å¹</strong>.', 158 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 %icon.</small>' 159 } 160}; 161 162// Add 2 characters language support (Android mostly) 163for ( var lang in ath.intl ) { 164 ath.intl[lang.substr(0, 2)] = ath.intl[lang]; 165} 166 167// default options 168ath.defaults = { 169 appID: 'org.cubiq.addtohome', // local storage name (no need to change) 170 fontSize: 15, // base font size, used to properly resize the popup based on viewport scale factor 171 debug: false, // override browser checks 172 logging: false, // log reasons for showing or not showing to js console; defaults to true when debug is true 173 modal: false, // prevent further actions until the message is closed 174 mandatory: false, // you can't proceed if you don't add the app to the homescreen 175 autostart: true, // show the message automatically 176 skipFirstVisit: false, // show only to returning visitors (ie: skip the first time you visit) 177 startDelay: 1, // display the message after that many seconds from page load 178 lifespan: 15, // life of the message in seconds 179 displayPace: 1440, // minutes before the message is shown again (0: display every time, default 24 hours) 180 maxDisplayCount: 0, // absolute maximum number of times the message will be shown to the user (0: no limit) 181 icon: true, // add touch icon to the message 182 message: '', // the message can be customized 183 validLocation: [], // list of pages where the message will be shown (array of regexes) 184 onInit: null, // executed on instance creation 185 onShow: null, // executed when the message is shown 186 onRemove: null, // executed when the message is removed 187 onAdd: null, // when the application is launched the first time from the homescreen (guesstimate) 188 onPrivate: null, // executed if user is in private mode 189 privateModeOverride: false, // show the message even in private mode (very rude) 190 detectHomescreen: false // try to detect if the site has been added to the homescreen (false | true | 'hash' | 'queryString' | 'smartURL') 191}; 192 193// browser info and capability 194var _ua = window.navigator.userAgent; 195 196var _nav = window.navigator; 197_extend(ath, { 198 hasToken: document.location.hash == '#ath' || _reSmartURL.test(document.location.href) || _reQueryString.test(document.location.search), 199 isRetina: window.devicePixelRatio && window.devicePixelRatio > 1, 200 isIDevice: (/iphone|ipod|ipad/i).test(_ua), 201 isMobileChrome: _ua.indexOf('Android') > -1 && (/Chrome\/[.0-9]*/).test(_ua) && _ua.indexOf("Version") == -1, 202 isMobileIE: _ua.indexOf('Windows Phone') > -1, 203 language: _nav.language && _nav.language.toLowerCase().replace('-', '_') || '' 204}); 205 206// falls back to en_us if language is unsupported 207ath.language = ath.language && ath.language in ath.intl ? ath.language : 'en_us'; 208 209ath.isMobileSafari = ath.isIDevice && _ua.indexOf('Safari') > -1 && _ua.indexOf('CriOS') < 0; 210ath.OS = ath.isIDevice ? 'ios' : ath.isMobileChrome ? 'android' : ath.isMobileIE ? 'windows' : 'unsupported'; 211 212ath.OSVersion = _ua.match(/(OS|Android) (\d+[_\.]\d+)/); 213ath.OSVersion = ath.OSVersion && ath.OSVersion[2] ? +ath.OSVersion[2].replace('_', '.') : 0; 214 215ath.isStandalone = 'standalone' in window.navigator && window.navigator.standalone; 216ath.isTablet = (ath.isMobileSafari && _ua.indexOf('iPad') > -1) || (ath.isMobileChrome && _ua.indexOf('Mobile') < 0); 217 218ath.isCompatible = (ath.isMobileSafari && ath.OSVersion >= 6) || ath.isMobileChrome; // TODO: add winphone 219 220var _defaultSession = { 221 lastDisplayTime: 0, // last time we displayed the message 222 returningVisitor: false, // is this the first time you visit
223 displayCount: 0, // number of times the message has been shown 224 optedout: false, // has the user opted out 225 added: false // has been actually added to the homescreen 226}; 227 228ath.removeSession = function (appID) { 229 try { 230 if (!localStorage) { 231 throw new Error('localStorage is not defined'); 232 } 233 234 localStorage.removeItem(appID || ath.defaults.appID); 235 } catch (e) { 236 // we are most likely in private mode 237 } 238}; 239 240ath.doLog = function (logStr) { 241 if ( this.options.logging ) { 242 console.log(logStr); 243 } 244}; 245 246ath.Class = function (options) { 247 // class methods 248 this.doLog = ath.doLog; 249 250 // merge default options with user config 251 this.options = _extend({}, ath.defaults); 252 _extend(this.options, options); 253 // override defaults that are dependent on each other 254 if ( this.options && this.options.debug && (typeof this.options.logging === "undefined") ) { 255 this.options.logging = true; 256 } 257 258 // IE<9 so exit (I hate you, really) 259 if ( !_eventListener ) { 260 return; 261 } 262 263 // normalize some options 264 this.options.mandatory = this.options.mandatory && ( 'standalone' in window.navigator || this.options.debug ); 265 this.options.modal = this.options.modal || this.options.mandatory; 266 if ( this.options.mandatory ) { 267 this.options.startDelay = -0.5; // make the popup hasty 268 } 269 this.options.detectHomescreen = this.options.detectHomescreen === true ? 'hash' : this.options.detectHomescreen; 270 271 // setup the debug environment 272 if ( this.options.debug ) { 273 ath.isCompatible = true; 274 ath.OS = typeof this.options.debug == 'string' ? this.options.debug : ath.OS == 'unsupported' ? 'android' : ath.OS; 275 ath.OSVersion = ath.OS == 'ios' ? '8' : '4'; 276 } 277 278 // the element the message will be appended to 279 this.container = document.body; 280 281 // load session 282 this.session = this.getItem(this.options.appID); 283 this.session = this.session ? JSON.parse(this.session) : undefined; 284 285 // user most likely came from a direct link containing our token, we don't need it and we remove it 286 if ( ath.hasToken && ( !ath.isCompatible || !this.session ) ) { 287 ath.hasToken = false; 288 _removeToken(); 289 } 290 291 // the device is not supported 292 if ( !ath.isCompatible ) { 293 this.doLog("Add to homescreen: not displaying callout because device not supported"); 294 return; 295 } 296 297 this.session = this.session || _defaultSession; 298 299 // check if we can use the local storage 300 try { 301 if (!localStorage) { 302 throw new Error('localStorage is not defined'); 303 } 304 305 localStorage.setItem(this.options.appID, JSON.stringify(this.session)); 306 ath.hasLocalStorage = true; 307 } catch (e) { 308 // we are most likely in private mode 309 ath.hasLocalStorage = false; 310 311 if ( this.options.onPrivate ) { 312 this.options.onPrivate.call(this); 313 } 314 } 315 316 // check if this is a valid location 317 var isValidLocation = !this.options.validLocation.length; 318 for ( var i = this.options.validLocation.length; i--; ) { 319 if ( this.options.validLocation[i].test(document.location.href) ) { 320 isValidLocation = true; 321 break; 322 } 323 } 324 325 // check compatibility with old versions of add to homescreen. Opt-out if an old session is found 326 if ( this.getItem('addToHome') ) { 327 this.optOut(); 328 } 329 330 // critical errors: 331 if ( this.session.optedout ) { 332 this.doLog("Add to homescreen: not displaying callout because user opted out"); 333 return; 334 } 335 if ( this.session.added ) { 336 this.doLog("Add to homescreen: not displaying callout because already added to the homescreen"); 337 return; 338 } 339 if ( !isValidLocation ) { 340 this.doLog("Add to homescreen: not displaying callout because not a valid location"); 341 return; 342 } 343 344 // check if the app is in stand alone mode 345 if ( ath.isStandalone ) { 346 // execute the onAdd event if we haven't already 347 if ( !this.session.added ) { 348 this.session.added = true; 349 this.updateSession(); 350 351 if ( this.options.onAdd && ath.hasLocalStorage ) { // double check on localstorage to avoid multiple calls to the custom event 352 this.options.onAdd.call(this); 353 } 354 } 355 356 this.doLog("Add to homescreen: not displaying callout because in standalone mode"); 357 return; 358 } 359 360 // (try to) check if the page has been added to the homescreen 361 if ( this.options.detectHomescreen ) { 362 // the URL has the token, we are likely coming from the homescreen 363 if ( ath.hasToken ) { 364 _removeToken(); // we don't actually need the token anymore, we remove it to prevent redistribution 365 366 // this is called the first time the user opens the app from the homescreen 367 if ( !this.session.added ) { 368 this.session.added = true; 369 this.updateSession(); 370 371 if ( this.options.onAdd && ath.hasLocalStorage ) { // double check on localstorage to avoid multiple calls to the custom event 372 this.options.onAdd.call(this); 373 } 374 } 375 376 this.doLog("Add to homescreen: not displaying callout because URL has token, so we are likely coming from homescreen"); 377 return; 378 } 379 380 // URL doesn't have the token, so add it 381 if ( this.options.detectHomescreen == 'hash' ) { 382 history.replaceState('', window.document.title, document.location.href + '#ath'); 383 } else if ( this.options.detectHomescreen == 'smartURL' ) { 384 history.replaceState('', window.document.title, document.location.href.replace(/(\/)?$/, '/ath$1')); 385 } else { 386 history.replaceState('', window.document.title, document.location.href + (document.location.search ? '&' : '?' ) + 'ath='); 387 } 388 } 389 390 // check if this is a returning visitor 391 if ( !this.session.returningVisitor ) { 392 this.session.returningVisitor = true; 393 this.updateSession(); 394 395 // we do not show the message if this is your first visit 396 if ( this.options.skipFirstVisit ) { 397 this.doLog("Add to homescreen: not displaying callout because skipping first visit"); 398 return; 399 } 400 } 401 402 // we do no show the message in private mode 403 if ( !this.options.privateModeOverride && !ath.hasLocalStorage ) { 404 this.doLog("Add to homescreen: not displaying callout because browser is in private mode"); 405 return; 406 } 407 408 // all checks passed, ready to display 409 this.ready = true; 410 411 if ( this.options.onInit ) { 412 this.options.onInit.call(this); 413 } 414 415 if ( this.options.autostart ) { 416 this.doLog("Add to homescreen: autostart displaying callout"); 417 this.show(); 418 } 419}; 420 421ath.Class.prototype = { 422 // event type to method conversion 423 events: { 424 load: '_delayedShow', 425 error: '_delayedShow', 426 orientationchange: 'resize', 427 resize: 'resize', 428 scroll: 'resize', 429 click: 'remove', 430 touchmove: '_preventDefault', 431 transitionend: '_removeElements', 432 webkitTransitionEnd: '_removeElements', 433 MSTransitionEnd: '_removeElements' 434 }, 435 436 handleEvent: function (e) { 437 var type = this.events[e.type]; 438 if ( type ) { 439 this[type](e); 440 } 441 }, 442 443 show: function (force) { 444 // in autostart mode wait for the document to be ready 445 if ( this.options.autostart && !_DOMReady ) { 446 setTimeout(this.show.bind(this), 50); 447 // we are not displaying callout because DOM not ready, but don't log that because 448 // it would log too frequently 449 return; 450 } 451 452 // message already on screen 453 if ( this.shown ) { 454 this.doLog("Add to homescreen: not displaying callout because already shown on screen"); 455 return; 456 } 457 458 var now = Date.now(); 459 var lastDisplayTime = this.session.lastDisplayTime; 460 461 if ( force !== true ) { 462 // this is needed if autostart is disabled and you programmatically call the show() method 463 if ( !this.ready ) { 464 this.doLog("Add to homescreen: not displaying callout because not ready"); 465 return; 466 } 467 468 // we obey the display pace (prevent the message to popup too often) 469 if ( now - lastDisplayTime < this.options.displayPace * 60000 ) { 470 this.doLog("Add to homescreen: not displaying callout because displayed recently"); 471 return; 472 } 473 474 // obey the maximum number of display count 475 if ( this.options.maxDisplayCount && this.session.displayCount >= this.options.maxDisplayCount ) { 476 this.doLog("Add to homescreen: not displaying callout because displayed too many times already"); 477 return; 478 } 479 } 480 481 this.shown = true; 482 483 // increment the display count 484 this.session.lastDisplayTime = now; 485 this.session.displayCount++; 486 this.updateSession(); 487 488 // try to get the highest resolution application icon 489 if ( !this.applicationIcon ) { 490 if ( ath.OS == 'ios' ) { 491 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]'); 492 } else { 493 this.applicationIcon = document.querySelector('head link[rel^="shortcut icon"][sizes="196x196"],head link[rel^=apple-touch-icon]'); 494 } 495 } 496 497 var message = ''; 498 499 if ( typeof this.options.message == 'object' && ath.language in this.options.message ) { // use custom language message 500 message = this.options.message[ath.language][ath.OS]; 501 } else if ( typeof this.options.message == 'object' && ath.OS in this.options.message ) { // use custom os message 502 message = this.options.message[ath.OS]; 503 } else if ( this.options.message in ath.intl ) { // you can force the locale 504 message = ath.intl[this.options.message][ath.OS]; 505 } else if ( this.options.message !== '' ) { // use a custom message 506 message = this.options.message; 507 } else if ( ath.OS in ath.intl[ath.language] ) { // otherwise we use our message 508 message = ath.intl[ath.language][ath.OS]; 509 } 510 511 // add the action icon 512 message = '<p>' + message.replace(/%icon(?:\[([^\]]+)\])?/gi, function(matches, group1) { 513 return '<span class="ath-action-icon">' + (!!group1 ? group1 : 'icon') + '</span>'; 514 }) + '</p>'; 515 516 // create the message container 517 this.viewport = document.createElement('div'); 518 this.viewport.className = 'ath-viewport'; 519 if ( this.options.modal ) { 520 this.viewport.className += ' ath-modal'; 521 } 522 if ( this.options.mandatory ) { 523 this.viewport.className += ' ath-mandatory'; 524 } 525 this.viewport.style.position = 'absolute'; 526 527 // create the actual message element 528 this.element = document.createElement('div'); 529 this.element.className = 'ath-container ath-' + ath.OS + ' ath-' + ath.OS + (parseInt(ath.OSVersion) || '') + ' ath-' + (ath.isTablet ? 'tablet' : 'phone'); 530 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;'; 531 this.element.style.webkitTransform = 'translate3d(0,-' + window.innerHeight + 'px,0)'; 532 this.element.style.transform = 'translate3d(0,-' + window.innerHeight + 'px,0)'; 533 534 // add the application icon 535 if ( this.options.icon && this.applicationIcon ) { 536 this.element.className += ' ath-icon'; 537 this.img = document.createElement('img'); 538 this.img.className = 'ath-application-icon'; 539 this.img.addEventListener('load', this, false); 540 this.img.addEventListener('error', this, false); 541 542 this.img.src = this.applicationIcon.href; 543 this.element.appendChild(this.img); 544 } 545 546 this.element.innerHTML += message; 547 548 // we are not ready to show, place the message out of sight 549 this.viewport.style.left = '-99999em'; 550 551 // attach all elements to the DOM 552 this.viewport.appendChild(this.element); 553 this.container.appendChild(this.viewport); 554 555 // if we don't have to wait for an image to load, show the message right away 556 if ( this.img ) { 557 this.doLog("Add to homescreen: not displaying callout because waiting for img to load"); 558 } else { 559 this._delayedShow(); 560 } 561 }, 562 563 _delayedShow: function (e) { 564 setTimeout(this._show.bind(this), this.options.startDelay * 1000 + 500); 565 }, 566 567 _show: function () { 568 var that = this; 569 570 // update the viewport size and orientation 571 this.updateViewport(); 572 573 // reposition/resize the message on orientation change 574 window.addEventListener('resize', this, false); 575 window.addEventListener('scroll', this, false); 576 window.addEventListener('orientationchange', this, false); 577 578 if ( this.options.modal ) { 579 // lock any other interaction 580 document.addEventListener('touchmove', this, true); 581 } 582 583 // Enable closing after 1 second 584 if ( !this.options.mandatory ) { 585 setTimeout(function () { 586 that.element.addEventListener('click', that, true); 587 }, 1000); 588 } 589 590 // kick the animation 591 setTimeout(function () { 592 that.element.style.webkitTransitionDuration = '1.2s'; 593 that.element.style.transitionDuration = '1.2s';
594 that.element.style.webkitTransform = 'translate3d(0,0,0)'; 595 that.element.style.transform = 'translate3d(0,0,0)'; 596 }, 0); 597 598 // set the destroy timer 599 if ( this.options.lifespan ) { 600 this.removeTimer = setTimeout(this.remove.bind(this), this.options.lifespan * 1000); 601 } 602 603 // fire the custom onShow event 604 if ( this.options.onShow ) { 605 this.options.onShow.call(this); 606 } 607 }, 608 609 remove: function () { 610 clearTimeout(this.removeTimer); 611 612 // clear up the event listeners 613 if ( this.img ) { 614 this.img.removeEventListener('load', this, false); 615 this.img.removeEventListener('error', this, false); 616 } 617 618 window.removeEventListener('resize', this, false); 619 window.removeEventListener('scroll', this, false); 620 window.removeEventListener('orientationchange', this, false); 621 document.removeEventListener('touchmove', this, true); 622 this.element.removeEventListener('click', this, true); 623 624 // remove the message element on transition end 625 this.element.addEventListener('transitionend', this, false); 626 this.element.addEventListener('webkitTransitionEnd', this, false); 627 this.element.addEventListener('MSTransitionEnd', this, false); 628 629 // start the fade out animation 630 this.element.style.webkitTransitionDuration = '0.3s'; 631 this.element.style.opacity = '0'; 632 }, 633 634 _removeElements: function () { 635 this.element.removeEventListener('transitionend', this, false); 636 this.element.removeEventListener('webkitTransitionEnd', this, false); 637 this.element.removeEventListener('MSTransitionEnd', this, false); 638 639 // remove the message from the DOM 640 this.container.removeChild(this.viewport); 641 642 this.shown = false; 643 644 // fire the custom onRemove event 645 if ( this.options.onRemove ) { 646 this.options.onRemove.call(this); 647 } 648 }, 649 650 updateViewport: function () { 651 if ( !this.shown ) { 652 return; 653 } 654 655 this.viewport.style.width = window.innerWidth + 'px'; 656 this.viewport.style.height = window.innerHeight + 'px'; 657 this.viewport.style.left = window.scrollX + 'px'; 658 this.viewport.style.top = window.scrollY + 'px'; 659 660 var clientWidth = document.documentElement.clientWidth; 661 662 this.orientation = clientWidth > document.documentElement.clientHeight ? 'landscape' : 'portrait'; 663 664 var screenWidth = ath.OS == 'ios' ? this.orientation == 'portrait' ? screen.width : screen.height : screen.width; 665 this.scale = screen.width > clientWidth ? 1 : screenWidth / window.innerWidth; 666 667 this.element.style.fontSize = this.options.fontSize / this.scale + 'px'; 668 }, 669 670 resize: function () { 671 clearTimeout(this.resizeTimer); 672 this.resizeTimer = setTimeout(this.updateViewport.bind(this), 100); 673 }, 674 675 updateSession: function () { 676 if ( ath.hasLocalStorage === false ) { 677 return; 678 } 679 680 if (localStorage) { 681 localStorage.setItem(this.options.appID, JSON.stringify(this.session)); 682 } 683 }, 684 685 clearSession: function () { 686 this.session = _defaultSession; 687 this.updateSession(); 688 }, 689 690 getItem: function(item) { 691 try { 692 if (!localStorage) { 693 throw new Error('localStorage is not defined'); 694 } 695 696 return localStorage.getItem(item); 697 } catch(e) { 698 // Preventing exception for some browsers when fetching localStorage key 699 ath.hasLocalStorage = false; 700 } 701 }, 702 703 optOut: function () { 704 this.session.optedout = true; 705 this.updateSession(); 706 }, 707 708 optIn: function () { 709 this.session.optedout = false; 710 this.updateSession(); 711 }, 712 713 clearDisplayCount: function () { 714 this.session.displayCount = 0; 715 this.updateSession(); 716 }, 717 718 _preventDefault: function (e) { 719 e.preventDefault(); 720 e.stopPropagation(); 721 } 722}; 723 724// utility 725function _extend (target, obj) { 726 for ( var i in obj ) { 727 target[i] = obj[i]; 728 } 729 730 return target; 731} 732 733function _removeToken () { 734 if ( document.location.hash == '#ath' ) { 735 history.replaceState('', window.document.title, document.location.href.split('#')[0]); 736 } 737 738 if ( _reSmartURL.test(document.location.href) ) { 739 history.replaceState('', window.document.title, document.location.href.replace(_reSmartURL, '$1')); 740 } 741 742 if ( _reQueryString.test(document.location.search) ) { 743 history.replaceState('', window.document.title, document.location.href.replace(_reQueryString, '$2')); 744 } 745} 746 747// expose to the world 748window.addToHomescreen = ath; 749 750})(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.