Skip to content
This repository has been archived by the owner before Nov 9, 2022. It is now read-only.
Permalink
Newer
Older
100644 750 lines (614 sloc) 32.1 KB
Aug 19, 2016
1
/* Add to Homescreen v3.2.3 ~ (c) 2015 Matteo Spinelli ~ @license: http://cubiq.org/license */
Apr 6, 2014
2
(function (window, document) {
3
/*
RC1
Apr 29, 2014
4
_ _ _____ _____
5
___ _| |_| |_ _|___| | |___ _____ ___ ___ ___ ___ ___ ___ ___
Apr 6, 2014
6
| .'| . | . | | | | . | | . | | -_|_ -| _| _| -_| -_| |
7
|__,|___|___| |_| |___|__|__|___|_|_|_|___|___|___|_| |___|___|_|_|
8
by Matteo Spinelli ~ http://cubiq.org
9
*/
10
Nov 22, 2014
11
// Check for addEventListener browser support (prevent errors in IE<9)
12
var _eventListener = 'addEventListener' in window;
13
Apr 6, 2014
14
// Check if document is loaded, needed by autostart
15
var _DOMReady = false;
May 22, 2014
16
if ( document.readyState === 'complete' ) {
Apr 6, 2014
17
_DOMReady = true;
Nov 22, 2014
18
} else if ( _eventListener ) {
19
window.addEventListener('load', loaded, false);
May 22, 2014
20
}
21
22
function loaded () {
23
window.removeEventListener('load', loaded, false);
24
_DOMReady = true;
Apr 6, 2014
25
}
26
27
// regex used to detect if app has been added to the homescreen
28
var _reSmartURL = /\/ath(\/)?$/;
29
var _reQueryString = /([\?&]ath=[^&]*$|&ath=[^&]*(&))/;
30
31
// singleton
32
var _instance;
33
function ath (options) {
RC1
Apr 29, 2014
34
_instance = _instance || new ath.Class(options);
Apr 6, 2014
35
RC1
Apr 29, 2014
36
return _instance;
Apr 6, 2014
37
}
38
39
// message in all supported languages
40
ath.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
},
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
Apr 6, 2014
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>'
Apr 6, 2014
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>.',
Aug 13, 2015
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
Apr 6, 2014
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>'
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
May 27, 2014
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>'
May 27, 2014
119
},
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>'
126
ru_ru: {
127
ios: 'Чтобы добавить этот сайт на свой домашний экран, нажмите на иконку %icon и затем <strong>На экран "Домой"</strong>.',
128
android: 'Чтобы добавить сайт на свой домашний экран, откройте меню браузера и нажмите на <strong>Добавить на главный экран</strong>. <small>Меню можно вызвать, нажав на кнопку меню вашего телефона, если она есть. Или найдите иконку сверху справа %icon[иконка].</small>'
129
},
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 prehliadač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
May 27, 2014
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.',
Jan 12, 2016
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>'
May 27, 2014
144
},
146
uk_ua: {
147
ios: 'Щоб додати цей сайт на початковий екран, натисніть %icon, а потім <strong>На початковий екран</strong>.',
148
android: 'Щоб додати цей сайт на домашній екран, відкрийте меню браузера та виберіть <strong>Додати на головний екран</strong>. <small>Це можливо зробити, натиснувши кнопку меню на вашому смартфоні, якщо така є. Або ж на іконці зверху справа %icon.</small>'
May 27, 2014
149
},
152
ios: '如要把应用程序加至主屏幕,请点击%icon, 然后<strong>添加到主屏幕</strong>',
Jul 9, 2015
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>'
Apr 6, 2014
160
};
161
162
// Add 2 characters language support (Android mostly)
163
for ( var lang in ath.intl ) {
164
ath.intl[lang.substr(0, 2)] = ath.intl[lang];
165
}
166
Apr 6, 2014
167
// default options
168
ath.defaults = {
RC1
Apr 29, 2014
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
Apr 6, 2014
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
Apr 6, 2014
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)
RC1
Apr 29, 2014
177
startDelay: 1, // display the message after that many seconds from page load
Apr 6, 2014
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)
Apr 6, 2014
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
194
var _ua = window.navigator.userAgent;
Sep 27, 2014
195
Apr 6, 2014
196
var _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,
Apr 6, 2014
202
isMobileIE: _ua.indexOf('Windows Phone') > -1,
203
language: _nav.language && _nav.language.toLowerCase().replace('-', '_') || ''
Apr 6, 2014
204
});
205
206
// falls back to en_us if language is unsupported
207
ath.language = ath.language && ath.language in ath.intl ? ath.language : 'en_us';
Apr 6, 2014
209
ath.isMobileSafari = ath.isIDevice && _ua.indexOf('Safari') > -1 && _ua.indexOf('CriOS') < 0;
210
ath.OS = ath.isIDevice ? 'ios' : ath.isMobileChrome ? 'android' : ath.isMobileIE ? 'windows' : 'unsupported';
211
RC1
Apr 29, 2014
212
ath.OSVersion = _ua.match(/(OS|Android) (\d+[_\.]\d+)/);
Apr 6, 2014
213
ath.OSVersion = ath.OSVersion && ath.OSVersion[2] ? +ath.OSVersion[2].replace('_', '.') : 0;
214
215
ath.isStandalone = 'standalone' in window.navigator && window.navigator.standalone;
Apr 6, 2014
216
ath.isTablet = (ath.isMobileSafari && _ua.indexOf('iPad') > -1) || (ath.isMobileChrome && _ua.indexOf('Mobile') < 0);
217
May 2, 2014
218
ath.isCompatible = (ath.isMobileSafari && ath.OSVersion >= 6) || ath.isMobileChrome; // TODO: add winphone
Apr 6, 2014
219
220
var _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
228
ath.removeSession = function (appID) {
229
try {
230
if (!localStorage) {
231
throw new Error('localStorage is not defined');
232
}
233
Apr 6, 2014
234
localStorage.removeItem(appID || ath.defaults.appID);
235
} catch (e) {
236
// we are most likely in private mode
237
}
238
};
239
240
ath.doLog = function (logStr) {
241
if ( this.options.logging ) {
242
console.log(logStr);
243
}
244
};
245
Apr 6, 2014
246
ath.Class = function (options) {
247
// class methods
248
this.doLog = ath.doLog;
249
Apr 6, 2014
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
Aug 18, 2016
254
if ( this.options && this.options.debug && (typeof this.options.logging === "undefined") ) {
255
this.options.logging = true;
256
}
Apr 6, 2014
257
Nov 22, 2014
258
// IE<9 so exit (I hate you, really)
259
if ( !_eventListener ) {
260
return;
261
}
262
Apr 6, 2014
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;
Sep 27, 2014
275
ath.OSVersion = ath.OS == 'ios' ? '8' : '4';
Apr 6, 2014
276
}
277
278
// the element the message will be appended to
279
this.container = document.body;
Apr 6, 2014
280
281
// load session
282
this.session = this.getItem(this.options.appID);
283
this.session = this.session ? JSON.parse(this.session) : undefined;
Apr 6, 2014
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
RC1
Apr 29, 2014
291
// the device is not supported
Apr 6, 2014
292
if ( !ath.isCompatible ) {
293
this.doLog("Add to homescreen: not displaying callout because device not supported");
Apr 6, 2014
294
return;
295
}
Sep 27, 2014
296
Apr 6, 2014
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
Apr 6, 2014
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
RC1
Apr 29, 2014
325
// check compatibility with old versions of add to homescreen. Opt-out if an old session is found
326
if ( this.getItem('addToHome') ) {
RC1
Apr 29, 2014
327
this.optOut();
328
}
329
Apr 6, 2014
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");
Apr 6, 2014
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");
Apr 6, 2014
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
RC1
Apr 29, 2014
365
Apr 6, 2014
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");
Apr 6, 2014
377
return;
378
}
379
RC1
Apr 29, 2014
380
// URL doesn't have the token, so add it
Apr 6, 2014
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");
Apr 6, 2014
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");
Apr 6, 2014
405
return;
406
}
407
RC1
Apr 29, 2014
408
// all checks passed, ready to display
Apr 6, 2014
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");
Apr 6, 2014
417
this.show();
418
}
419
};
420
421
ath.Class.prototype = {
422
// event type to method conversion
423
events: {
424
load: '_delayedShow',
RC1
Apr 29, 2014
425
error: '_delayedShow',
Apr 6, 2014
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
Apr 6, 2014
449
return;
450
}
451
RC1
Apr 29, 2014
452
// message already on screen
453
if ( this.shown ) {
454
this.doLog("Add to homescreen: not displaying callout because already shown on screen");
RC1
Apr 29, 2014
455
return;
456
}
457
Apr 6, 2014
458
var now = Date.now();
459
var lastDisplayTime = this.session.lastDisplayTime;
460
RC1
Apr 29, 2014
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");
RC1
Apr 29, 2014
465
return;
466
}
Apr 6, 2014
467
RC1
Apr 29, 2014
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");
RC1
Apr 29, 2014
471
return;
472
}
Apr 6, 2014
473
RC1
Apr 29, 2014
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");
RC1
Apr 29, 2014
477
return;
478
}
Apr 6, 2014
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];
May 18, 2015
503
} else if ( this.options.message in ath.intl ) { // you can force the locale
504
message = ath.intl[this.options.message][ath.OS];
May 18, 2015
505
} else if ( this.options.message !== '' ) { // use a custom message
Apr 6, 2014
506
message = this.options.message;
May 18, 2015
507
} else if ( ath.OS in ath.intl[ath.language] ) { // otherwise we use our message
508
message = ath.intl[ath.language][ath.OS];
Apr 6, 2014
509
}
510
511
// add the action icon
512
message = '<p>' + message.replace(/%icon(?:\[([^\]]+)\])?/gi, function(matches, group1) {
Jun 29, 2015
513
return '<span class="ath-action-icon">' + (!!group1 ? group1 : 'icon') + '</span>';
514
}) + '</p>';
Apr 6, 2014
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');
Jul 27, 2016
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;';
Apr 6, 2014
531
this.element.style.webkitTransform = 'translate3d(0,-' + window.innerHeight + 'px,0)';
532
this.element.style.transform = 'translate3d(0,-' + window.innerHeight + 'px,0)';
Apr 6, 2014
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 {
Apr 6, 2014
559
this._delayedShow();
560
}
561
},
562
RC1
Apr 29, 2014
563
_delayedShow: function (e) {
Apr 6, 2014
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);
RC1
Apr 29, 2014
575
window.addEventListener('scroll', this, false);
576
window.addEventListener('orientationchange', this, false);
Apr 6, 2014
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)';
Apr 6, 2014
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
RC1
Apr 29, 2014
613
if ( this.img ) {
614
this.img.removeEventListener('load', this, false);
615
this.img.removeEventListener('error', this, false);
616
}
Apr 6, 2014
617
618
window.removeEventListener('resize', this, false);
619
window.removeEventListener('scroll', this, false);
RC1
Apr 29, 2014
620
window.removeEventListener('orientationchange', this, false);
Apr 6, 2014
621
document.removeEventListener('touchmove', this, true);
622
this.element.removeEventListener('click', this, true);
623
RC1
Apr 29, 2014
624
// remove the message element on transition end
Apr 6, 2014
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
}
Apr 6, 2014
683
},
684
685
clearSession: function () {
686
this.session = _defaultSession;
687
this.updateSession();
688
},
689
May 17, 2015
690
getItem: function(item) {
691
try {
692
if (!localStorage) {
693
throw new Error('localStorage is not defined');
694
}
695
May 17, 2015
696
return localStorage.getItem(item);
697
} catch(e) {
698
// Preventing exception for some browsers when fetching localStorage key
699
ath.hasLocalStorage = false;
700
}
701
},
Apr 6, 2014
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
725
function _extend (target, obj) {
726
for ( var i in obj ) {
727
target[i] = obj[i];
728
}
729
730
return target;
731
}
732
733
function _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
748
window.addToHomescreen = ath;
749
RC1
Apr 29, 2014
750
})(window, document);