add-to-homescreen-rails 2.0.11 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 33563cc6a4d40df0a1db3bd9164dbcb510dbd3b1
4
- data.tar.gz: 9b8548bc42c1ed0a38022554f7ded14254698e9e
3
+ metadata.gz: 43848950e30ebbafde25389ad2d854996a5bc41c
4
+ data.tar.gz: c7b0e65e26182693cd08415049890ff67d00860e
5
5
  SHA512:
6
- metadata.gz: b201d11dfbfc19e09aceb307c8dfc4d944ae205ac93a18add684b3b1e528769fd5d73e4ba2d153e2cfa828e9f736f082d46016874a10b2ebce100f4a0b07cb92
7
- data.tar.gz: 83b06828330ace16c2d363410cf67bd1d1d0fd0a218cf302faca997d7f985965696e6ff14fe6548a3a1b8c42b15542568a29e6b6a4b0967f60c9017b0f1ab8fa
6
+ metadata.gz: 653f4252b0dc5a2f012f7ba277d73c2861dbec2d29764d51c9780c3e2eb64bae53e2450b7eebe81107f0bb34578bac1c034f488677af0496efdc2ec977c5deb9
7
+ data.tar.gz: 03321d356ed81b371acfa3df6193b4b44f92131f5395f80cad3cc75e07e45fdb851a3f280c5724dcf1f2c9773c12968da9ec71d28250146c13fb677ef41bc18d
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ *IMPORTANT:* If you are upgrading please note that there are major changes to the way version 3 works. Make sure you read the updated documentation and also update your rails require statement as shown below.
2
+
1
3
  # 'Add to Home screen' for Rails
2
4
 
3
5
  This gem vendors the 'Add to Home screen' assets for Rails 3.1 and greater.
@@ -16,21 +18,19 @@ For the orignal javascript and css file go to his [Github repository](https://gi
16
18
  In your Gemfile, add:
17
19
 
18
20
  ```ruby
19
- group :assets do
20
- gem 'add-to-homescreen-rails'
21
- end
21
+ gem 'add-to-homescreen-rails'
22
22
  ```
23
23
 
24
24
  You can include it by adding the following to your javascript file:
25
25
 
26
26
  ```javascript
27
- //= require add2home
27
+ //= require addtohomescreen
28
28
  ```
29
29
 
30
30
  And to the css file:
31
31
 
32
32
  ```css
33
33
  /*
34
- *= require add2home
34
+ *= require addtohomescreen
35
35
  */
36
36
  ```
@@ -0,0 +1,552 @@
1
+ /* Add to Homescreen v3.0.0 ~ (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
+
11
+ // Check if document is loaded, needed by autostart
12
+ var _DOMReady = false;
13
+ window.addEventListener('load', loaded, false);
14
+ function loaded () {
15
+ window.removeEventListener('load', loaded, false);
16
+ _DOMReady = true;
17
+ }
18
+
19
+ // regex used to detect if app has been added to the homescreen
20
+ var _reSmartURL = /\/ath(\/)?$/;
21
+ var _reQueryString = /([\?&]ath=[^&]*$|&ath=[^&]*(&))/;
22
+
23
+ // singleton
24
+ var _instance;
25
+ function ath (options) {
26
+ _instance = _instance || new ath.Class(options);
27
+
28
+ return _instance;
29
+ }
30
+
31
+ // message in all supported languages
32
+ ath.intl = {
33
+ en_us: {
34
+ message: 'To add this web app to the home screen: tap %icon and then <strong>%action</strong>.',
35
+ action: { ios: 'Add to Home Screen', android: 'Add to homescreen', windows: 'pin to start' }
36
+ },
37
+
38
+ it_it: {
39
+ message: 'Per Aggiungere questa web app alla schermata iniziale: premi %icon e poi <strong>%action</strong>.',
40
+ action: { ios: 'Aggiungi a Home', android: 'Aggiungi alla homescreen', windows: 'pin to start' }
41
+ }
42
+ };
43
+
44
+ // default options
45
+ ath.defaults = {
46
+ appID: 'org.cubiq.addtohome', // local storage name (no need to change)
47
+ fontSize: 15, // base font size, used to properly resize the popup based on viewport scale factor
48
+ debug: false, // override browser checks
49
+ modal: false, // prevent further actions until the message is closed
50
+ mandatory: false, // you can't proceed if you don't add the app to the homescreen
51
+ autostart: true, // show the message automatically
52
+ skipFirstVisit: false, // show only to returning visitors (ie: skip the first time you visit)
53
+ startDelay: 1, // display the message after that many seconds from page load
54
+ lifespan: 15, // life of the message in seconds
55
+ displayPace: 1440, // minutes before the message is shown again (0: display every time, default 24 hours)
56
+ maxDisplayCount: 0, // absolute maximum number of times the message will be shown to the user (0: no limit)
57
+ icon: true, // add touch icon to the message
58
+ message: '', // the message can be customized
59
+ validLocation: [], // list of pages where the message will be shown (array of regexes)
60
+ onInit: null, // executed on instance creation
61
+ onShow: null, // executed when the message is shown
62
+ onRemove: null, // executed when the message is removed
63
+ onAdd: null, // when the application is launched the first time from the homescreen (guesstimate)
64
+ onPrivate: null, // executed if user is in private mode
65
+ detectHomescreen: false // try to detect if the site has been added to the homescreen (false | true | 'hash' | 'queryString' | 'smartURL')
66
+ };
67
+
68
+ // browser info and capability
69
+ var _ua = window.navigator.userAgent;
70
+ var _nav = window.navigator;
71
+ _extend(ath, {
72
+ hasToken: document.location.hash == '#ath' || _reSmartURL.test(document.location.href) || _reQueryString.test(document.location.search),
73
+ isRetina: window.devicePixelRatio && window.devicePixelRatio > 1,
74
+ isIDevice: (/iphone|ipod|ipad/i).test(_ua),
75
+ isMobileChrome: _ua.indexOf('Android') > -1 && (/Chrome\/[.0-9]*/).test(_ua),
76
+ isMobileIE: _ua.indexOf('Windows Phone') > -1,
77
+ language: _nav.language && _nav.language.toLowerCase().replace('-', '_')
78
+ });
79
+
80
+ ath.isMobileSafari = ath.isIDevice && _ua.indexOf('Safari') > -1 && _ua.indexOf('CriOS') < 0;
81
+ ath.OS = ath.isIDevice ? 'ios' : ath.isMobileChrome ? 'android' : ath.isMobileIE ? 'windows' : 'unsupported';
82
+
83
+ ath.OSVersion = _ua.match(/(OS|Android) (\d+[_\.]\d+)/);
84
+ ath.OSVersion = ath.OSVersion && ath.OSVersion[2] ? +ath.OSVersion[2].replace('_', '.') : 0;
85
+
86
+ ath.isStandalone = window.navigator.standalone || ( ath.isMobileChrome && ( screen.height - document.documentElement.clientHeight < 40 ) ); // TODO: check the lame polyfill
87
+ ath.isTablet = (ath.isMobileSafari && _ua.indexOf('iPad') > -1) || (ath.isMobileChrome && _ua.indexOf('Mobile') < 0);
88
+
89
+ ath.isCompatible = (ath.isIDevice && ath.OSVersion >= 6) || ath.isMobileChrome; // TODO: add winphone
90
+
91
+ // falls back to en_us if language is unsupported
92
+ ath.language = ath.language && ath.language in ath.intl ? ath.language : 'en_us';
93
+
94
+ var _defaultSession = {
95
+ lastDisplayTime: 0, // last time we displayed the message
96
+ returningVisitor: false, // is this the first time you visit
97
+ displayCount: 0, // number of times the message has been shown
98
+ optedout: false, // has the user opted out
99
+ added: false // has been actually added to the homescreen
100
+ };
101
+
102
+ ath.removeSession = function (appID) {
103
+ try {
104
+ localStorage.removeItem(appID || ath.defaults.appID);
105
+ } catch (e) {
106
+ // we are most likely in private mode
107
+ }
108
+ };
109
+
110
+ ath.Class = function (options) {
111
+ // merge default options with user config
112
+ this.options = _extend({}, ath.defaults);
113
+ _extend(this.options, options);
114
+
115
+ // normalize some options
116
+ this.options.mandatory = this.options.mandatory && ( 'standalone' in window.navigator || this.options.debug );
117
+ this.options.modal = this.options.modal || this.options.mandatory;
118
+ if ( this.options.mandatory ) {
119
+ this.options.startDelay = -0.5; // make the popup hasty
120
+ }
121
+ this.options.detectHomescreen = this.options.detectHomescreen === true ? 'hash' : this.options.detectHomescreen;
122
+
123
+ // setup the debug environment
124
+ if ( this.options.debug ) {
125
+ ath.isCompatible = true;
126
+ ath.OS = typeof this.options.debug == 'string' ? this.options.debug : ath.OS == 'unsupported' ? 'android' : ath.OS;
127
+ ath.OSVersion = ath.OS == 'ios' ? '7' : '4';
128
+ }
129
+
130
+ // the element the message will be appended to
131
+ this.container = document.documentElement;
132
+
133
+ // load session
134
+ this.session = JSON.parse(localStorage.getItem(this.options.appID));
135
+
136
+ // user most likely came from a direct link containing our token, we don't need it and we remove it
137
+ if ( ath.hasToken && ( !ath.isCompatible || !this.session ) ) {
138
+ ath.hasToken = false;
139
+ _removeToken();
140
+ }
141
+
142
+ // the device is not supported
143
+ if ( !ath.isCompatible ) {
144
+ return;
145
+ }
146
+
147
+ this.session = this.session || _defaultSession;
148
+
149
+ // check if we can use the local storage
150
+ try {
151
+ localStorage.setItem(this.options.appID, JSON.stringify(this.session));
152
+ ath.hasLocalStorage = true;
153
+ } catch (e) {
154
+ // we are most likely in private mode
155
+ ath.hasLocalStorage = false;
156
+
157
+ if ( this.options.onPrivate ) {
158
+ this.options.onPrivate.call(this);
159
+ }
160
+ }
161
+
162
+ // check if this is a valid location
163
+ var isValidLocation = !this.options.validLocation.length;
164
+ for ( var i = this.options.validLocation.length; i--; ) {
165
+ if ( this.options.validLocation[i].test(document.location.href) ) {
166
+ isValidLocation = true;
167
+ break;
168
+ }
169
+ }
170
+
171
+ // check compatibility with old versions of add to homescreen. Opt-out if an old session is found
172
+ if ( localStorage.getItem('addToHome') ) {
173
+ this.optOut();
174
+ }
175
+
176
+ // critical errors:
177
+ // user opted out, already added to the homescreen, not a valid location
178
+ if ( this.session.optedout || this.session.added || !isValidLocation ) {
179
+ return;
180
+ }
181
+
182
+ // check if the app is in stand alone mode
183
+ if ( ath.isStandalone ) {
184
+ // execute the onAdd event if we haven't already
185
+ if ( !this.session.added ) {
186
+ this.session.added = true;
187
+ this.updateSession();
188
+
189
+ if ( this.options.onAdd && ath.hasLocalStorage ) { // double check on localstorage to avoid multiple calls to the custom event
190
+ this.options.onAdd.call(this);
191
+ }
192
+ }
193
+
194
+ return;
195
+ }
196
+
197
+ // (try to) check if the page has been added to the homescreen
198
+ if ( this.options.detectHomescreen ) {
199
+ // the URL has the token, we are likely coming from the homescreen
200
+ if ( ath.hasToken ) {
201
+ _removeToken(); // we don't actually need the token anymore, we remove it to prevent redistribution
202
+
203
+ // this is called the first time the user opens the app from the homescreen
204
+ if ( !this.session.added ) {
205
+ this.session.added = true;
206
+ this.updateSession();
207
+
208
+ if ( this.options.onAdd && ath.hasLocalStorage ) { // double check on localstorage to avoid multiple calls to the custom event
209
+ this.options.onAdd.call(this);
210
+ }
211
+ }
212
+
213
+ return;
214
+ }
215
+
216
+ // URL doesn't have the token, so add it
217
+ if ( this.options.detectHomescreen == 'hash' ) {
218
+ history.replaceState('', window.document.title, document.location.href + '#ath');
219
+ } else if ( this.options.detectHomescreen == 'smartURL' ) {
220
+ history.replaceState('', window.document.title, document.location.href.replace(/(\/)?$/, '/ath$1'));
221
+ } else {
222
+ history.replaceState('', window.document.title, document.location.href + (document.location.search ? '&' : '?' ) + 'ath=');
223
+ }
224
+ }
225
+
226
+ // check if this is a returning visitor
227
+ if ( !this.session.returningVisitor ) {
228
+ this.session.returningVisitor = true;
229
+ this.updateSession();
230
+
231
+ // we do not show the message if this is your first visit
232
+ if ( this.options.skipFirstVisit ) {
233
+ return;
234
+ }
235
+ }
236
+
237
+ // we do no show the message in private mode
238
+ if ( !ath.hasLocalStorage ) {
239
+ return;
240
+ }
241
+
242
+ // all checks passed, ready to display
243
+ this.ready = true;
244
+
245
+ if ( this.options.onInit ) {
246
+ this.options.onInit.call(this);
247
+ }
248
+
249
+ if ( this.options.autostart ) {
250
+ this.show();
251
+ }
252
+ };
253
+
254
+ ath.Class.prototype = {
255
+ // event type to method conversion
256
+ events: {
257
+ load: '_delayedShow',
258
+ error: '_delayedShow',
259
+ orientationchange: 'resize',
260
+ resize: 'resize',
261
+ scroll: 'resize',
262
+ click: 'remove',
263
+ touchmove: '_preventDefault',
264
+ transitionend: '_removeElements',
265
+ webkitTransitionEnd: '_removeElements',
266
+ MSTransitionEnd: '_removeElements'
267
+ },
268
+
269
+ handleEvent: function (e) {
270
+ var type = this.events[e.type];
271
+ if ( type ) {
272
+ this[type](e);
273
+ }
274
+ },
275
+
276
+ show: function (force) {
277
+ // in autostart mode wait for the document to be ready
278
+ if ( this.options.autostart && !_DOMReady ) {
279
+ setTimeout(this.show.bind(this), 50);
280
+ return;
281
+ }
282
+
283
+ // message already on screen
284
+ if ( this.shown ) {
285
+ return;
286
+ }
287
+
288
+ var now = Date.now();
289
+ var lastDisplayTime = this.session.lastDisplayTime;
290
+
291
+ if ( force !== true ) {
292
+ // this is needed if autostart is disabled and you programmatically call the show() method
293
+ if ( !this.ready ) {
294
+ return;
295
+ }
296
+
297
+ // we obey the display pace (prevent the message to popup too often)
298
+ if ( now - lastDisplayTime < this.options.displayPace * 60000 ) {
299
+ return;
300
+ }
301
+
302
+ // obey the maximum number of display count
303
+ if ( this.options.maxDisplayCount && this.session.displayCount >= this.options.maxDisplayCount ) {
304
+ return;
305
+ }
306
+ }
307
+
308
+ this.shown = true;
309
+
310
+ // increment the display count
311
+ this.session.lastDisplayTime = now;
312
+ this.session.displayCount++;
313
+ this.updateSession();
314
+
315
+ // try to get the highest resolution application icon
316
+ if ( !this.applicationIcon ) {
317
+ if ( ath.OS == 'ios' ) {
318
+ 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]');
319
+ } else {
320
+ this.applicationIcon = document.querySelector('head link[rel^="shortcut icon"][sizes="196x196"],head link[rel^=apple-touch-icon]');
321
+ }
322
+ }
323
+
324
+ var message = '';
325
+
326
+ if ( this.options.message in ath.intl ) { // you can force the locale
327
+ message = ath.intl[this.options.message].message.replace('%action', ath.intl[this.options.message].action[ath.OS]);
328
+ } else if ( this.options.message !== '' ) { // or use a custom message
329
+ message = this.options.message;
330
+ } else { // otherwise we use our message
331
+ message = ath.intl[ath.language].message.replace('%action', ath.intl[ath.language].action[ath.OS]);
332
+ }
333
+
334
+ // add the action icon
335
+ message = '<p>' + message.replace('%icon', '<span class="ath-action-icon">icon</span>') + '</p>';
336
+
337
+ // create the message container
338
+ this.viewport = document.createElement('div');
339
+ this.viewport.className = 'ath-viewport';
340
+ if ( this.options.modal ) {
341
+ this.viewport.className += ' ath-modal';
342
+ }
343
+ if ( this.options.mandatory ) {
344
+ this.viewport.className += ' ath-mandatory';
345
+ }
346
+ this.viewport.style.position = 'absolute';
347
+
348
+ // create the actual message element
349
+ this.element = document.createElement('div');
350
+ this.element.className = 'ath-container ath-' + ath.OS + ' ath-' + ath.OS + (ath.OSVersion + '').substr(0,1) + ' ath-' + (ath.isTablet ? 'tablet' : 'phone');
351
+ this.element.style.cssText = '-webkit-transition-property:-webkit-transform,opacity;-webkit-transition-duration:0;-webkit-transform:translate3d(0,0,0);transition-property:transform,opacity;transition-duration:0;transform:translate3d(0,0,0);-webkit-transition-timing-function:ease-out';
352
+ this.element.style.webkitTransform = 'translate3d(0,-' + window.innerHeight + 'px,0)';
353
+ this.element.style.webkitTransitionDuration = '0s';
354
+
355
+ // add the application icon
356
+ if ( this.options.icon && this.applicationIcon ) {
357
+ this.element.className += ' ath-icon';
358
+ this.img = document.createElement('img');
359
+ this.img.className = 'ath-application-icon';
360
+ this.img.addEventListener('load', this, false);
361
+ this.img.addEventListener('error', this, false);
362
+
363
+ this.img.src = this.applicationIcon.href;
364
+ this.element.appendChild(this.img);
365
+ }
366
+
367
+ this.element.innerHTML += message;
368
+
369
+ // we are not ready to show, place the message out of sight
370
+ this.viewport.style.left = '-99999em';
371
+
372
+ // attach all elements to the DOM
373
+ this.viewport.appendChild(this.element);
374
+ this.container.appendChild(this.viewport);
375
+
376
+ // if we don't have to wait for an image to load, show the message right away
377
+ if ( !this.img ) {
378
+ this._delayedShow();
379
+ }
380
+ },
381
+
382
+ _delayedShow: function (e) {
383
+ setTimeout(this._show.bind(this), this.options.startDelay * 1000 + 500);
384
+ },
385
+
386
+ _show: function () {
387
+ var that = this;
388
+
389
+ // update the viewport size and orientation
390
+ this.updateViewport();
391
+
392
+ // reposition/resize the message on orientation change
393
+ window.addEventListener('resize', this, false);
394
+ window.addEventListener('scroll', this, false);
395
+ window.addEventListener('orientationchange', this, false);
396
+
397
+ if ( this.options.modal ) {
398
+ // lock any other interaction
399
+ document.addEventListener('touchmove', this, true);
400
+ }
401
+
402
+ // Enable closing after 1 second
403
+ if ( !this.options.mandatory ) {
404
+ setTimeout(function () {
405
+ that.element.addEventListener('click', that, true);
406
+ }, 1000);
407
+ }
408
+
409
+ // kick the animation
410
+ setTimeout(function () {
411
+ that.element.style.webkitTransform = 'translate3d(0,0,0)';
412
+ that.element.style.webkitTransitionDuration = '1.2s';
413
+ }, 0);
414
+
415
+ // set the destroy timer
416
+ if ( this.options.lifespan ) {
417
+ this.removeTimer = setTimeout(this.remove.bind(this), this.options.lifespan * 1000);
418
+ }
419
+
420
+ // fire the custom onShow event
421
+ if ( this.options.onShow ) {
422
+ this.options.onShow.call(this);
423
+ }
424
+ },
425
+
426
+ remove: function () {
427
+ clearTimeout(this.removeTimer);
428
+
429
+ // clear up the event listeners
430
+ if ( this.img ) {
431
+ this.img.removeEventListener('load', this, false);
432
+ this.img.removeEventListener('error', this, false);
433
+ }
434
+
435
+ window.removeEventListener('resize', this, false);
436
+ window.removeEventListener('scroll', this, false);
437
+ window.removeEventListener('orientationchange', this, false);
438
+ document.removeEventListener('touchmove', this, true);
439
+ this.element.removeEventListener('click', this, true);
440
+
441
+ // remove the message element on transition end
442
+ this.element.addEventListener('transitionend', this, false);
443
+ this.element.addEventListener('webkitTransitionEnd', this, false);
444
+ this.element.addEventListener('MSTransitionEnd', this, false);
445
+
446
+ // start the fade out animation
447
+ this.element.style.webkitTransitionDuration = '0.3s';
448
+ this.element.style.opacity = '0';
449
+ },
450
+
451
+ _removeElements: function () {
452
+ this.element.removeEventListener('transitionend', this, false);
453
+ this.element.removeEventListener('webkitTransitionEnd', this, false);
454
+ this.element.removeEventListener('MSTransitionEnd', this, false);
455
+
456
+ // remove the message from the DOM
457
+ this.container.removeChild(this.viewport);
458
+
459
+ this.shown = false;
460
+
461
+ // fire the custom onRemove event
462
+ if ( this.options.onRemove ) {
463
+ this.options.onRemove.call(this);
464
+ }
465
+ },
466
+
467
+ updateViewport: function () {
468
+ if ( !this.shown ) {
469
+ return;
470
+ }
471
+
472
+ this.viewport.style.width = window.innerWidth + 'px';
473
+ this.viewport.style.height = window.innerHeight + 'px';
474
+ this.viewport.style.left = window.scrollX + 'px';
475
+ this.viewport.style.top = window.scrollY + 'px';
476
+
477
+ var clientWidth = document.documentElement.clientWidth;
478
+
479
+ this.orientation = clientWidth > document.documentElement.clientHeight ? 'landscape' : 'portrait';
480
+
481
+ var screenWidth = ath.OS == 'ios' ? this.orientation == 'portrait' ? screen.width : screen.height : screen.width;
482
+ this.scale = screen.width > clientWidth ? 1 : screenWidth / window.innerWidth;
483
+
484
+ this.element.style.fontSize = this.options.fontSize / this.scale + 'px';
485
+ },
486
+
487
+ resize: function () {
488
+ clearTimeout(this.resizeTimer);
489
+ this.resizeTimer = setTimeout(this.updateViewport.bind(this), 100);
490
+ },
491
+
492
+ updateSession: function () {
493
+ if ( ath.hasLocalStorage === false ) {
494
+ return;
495
+ }
496
+
497
+ localStorage.setItem(this.options.appID, JSON.stringify(this.session));
498
+ },
499
+
500
+ clearSession: function () {
501
+ this.session = _defaultSession;
502
+ this.updateSession();
503
+ },
504
+
505
+ optOut: function () {
506
+ this.session.optedout = true;
507
+ this.updateSession();
508
+ },
509
+
510
+ optIn: function () {
511
+ this.session.optedout = false;
512
+ this.updateSession();
513
+ },
514
+
515
+ clearDisplayCount: function () {
516
+ this.session.displayCount = 0;
517
+ this.updateSession();
518
+ },
519
+
520
+ _preventDefault: function (e) {
521
+ e.preventDefault();
522
+ e.stopPropagation();
523
+ }
524
+ };
525
+
526
+ // utility
527
+ function _extend (target, obj) {
528
+ for ( var i in obj ) {
529
+ target[i] = obj[i];
530
+ }
531
+
532
+ return target;
533
+ }
534
+
535
+ function _removeToken () {
536
+ if ( document.location.hash == '#ath' ) {
537
+ history.replaceState('', window.document.title, document.location.href.split('#')[0]);
538
+ }
539
+
540
+ if ( _reSmartURL.test(document.location.href) ) {
541
+ history.replaceState('', window.document.title, document.location.href.replace(_reSmartURL, '$1'));
542
+ }
543
+
544
+ if ( _reQueryString.test(document.location.search) ) {
545
+ history.replaceState('', window.document.title, document.location.href.replace(_reQueryString, '$2'));
546
+ }
547
+ }
548
+
549
+ // expose to the world
550
+ window.addToHomescreen = ath;
551
+
552
+ })(window, document);