addtohomescreen.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. /* Add to Homescreen v3.1.1 ~ (c) 2014 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. // Check for addEventListener browser support (prevent errors in IE<9)
  11. var _eventListener = 'addEventListener' in window;
  12. // Check if document is loaded, needed by autostart
  13. var _DOMReady = false;
  14. if ( document.readyState === 'complete' ) {
  15. _DOMReady = true;
  16. } else if ( _eventListener ) {
  17. window.addEventListener('load', loaded, false);
  18. }
  19. function loaded () {
  20. window.removeEventListener('load', loaded, false);
  21. _DOMReady = true;
  22. }
  23. // regex used to detect if app has been added to the homescreen
  24. var _reSmartURL = /\/ath(\/)?$/;
  25. var _reQueryString = /([\?&]ath=[^&]*$|&ath=[^&]*(&))/;
  26. // singleton
  27. var _instance;
  28. function ath (options) {
  29. _instance = _instance || new ath.Class(options);
  30. return _instance;
  31. }
  32. // message in all supported languages
  33. ath.intl = {
  34. de_de: {
  35. ios: 'Um diese Web-App zum Home-Bildschirm hinzuzufügen, tippen Sie auf %icon und dann <strong>Zum Home-Bildschirm</strong>.',
  36. 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>',
  37. },
  38. en_us: {
  39. ios: 'To add this web app to the home screen: tap %icon and then <strong>Add to Home Screen</strong>.',
  40. 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>',
  41. },
  42. es_es: {
  43. 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>.',
  44. 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>',
  45. },
  46. fr_fr: {
  47. ios: 'Pour ajouter cette application web sur l\'écran d\'accueil : Appuyez %icon et sélectionnez <strong>Ajouter sur l\'écran d\'accueil</strong>.',
  48. 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>',
  49. },
  50. he_il: {
  51. ios: '<span dir="rtl">להוספת האפליקציה למסך הבית: ללחוץ על %icon ואז <strong>הוסף למסך הבית</strong>.</span>',
  52. 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>',
  53. },
  54. it_it: {
  55. ios: 'Per aggiungere questa web app alla schermata iniziale: premi %icon e poi <strong>Aggiungi a Home</strong>.',
  56. 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>',
  57. },
  58. nb_no: {
  59. ios: 'For å installere denne appen på hjem-skjermen: trykk på %icon og deretter <strong>Legg til på Hjem-skjerm</strong>.',
  60. 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>',
  61. },
  62. pt_br: {
  63. ios: 'Para adicionar este app à tela de início: clique %icon e então <strong>Tela de início</strong>.',
  64. 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>',
  65. },
  66. pt_pt: {
  67. ios: 'Para adicionar esta app ao ecrã principal: clique %icon e depois <strong>Ecrã principal</strong>.',
  68. 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>',
  69. },
  70. nl_nl: {
  71. ios: 'Om deze webapp op je telefoon te installeren, klik op %icon en dan <strong>Zet in beginscherm</strong>.',
  72. 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>',
  73. },
  74. sv_se: {
  75. 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>.',
  76. 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>',
  77. },
  78. zh_cn: {
  79. ios: '如要把应用程式加至主屏幕,请点击%icon, 然后<strong>加至主屏幕</strong>',
  80. 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>',
  81. },
  82. zh_tw: {
  83. ios: '如要把應用程式加至主屏幕, 請點擊%icon, 然後<strong>加至主屏幕</strong>.',
  84. 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>',
  85. }
  86. };
  87. // Add 2 characters language support (Android mostly)
  88. for ( var lang in ath.intl ) {
  89. ath.intl[lang.substr(0, 2)] = ath.intl[lang];
  90. }
  91. // default options
  92. ath.defaults = {
  93. appID: 'org.cubiq.addtohome', // local storage name (no need to change)
  94. fontSize: 15, // base font size, used to properly resize the popup based on viewport scale factor
  95. debug: false, // override browser checks
  96. modal: false, // prevent further actions until the message is closed
  97. mandatory: false, // you can't proceed if you don't add the app to the homescreen
  98. autostart: true, // show the message automatically
  99. skipFirstVisit: false, // show only to returning visitors (ie: skip the first time you visit)
  100. startDelay: 1, // display the message after that many seconds from page load
  101. lifespan: 15, // life of the message in seconds
  102. displayPace: 1440, // minutes before the message is shown again (0: display every time, default 24 hours)
  103. maxDisplayCount: 0, // absolute maximum number of times the message will be shown to the user (0: no limit)
  104. icon: true, // add touch icon to the message
  105. message: '', // the message can be customized
  106. validLocation: [], // list of pages where the message will be shown (array of regexes)
  107. onInit: null, // executed on instance creation
  108. onShow: null, // executed when the message is shown
  109. onRemove: null, // executed when the message is removed
  110. onAdd: null, // when the application is launched the first time from the homescreen (guesstimate)
  111. onPrivate: null, // executed if user is in private mode
  112. privateModeOverride: false, // show the message even in private mode (very rude)
  113. detectHomescreen: false // try to detect if the site has been added to the homescreen (false | true | 'hash' | 'queryString' | 'smartURL')
  114. };
  115. // browser info and capability
  116. var _ua = window.navigator.userAgent;
  117. var _nav = window.navigator;
  118. _extend(ath, {
  119. hasToken: document.location.hash == '#ath' || _reSmartURL.test(document.location.href) || _reQueryString.test(document.location.search),
  120. isRetina: window.devicePixelRatio && window.devicePixelRatio > 1,
  121. isIDevice: (/iphone|ipod|ipad/i).test(_ua),
  122. isMobileChrome: _ua.indexOf('Android') > -1 && (/Chrome\/[.0-9]*/).test(_ua),
  123. isMobileIE: _ua.indexOf('Windows Phone') > -1,
  124. language: _nav.language && _nav.language.toLowerCase().replace('-', '_') || ''
  125. });
  126. // falls back to en_us if language is unsupported
  127. ath.language = ath.language && ath.language in ath.intl ? ath.language : 'en_us';
  128. ath.isMobileSafari = ath.isIDevice && _ua.indexOf('Safari') > -1 && _ua.indexOf('CriOS') < 0;
  129. ath.OS = ath.isIDevice ? 'ios' : ath.isMobileChrome ? 'android' : ath.isMobileIE ? 'windows' : 'unsupported';
  130. ath.OSVersion = _ua.match(/(OS|Android) (\d+[_\.]\d+)/);
  131. ath.OSVersion = ath.OSVersion && ath.OSVersion[2] ? +ath.OSVersion[2].replace('_', '.') : 0;
  132. ath.isStandalone = window.navigator.standalone || ( ath.isMobileChrome && ( screen.height - document.documentElement.clientHeight < 40 ) ); // TODO: check the lame polyfill
  133. ath.isTablet = (ath.isMobileSafari && _ua.indexOf('iPad') > -1) || (ath.isMobileChrome && _ua.indexOf('Mobile') < 0);
  134. ath.isCompatible = (ath.isMobileSafari && ath.OSVersion >= 6) || ath.isMobileChrome; // TODO: add winphone
  135. var _defaultSession = {
  136. lastDisplayTime: 0, // last time we displayed the message
  137. returningVisitor: false, // is this the first time you visit
  138. displayCount: 0, // number of times the message has been shown
  139. optedout: false, // has the user opted out
  140. added: false // has been actually added to the homescreen
  141. };
  142. ath.removeSession = function (appID) {
  143. try {
  144. localStorage.removeItem(appID || ath.defaults.appID);
  145. } catch (e) {
  146. // we are most likely in private mode
  147. }
  148. };
  149. ath.Class = function (options) {
  150. // merge default options with user config
  151. this.options = _extend({}, ath.defaults);
  152. _extend(this.options, options);
  153. // IE<9 so exit (I hate you, really)
  154. if ( !_eventListener ) {
  155. return;
  156. }
  157. // normalize some options
  158. this.options.mandatory = this.options.mandatory && ( 'standalone' in window.navigator || this.options.debug );
  159. this.options.modal = this.options.modal || this.options.mandatory;
  160. if ( this.options.mandatory ) {
  161. this.options.startDelay = -0.5; // make the popup hasty
  162. }
  163. this.options.detectHomescreen = this.options.detectHomescreen === true ? 'hash' : this.options.detectHomescreen;
  164. // setup the debug environment
  165. if ( this.options.debug ) {
  166. ath.isCompatible = true;
  167. ath.OS = typeof this.options.debug == 'string' ? this.options.debug : ath.OS == 'unsupported' ? 'android' : ath.OS;
  168. ath.OSVersion = ath.OS == 'ios' ? '8' : '4';
  169. }
  170. // the element the message will be appended to
  171. this.container = document.documentElement;
  172. // load session
  173. this.session = localStorage.getItem(this.options.appID);
  174. this.session = this.session ? JSON.parse(this.session) : undefined;
  175. // user most likely came from a direct link containing our token, we don't need it and we remove it
  176. if ( ath.hasToken && ( !ath.isCompatible || !this.session ) ) {
  177. ath.hasToken = false;
  178. _removeToken();
  179. }
  180. // the device is not supported
  181. if ( !ath.isCompatible ) {
  182. return;
  183. }
  184. this.session = this.session || _defaultSession;
  185. // check if we can use the local storage
  186. try {
  187. localStorage.setItem(this.options.appID, JSON.stringify(this.session));
  188. ath.hasLocalStorage = true;
  189. } catch (e) {
  190. // we are most likely in private mode
  191. ath.hasLocalStorage = false;
  192. if ( this.options.onPrivate ) {
  193. this.options.onPrivate.call(this);
  194. }
  195. }
  196. // check if this is a valid location
  197. var isValidLocation = !this.options.validLocation.length;
  198. for ( var i = this.options.validLocation.length; i--; ) {
  199. if ( this.options.validLocation[i].test(document.location.href) ) {
  200. isValidLocation = true;
  201. break;
  202. }
  203. }
  204. // check compatibility with old versions of add to homescreen. Opt-out if an old session is found
  205. if ( localStorage.getItem('addToHome') ) {
  206. this.optOut();
  207. }
  208. // critical errors:
  209. // user opted out, already added to the homescreen, not a valid location
  210. if ( this.session.optedout || this.session.added || !isValidLocation ) {
  211. return;
  212. }
  213. // check if the app is in stand alone mode
  214. if ( ath.isStandalone ) {
  215. // execute the onAdd event if we haven't already
  216. if ( !this.session.added ) {
  217. this.session.added = true;
  218. this.updateSession();
  219. if ( this.options.onAdd && ath.hasLocalStorage ) { // double check on localstorage to avoid multiple calls to the custom event
  220. this.options.onAdd.call(this);
  221. }
  222. }
  223. return;
  224. }
  225. // (try to) check if the page has been added to the homescreen
  226. if ( this.options.detectHomescreen ) {
  227. // the URL has the token, we are likely coming from the homescreen
  228. if ( ath.hasToken ) {
  229. _removeToken(); // we don't actually need the token anymore, we remove it to prevent redistribution
  230. // this is called the first time the user opens the app from the homescreen
  231. if ( !this.session.added ) {
  232. this.session.added = true;
  233. this.updateSession();
  234. if ( this.options.onAdd && ath.hasLocalStorage ) { // double check on localstorage to avoid multiple calls to the custom event
  235. this.options.onAdd.call(this);
  236. }
  237. }
  238. return;
  239. }
  240. // URL doesn't have the token, so add it
  241. if ( this.options.detectHomescreen == 'hash' ) {
  242. history.replaceState('', window.document.title, document.location.href + '#ath');
  243. } else if ( this.options.detectHomescreen == 'smartURL' ) {
  244. history.replaceState('', window.document.title, document.location.href.replace(/(\/)?$/, '/ath$1'));
  245. } else {
  246. history.replaceState('', window.document.title, document.location.href + (document.location.search ? '&' : '?' ) + 'ath=');
  247. }
  248. }
  249. // check if this is a returning visitor
  250. if ( !this.session.returningVisitor ) {
  251. this.session.returningVisitor = true;
  252. this.updateSession();
  253. // we do not show the message if this is your first visit
  254. if ( this.options.skipFirstVisit ) {
  255. return;
  256. }
  257. }
  258. // we do no show the message in private mode
  259. if ( !this.options.privateModeOverride && !ath.hasLocalStorage ) {
  260. return;
  261. }
  262. // all checks passed, ready to display
  263. this.ready = true;
  264. if ( this.options.onInit ) {
  265. this.options.onInit.call(this);
  266. }
  267. if ( this.options.autostart ) {
  268. this.show();
  269. }
  270. };
  271. ath.Class.prototype = {
  272. // event type to method conversion
  273. events: {
  274. load: '_delayedShow',
  275. error: '_delayedShow',
  276. orientationchange: 'resize',
  277. resize: 'resize',
  278. scroll: 'resize',
  279. click: 'remove',
  280. touchmove: '_preventDefault',
  281. transitionend: '_removeElements',
  282. webkitTransitionEnd: '_removeElements',
  283. MSTransitionEnd: '_removeElements'
  284. },
  285. handleEvent: function (e) {
  286. var type = this.events[e.type];
  287. if ( type ) {
  288. this[type](e);
  289. }
  290. },
  291. show: function (force) {
  292. // in autostart mode wait for the document to be ready
  293. if ( this.options.autostart && !_DOMReady ) {
  294. setTimeout(this.show.bind(this), 50);
  295. return;
  296. }
  297. // message already on screen
  298. if ( this.shown ) {
  299. return;
  300. }
  301. var now = Date.now();
  302. var lastDisplayTime = this.session.lastDisplayTime;
  303. if ( force !== true ) {
  304. // this is needed if autostart is disabled and you programmatically call the show() method
  305. if ( !this.ready ) {
  306. return;
  307. }
  308. // we obey the display pace (prevent the message to popup too often)
  309. if ( now - lastDisplayTime < this.options.displayPace * 60000 ) {
  310. return;
  311. }
  312. // obey the maximum number of display count
  313. if ( this.options.maxDisplayCount && this.session.displayCount >= this.options.maxDisplayCount ) {
  314. return;
  315. }
  316. }
  317. this.shown = true;
  318. // increment the display count
  319. this.session.lastDisplayTime = now;
  320. this.session.displayCount++;
  321. this.updateSession();
  322. // try to get the highest resolution application icon
  323. if ( !this.applicationIcon ) {
  324. if ( ath.OS == 'ios' ) {
  325. 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]');
  326. } else {
  327. this.applicationIcon = document.querySelector('head link[rel^="shortcut icon"][sizes="196x196"],head link[rel^=apple-touch-icon]');
  328. }
  329. }
  330. var message = '';
  331. if ( this.options.message in ath.intl ) { // you can force the locale
  332. message = ath.intl[this.options.message][ath.OS];
  333. } else if ( this.options.message !== '' ) { // or use a custom message
  334. message = this.options.message;
  335. } else { // otherwise we use our message
  336. message = ath.intl[ath.language][ath.OS];
  337. }
  338. // add the action icon
  339. message = '<p>' + message.replace('%icon', '<span class="ath-action-icon">icon</span>') + '</p>';
  340. // create the message container
  341. this.viewport = document.createElement('div');
  342. this.viewport.className = 'ath-viewport';
  343. if ( this.options.modal ) {
  344. this.viewport.className += ' ath-modal';
  345. }
  346. if ( this.options.mandatory ) {
  347. this.viewport.className += ' ath-mandatory';
  348. }
  349. this.viewport.style.position = 'absolute';
  350. // create the actual message element
  351. this.element = document.createElement('div');
  352. this.element.className = 'ath-container ath-' + ath.OS + ' ath-' + ath.OS + (ath.OSVersion + '').substr(0,1) + ' ath-' + (ath.isTablet ? 'tablet' : 'phone');
  353. 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;';
  354. this.element.style.webkitTransform = 'translate3d(0,-' + window.innerHeight + 'px,0)';
  355. this.element.style.transform = 'translate3d(0,-' + window.innerHeight + 'px,0)';
  356. // add the application icon
  357. if ( this.options.icon && this.applicationIcon ) {
  358. this.element.className += ' ath-icon';
  359. this.img = document.createElement('img');
  360. this.img.className = 'ath-application-icon';
  361. this.img.addEventListener('load', this, false);
  362. this.img.addEventListener('error', this, false);
  363. this.img.src = this.applicationIcon.href;
  364. this.element.appendChild(this.img);
  365. }
  366. this.element.innerHTML += message;
  367. // we are not ready to show, place the message out of sight
  368. this.viewport.style.left = '-99999em';
  369. // attach all elements to the DOM
  370. this.viewport.appendChild(this.element);
  371. this.container.appendChild(this.viewport);
  372. // if we don't have to wait for an image to load, show the message right away
  373. if ( !this.img ) {
  374. this._delayedShow();
  375. }
  376. },
  377. _delayedShow: function (e) {
  378. setTimeout(this._show.bind(this), this.options.startDelay * 1000 + 500);
  379. },
  380. _show: function () {
  381. var that = this;
  382. // update the viewport size and orientation
  383. this.updateViewport();
  384. // reposition/resize the message on orientation change
  385. window.addEventListener('resize', this, false);
  386. window.addEventListener('scroll', this, false);
  387. window.addEventListener('orientationchange', this, false);
  388. if ( this.options.modal ) {
  389. // lock any other interaction
  390. document.addEventListener('touchmove', this, true);
  391. }
  392. // Enable closing after 1 second
  393. if ( !this.options.mandatory ) {
  394. setTimeout(function () {
  395. that.element.addEventListener('click', that, true);
  396. }, 1000);
  397. }
  398. // kick the animation
  399. setTimeout(function () {
  400. that.element.style.webkitTransitionDuration = '1.2s';
  401. that.element.style.transitionDuration = '1.2s';
  402. that.element.style.webkitTransform = 'translate3d(0,0,0)';
  403. that.element.style.transform = 'translate3d(0,0,0)';
  404. }, 0);
  405. // set the destroy timer
  406. if ( this.options.lifespan ) {
  407. this.removeTimer = setTimeout(this.remove.bind(this), this.options.lifespan * 1000);
  408. }
  409. // fire the custom onShow event
  410. if ( this.options.onShow ) {
  411. this.options.onShow.call(this);
  412. }
  413. },
  414. remove: function () {
  415. clearTimeout(this.removeTimer);
  416. // clear up the event listeners
  417. if ( this.img ) {
  418. this.img.removeEventListener('load', this, false);
  419. this.img.removeEventListener('error', this, false);
  420. }
  421. window.removeEventListener('resize', this, false);
  422. window.removeEventListener('scroll', this, false);
  423. window.removeEventListener('orientationchange', this, false);
  424. document.removeEventListener('touchmove', this, true);
  425. this.element.removeEventListener('click', this, true);
  426. // remove the message element on transition end
  427. this.element.addEventListener('transitionend', this, false);
  428. this.element.addEventListener('webkitTransitionEnd', this, false);
  429. this.element.addEventListener('MSTransitionEnd', this, false);
  430. // start the fade out animation
  431. this.element.style.webkitTransitionDuration = '0.3s';
  432. this.element.style.opacity = '0';
  433. },
  434. _removeElements: function () {
  435. this.element.removeEventListener('transitionend', this, false);
  436. this.element.removeEventListener('webkitTransitionEnd', this, false);
  437. this.element.removeEventListener('MSTransitionEnd', this, false);
  438. // remove the message from the DOM
  439. this.container.removeChild(this.viewport);
  440. this.shown = false;
  441. // fire the custom onRemove event
  442. if ( this.options.onRemove ) {
  443. this.options.onRemove.call(this);
  444. }
  445. },
  446. updateViewport: function () {
  447. if ( !this.shown ) {
  448. return;
  449. }
  450. this.viewport.style.width = window.innerWidth + 'px';
  451. this.viewport.style.height = window.innerHeight + 'px';
  452. this.viewport.style.left = window.scrollX + 'px';
  453. this.viewport.style.top = window.scrollY + 'px';
  454. var clientWidth = document.documentElement.clientWidth;
  455. this.orientation = clientWidth > document.documentElement.clientHeight ? 'landscape' : 'portrait';
  456. var screenWidth = ath.OS == 'ios' ? this.orientation == 'portrait' ? screen.width : screen.height : screen.width;
  457. this.scale = screen.width > clientWidth ? 1 : screenWidth / window.innerWidth;
  458. this.element.style.fontSize = this.options.fontSize / this.scale + 'px';
  459. },
  460. resize: function () {
  461. clearTimeout(this.resizeTimer);
  462. this.resizeTimer = setTimeout(this.updateViewport.bind(this), 100);
  463. },
  464. updateSession: function () {
  465. if ( ath.hasLocalStorage === false ) {
  466. return;
  467. }
  468. localStorage.setItem(this.options.appID, JSON.stringify(this.session));
  469. },
  470. clearSession: function () {
  471. this.session = _defaultSession;
  472. this.updateSession();
  473. },
  474. optOut: function () {
  475. this.session.optedout = true;
  476. this.updateSession();
  477. },
  478. optIn: function () {
  479. this.session.optedout = false;
  480. this.updateSession();
  481. },
  482. clearDisplayCount: function () {
  483. this.session.displayCount = 0;
  484. this.updateSession();
  485. },
  486. _preventDefault: function (e) {
  487. e.preventDefault();
  488. e.stopPropagation();
  489. }
  490. };
  491. // utility
  492. function _extend (target, obj) {
  493. for ( var i in obj ) {
  494. target[i] = obj[i];
  495. }
  496. return target;
  497. }
  498. function _removeToken () {
  499. if ( document.location.hash == '#ath' ) {
  500. history.replaceState('', window.document.title, document.location.href.split('#')[0]);
  501. }
  502. if ( _reSmartURL.test(document.location.href) ) {
  503. history.replaceState('', window.document.title, document.location.href.replace(_reSmartURL, '$1'));
  504. }
  505. if ( _reQueryString.test(document.location.search) ) {
  506. history.replaceState('', window.document.title, document.location.href.replace(_reQueryString, '$2'));
  507. }
  508. }
  509. // expose to the world
  510. window.addToHomescreen = ath;
  511. })(window, document);