@glitchr/transparent 1.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.
package/src/index.js ADDED
@@ -0,0 +1,1645 @@
1
+ (function(namespace) {
2
+
3
+ namespace.replaceHash = function(newHash, triggerHashChange = true, skipIfEmptyIdentifier = true) {
4
+
5
+ if(!newHash) newHash = "";
6
+ if (newHash !== "" && (''+newHash).charAt(0) !== '#')
7
+ newHash = '#' + newHash;
8
+
9
+ var oldURL = location.origin+location.pathname+location.hash;
10
+ var newURL = location.origin+location.pathname+newHash;
11
+
12
+ var fallback = $(newHash).length === 0;
13
+
14
+ var hashElement = $(newHash)[0] ?? undefined;
15
+ if (hashElement !== undefined) // Update hash only if element is displayed
16
+ fallback |= window.getComputedStyle(hashElement)["display"] == "none";
17
+
18
+ if((skipIfEmptyIdentifier && !newHash) || fallback){
19
+
20
+ dispatchEvent(new HashChangeEvent("hashfallback", {oldURL:oldURL, newURL:newURL}));
21
+ newHash = "";
22
+
23
+ oldURL = location.origin+location.pathname+location.hash;
24
+ newURL = location.origin+location.pathname+newHash;
25
+ }
26
+
27
+ if(oldURL == newURL) return false;
28
+
29
+ var state = Object.assign({}, history.state, {href: newURL});
30
+ history.replaceState(state, '', newURL);
31
+
32
+ if(triggerHashChange)
33
+ dispatchEvent(new HashChangeEvent("hashchange", {oldURL:oldURL, newURL:newURL}));
34
+
35
+ return true;
36
+ }
37
+
38
+ })(window);
39
+
40
+ $.fn.serializeObject = function() {
41
+ var o = {};
42
+ var a = this.serializeArray();
43
+ $.each(a, function() {
44
+ if (o[this.name]) {
45
+ if (!o[this.name].push) {
46
+ o[this.name] = [o[this.name]];
47
+ }
48
+ o[this.name].push(this.value || '');
49
+ } else {
50
+ o[this.name] = this.value || '';
51
+ }
52
+ });
53
+ return o;
54
+ };
55
+
56
+ $.fn.isScrollable = function()
57
+ {
58
+ for (let el of $(this).isScrollableX())
59
+ if(el) return true;
60
+
61
+ for (let el of $(this).isScrollableY())
62
+ if(el) return true;
63
+
64
+ return false;
65
+ }
66
+
67
+ $.fn.isScrollableX = function() {
68
+
69
+ return $(this).map(function(i) {
70
+
71
+ var el = this[i] === window ? document.documentElement : this[i];
72
+ var isDom = el == document.documentElement;
73
+
74
+ var hasScrollableContent = el.scrollWidth > el.clientWidth;
75
+
76
+ var overflowStyle = window.getComputedStyle(el).overflowX;
77
+ var isOverflowScroll = overflowStyle.indexOf('scroll') !== -1;
78
+
79
+ return hasScrollableContent && (isOverflowScroll || isDom);
80
+
81
+ }.bind(this));
82
+ }
83
+
84
+ $.fn.isScrollableY = function() {
85
+
86
+ return $(this).map(function(i) {
87
+
88
+ var el = this[i] === window ? document.documentElement : this[i];
89
+ var isDom = el == document.documentElement;
90
+
91
+ var hasScrollableContent = el.scrollHeight > el.clientHeight;
92
+
93
+ var overflowStyle = window.getComputedStyle(el).overflowY;
94
+ var isOverflowScroll = overflowStyle.indexOf('scroll') !== -1;
95
+
96
+ return hasScrollableContent && (isOverflowScroll || isDom);
97
+
98
+ }.bind(this));
99
+ }
100
+
101
+ $.fn.closestScrollable = function()
102
+ {
103
+ return $(this).map((i) => {
104
+
105
+ var target = this[i] === window ? document.documentElement : this[i];
106
+ if (target === undefined) target = document.documentElement;
107
+
108
+ while (target !== document.documentElement) {
109
+
110
+ if($(target).isScrollable()) return target;
111
+
112
+ if(target.parentElement === undefined) return undefined;
113
+ if(target.parentElement === null) return null;
114
+
115
+ target = target.parentElement;
116
+ }
117
+
118
+ return $(target).isScrollable() ? target : undefined;
119
+ });
120
+ }
121
+
122
+ $.fn.repaint = function(duration = 1000, reiteration=5) {
123
+
124
+ var time = 0;
125
+ var interval = undefined;
126
+ var fn = function () {
127
+
128
+ $(this).each(function (_, el) {
129
+
130
+ var displayBak = el.style.display;
131
+
132
+ el.style.display = "none";
133
+ el.style.display = displayBak;
134
+ el.offsetHeight;
135
+ });
136
+
137
+ if (time > duration) clearInterval(interval);
138
+ time += duration/reiteration;
139
+
140
+ }.bind(this);
141
+
142
+ fn();
143
+ if(reiteration > 0)
144
+ interval = setInterval(fn, duration/reiteration);
145
+ };
146
+
147
+ ;(function (root, factory) {
148
+
149
+ if (typeof define === 'function' && define.amd) {
150
+ define(factory);
151
+ } else if (typeof exports === 'object') {
152
+ module.exports = factory();
153
+ } else {
154
+ root.Transparent = factory();
155
+ }
156
+
157
+ })(this, function () {
158
+
159
+ var Transparent = {};
160
+ Transparent.version = '0.1.0';
161
+
162
+ var Settings = Transparent.settings = {
163
+ "headers": {},
164
+ "data": {},
165
+ "disable": false,
166
+ "global_code": true,
167
+ "debug": true,
168
+ "lazyload": true,
169
+ "response_text": {},
170
+ "response_limit": 25,
171
+ "throttle": 1000,
172
+ "identifier": "#page",
173
+ "loader": "#loader",
174
+ "smoothscroll_duration": "200ms",
175
+ "smoothscroll_speed" : 0,
176
+ "smoothscroll_easing" : "swing",
177
+ "exceptions": []
178
+ };
179
+
180
+ const State = Transparent.state = {
181
+
182
+ ROOT : "transparent",
183
+
184
+ SWITCH : "X-to-Y",
185
+ SAME : "same",
186
+ READY : "ready",
187
+ RELOAD : "reload",
188
+ DISABLE : "disable",
189
+ LOADING : "loading",
190
+ NEW : "new",
191
+ FIRST : "first",
192
+ SUBMIT : "submit",
193
+ POPSTATE : "popstate",
194
+ HASHCHANGE : "hashchange",
195
+ CLICK : "click",
196
+
197
+ PREACTIVE : "pre-active",
198
+ ACTIVEIN : "active-in",
199
+ ACTIVE : "active",
200
+ ACTIVEOUT : "active-out",
201
+ POSTACTIVE : "post-active",
202
+
203
+ NOTIFICATION: "notification"
204
+ };
205
+
206
+ var isReady = false;
207
+ var rescueMode = false;
208
+
209
+ Transparent.html = $($(document).find("html")[0]);
210
+ Transparent.html.addClass(Transparent.state.ROOT+ " " + Transparent.state.LOADING + " " + Transparent.state.FIRST);
211
+
212
+ if(!Transparent.html.hasClass(Transparent.state.ACTIVE)) {
213
+ Transparent.html.addClass(Transparent.state.ACTIVE);
214
+ dispatchEvent(new Event('transparent:'+Transparent.state.ACTIVE));
215
+ }
216
+
217
+ window.addEventListener("DOMContentLoaded", function()
218
+ {
219
+ Transparent.loader = $($(document).find(Settings.loader)[0] ?? Transparent.html);
220
+ Transparent.lazyLoad();
221
+ });
222
+
223
+ Transparent.isRescueMode = function() { return rescueMode; }
224
+ Transparent.getData = function(uuid)
225
+ {
226
+ return (Settings["data"][uuid] ? Settings["data"][uuid] : {});
227
+ }
228
+
229
+ Transparent.setData = function(uuid, data)
230
+ {
231
+ Settings["data"][uuid] = data;
232
+ return this;
233
+ }
234
+
235
+ Transparent.getResponseText = function(uuid)
236
+ {
237
+ var array = JSON.parse(sessionStorage.getItem('transparent')) || [];
238
+
239
+ // Bubble up the most recent uuid
240
+ var index = array.indexOf(uuid);
241
+ if (index > -1) {
242
+ array.splice(index, 1);
243
+ array.push(uuid);
244
+ }
245
+
246
+ // If no response refresh page based on the requested url
247
+ return sessionStorage.getItem('transparent[response]['+uuid+']') || null;
248
+ }
249
+
250
+ Transparent.getResponsePosition = function(uuid)
251
+ {
252
+ var array = JSON.parse(sessionStorage.getItem('transparent')) || [];
253
+
254
+ // Bubble up the most recent uuid
255
+ var index = array.indexOf(uuid);
256
+ if (index > -1) {
257
+ array.splice(index, 1);
258
+ array.push(uuid);
259
+ }
260
+
261
+ // If no response refresh page based on the requested url
262
+ var position = sessionStorage.getItem('transparent[position]['+uuid+']');
263
+ return position != "undefined" ? (JSON.parse(position) || []) : [];
264
+ }
265
+
266
+ Transparent.getResponse = function(uuid)
267
+ {
268
+ return [ Transparent.getResponseText(uuid), Transparent.getResponsePosition(uuid) ];
269
+ }
270
+
271
+ function isDomEntity(entity)
272
+ {
273
+ return typeof entity === 'object' && entity.nodeType !== undefined;
274
+ }
275
+
276
+ Transparent.setResponse = function(uuid, responseText, scrollableXY = [], exceptionRaised = false)
277
+ {
278
+ if(isDomEntity(responseText))
279
+ responseText = responseText.outerHTML;
280
+
281
+ var array = JSON.parse(sessionStorage.getItem('transparent')) || [];
282
+ array.push(uuid);
283
+
284
+ while(array.length > Settings["response_limit"])
285
+ sessionStorage.removeItem('transparent['+array.shift()+']');
286
+
287
+ try {
288
+
289
+ if(isLocalStorageNameSupported()) {
290
+
291
+ sessionStorage.setItem('transparent', JSON.stringify(array));
292
+ sessionStorage.setItem('transparent[response]['+uuid+']', responseText);
293
+ sessionStorage.setItem('transparent[position]['+uuid+']', JSON.stringify(scrollableXY));
294
+ }
295
+
296
+ } catch(e) {
297
+
298
+ if (e.name === 'QuotaExceededError')
299
+ sessionStorage.clear();
300
+
301
+ return exceptionRaised === false ? Transparent.setResponse(uuid, responseText, scrollableXY, true) : this;
302
+ }
303
+
304
+ return this;
305
+ }
306
+
307
+ Transparent.setResponse = function(uuid, responseText, scrollableXY, exceptionRaised = false)
308
+ {
309
+ if(isDomEntity(responseText))
310
+ responseText = responseText.outerHTML;
311
+
312
+ var array = JSON.parse(sessionStorage.getItem('transparent')) || [];
313
+ if( ! (uuid in array) ) {
314
+
315
+ array.push(uuid);
316
+ while(array.length > Settings["response_limit"])
317
+ sessionStorage.removeItem('transparent['+array.shift()+']');
318
+ }
319
+
320
+ try {
321
+
322
+ if(isLocalStorageNameSupported()) {
323
+
324
+ sessionStorage.setItem('transparent', JSON.stringify(array));
325
+ sessionStorage.setItem('transparent[response]['+uuid+']', responseText);
326
+ sessionStorage.setItem('transparent[position]['+uuid+']', JSON.stringify(scrollableXY));
327
+ }
328
+
329
+ } catch(e) {
330
+
331
+ if (e.name === 'QuotaExceededError')
332
+ sessionStorage.clear();
333
+
334
+ return exceptionRaised === false ? Transparent.setResponse(uuid, responseText, scrollableXY, true) : this;
335
+ }
336
+
337
+ return this;
338
+ }
339
+
340
+ Transparent.setResponseText = function(uuid, responseText, exceptionRaised = false)
341
+ {
342
+ if(isDomEntity(responseText))
343
+ responseText = responseText.outerHTML;
344
+
345
+ var array = JSON.parse(sessionStorage.getItem('transparent')) || [];
346
+ if( ! (uuid in array) ) {
347
+
348
+ array.push(uuid);
349
+ while(array.length > Settings["response_limit"])
350
+ sessionStorage.removeItem('transparent['+array.shift()+']');
351
+ }
352
+
353
+ try {
354
+
355
+ if(isLocalStorageNameSupported()) {
356
+
357
+ sessionStorage.setItem('transparent', JSON.stringify(array));
358
+ sessionStorage.setItem('transparent[response]['+uuid+']', responseText);
359
+ }
360
+
361
+ } catch(e) {
362
+
363
+ if (e.name === 'QuotaExceededError')
364
+ sessionStorage.clear();
365
+
366
+ return exceptionRaised === false ? Transparent.setResponseText(uuid, responseText, true) : this;
367
+ }
368
+
369
+ return this;
370
+ }
371
+
372
+ Transparent.setResponsePosition = function(uuid, scrollableXY, exceptionRaised = false)
373
+ {
374
+ var array = JSON.parse(sessionStorage.getItem('transparent')) || [];
375
+ if( ! (uuid in array) ) {
376
+
377
+ array.push(uuid);
378
+ while(array.length > Settings["response_limit"])
379
+ sessionStorage.removeItem('transparent['+array.shift()+']');
380
+ }
381
+
382
+ try {
383
+
384
+ if(isLocalStorageNameSupported()) {
385
+
386
+ sessionStorage.setItem('transparent', JSON.stringify(array));
387
+ sessionStorage.setItem('transparent[position]['+uuid+']', JSON.stringify(scrollableXY));
388
+ }
389
+
390
+ } catch(e) {
391
+
392
+ if (e.name === 'QuotaExceededError')
393
+ sessionStorage.clear();
394
+
395
+ return exceptionRaised === false ? Transparent.setResponsePosition(uuid, scrollableXY, true) : this;
396
+ }
397
+
398
+ return this;
399
+ }
400
+
401
+ Transparent.hasResponse = function(uuid)
402
+ {
403
+ if(isLocalStorageNameSupported())
404
+ return 'transparent[response]['+uuid+']' in sessionStorage;
405
+
406
+ return false;
407
+ }
408
+
409
+ Transparent.configure = function (options) {
410
+
411
+ var key, value;
412
+ for (key in options) {
413
+ value = options[key];
414
+ if (value !== undefined && options.hasOwnProperty(key)) Settings[key] = value;
415
+ }
416
+
417
+ return this;
418
+ };
419
+
420
+ Transparent.ready = function (options = {}) {
421
+
422
+ Transparent.configure({'x-ajax-request': true});
423
+ Transparent.configure(options);
424
+
425
+ isReady = true;
426
+
427
+ dispatchEvent(new Event('transparent:'+Transparent.state.READY));
428
+ Transparent.html.addClass(Transparent.state.READY);
429
+
430
+ Transparent.addLayout();
431
+ Transparent.lazyLoad();
432
+
433
+ Transparent.scrollToHash(location.hash, {}, function() {
434
+
435
+ Transparent.activeOut(() => Transparent.html.removeClass(Transparent.state.FIRST));
436
+ });
437
+
438
+ return this;
439
+ };
440
+
441
+ Transparent.addLayout = function() {
442
+
443
+ var id = Transparent.getLayout();
444
+ if(id === undefined) return false;
445
+
446
+ var isKnown = knownLayout.indexOf(id) !== -1;
447
+ if(!isKnown) knownLayout.push(id);
448
+
449
+ return !isKnown;
450
+ }
451
+
452
+ Transparent.getLayout = function(dom = null) {
453
+
454
+ var layout = dom !== null ? $(dom).find(Settings.identifier) : $(Settings.identifier);
455
+ if(!layout.length) return undefined;
456
+
457
+ return layout.data("layout");
458
+ }
459
+
460
+ Transparent.findNearestForm = function (el) {
461
+
462
+ switch (el.tagName) {
463
+ case "FORM":
464
+ var form = $(el);
465
+ return (form.length ? form[0] : undefined);
466
+ case "INPUT":
467
+ case "BUTTON":
468
+ var form = $(el).closest("form");
469
+ return (form.length ? form[0] : undefined);
470
+ }
471
+
472
+ // Try to detect target element
473
+ if (el.target) {
474
+
475
+ if (el.target.tagName == "FORM")
476
+ return Transparent.findNearestForm(el.target);
477
+
478
+ if (el.target.tagName == "BUTTON" && el.target.getAttribute("type") == "submit")
479
+ return Transparent.findNearestForm(el.target);
480
+
481
+ if (el.target.tagName == "INPUT" && el.target.getAttribute("type") == "submit")
482
+ return Transparent.findNearestForm(el.target);
483
+
484
+ var form = $(el.target).closest("form");
485
+ return (form.length ? form[0] : undefined);
486
+ }
487
+
488
+ return null;
489
+ }
490
+
491
+ window.previousLocation = window.location.toString();
492
+ Transparent.findLink = function (el) {
493
+
494
+ if (el.type == Transparent.state.HASHCHANGE) {
495
+
496
+ var href = el.newURL;
497
+ if(!href) return null;
498
+
499
+ if (href.startsWith("#")) href = location.pathname + href;
500
+ if (href.endsWith ("#")) href = href.slice(0, -1);
501
+
502
+ var data = history.state ? Transparent.getData(history.state.uuid) : {};
503
+ return ["GET", new URL(el.newURL), data];
504
+
505
+ } else if (el.type == Transparent.state.POPSTATE) {
506
+
507
+ if(!el.state) return;
508
+
509
+ var href = el.state.href;
510
+ if (href.startsWith("#")) href = location.pathname + href;
511
+ if (href.endsWith ("#")) href = href.slice(0, -1);
512
+
513
+ var type = el.state.type;
514
+ var data = Transparent.getData(el.state.uuid);
515
+
516
+ var https = /^https?:\/\//i;
517
+ if (https.test(href)) return [type, new URL(href), data];
518
+
519
+ var hash = /^\#\w*/i;
520
+ if (hash.test(href)) return [type, new URL(location.origin+location.pathname+href), data];
521
+
522
+ return [type, new URL(href, location.origin), data];
523
+
524
+ } else if(el.type == Transparent.state.SUBMIT) {
525
+
526
+ if(el.target && el.target.tagName == "FORM") {
527
+
528
+ // Action must be prevented here
529
+ // This is specific to form submission
530
+ el.preventDefault();
531
+
532
+ var href = el.target.getAttribute("action");
533
+ if(!href) href = location.pathname + href;
534
+
535
+ if (href.startsWith("#")) href = location.pathname + href;
536
+ if (href.endsWith ("#")) href = href.slice(0, -1);
537
+
538
+ var method = el.target.getAttribute("method") || "GET";
539
+ method = method.toUpperCase();
540
+
541
+ var form = Transparent.findNearestForm(el);
542
+ if (form == null) {
543
+ console.error("No form found upstream of ", el);
544
+ return null;
545
+ }
546
+
547
+ if(!form.checkValidity()) return null;
548
+
549
+ var pat = /^https?:\/\//i;
550
+ if (pat.test(href)) return [method, new URL(href), form];
551
+ return [method, new URL(href, location.origin), form];
552
+ }
553
+ }
554
+
555
+ closestEl = $(el).closest("a");
556
+ if(!closestEl.length) closestEl = $(el).closest("button");
557
+ if(!closestEl.length) closestEl = $(el).closest("input");
558
+ if (closestEl.length) el = closestEl[0];
559
+ switch (el.tagName) {
560
+
561
+ case "A":
562
+ var href = el.href;
563
+ if(!href) return null;
564
+
565
+ if (href.startsWith("#")) href = location.pathname + href;
566
+ if (href.endsWith ("#")) href = href.slice(0, -1);
567
+
568
+ var pat = /^https?:\/\//i;
569
+ if (pat.test(href)) return ["GET", new URL(href), el];
570
+
571
+ return ["GET", new URL(href, location.origin), el];
572
+
573
+ case "INPUT":
574
+ case "BUTTON":
575
+ var domainBaseURI = el.baseURI.split('/').slice(0, 3).join('/');
576
+ var domainFormAction = el.formAction.split('/').slice(0, 3).join('/');
577
+
578
+ var pathname = el.formAction.replace(domainFormAction, "");
579
+ if(!pathname) return null;
580
+
581
+ if (domainBaseURI == domainFormAction && el.getAttribute("type") == "submit") {
582
+
583
+ var form = Transparent.findNearestForm(el);
584
+ if (form == null) {
585
+ console.error("No form found upstream of ", el);
586
+ return null;
587
+ }
588
+
589
+ if(!form.checkValidity()) return null;
590
+
591
+ var pat = /^https?:\/\//i;
592
+ if (pat.test(href)) return ["POST", new URL(pathname), form];
593
+ return ["POST", new URL(pathname, location.origin), form];
594
+ }
595
+ }
596
+
597
+ // Try to detect target element
598
+ if (el.target) {
599
+
600
+ if (el.target.tagName == "A" && el.target.href)
601
+ return Transparent.findLink(el.target);
602
+
603
+ if (el.target.tagName == "BUTTON" && el.target.getAttribute("type") == "submit")
604
+ return Transparent.findLink(el.target);
605
+
606
+ if (el.target.tagName == "INPUT" && el.target.getAttribute("type") == "submit")
607
+ return Transparent.findLink(el.target);
608
+ }
609
+
610
+ // Try to catch a custom href attribute without "A" tag
611
+ if (el.target && $(el.target).attr("href")) {
612
+
613
+ var href = $(el.target).attr("href");
614
+ if(!href) return null;
615
+
616
+ if (href.startsWith("#")) href = location.pathname + href;
617
+ if (href.endsWith ("#")) href = href.slice(0, -1);
618
+
619
+ var form = Transparent.findNearestForm(el);
620
+ if (form == null) return null;
621
+
622
+ var pat = /^https?:\/\//i;
623
+ if (pat.test(href)) return ["GET", new URL(href), form];
624
+ return ["GET", new URL(href, location.origin), form];
625
+ }
626
+
627
+ if (el.target && el.target.parentElement)
628
+ return Transparent.findLink(el.target.parentElement);
629
+
630
+ return null;
631
+ };
632
+
633
+
634
+ Transparent.preload = function (link, callback = function(preload = []) {}) {
635
+
636
+ var nPreload = 0;
637
+ for (var as in link) {
638
+
639
+ link[as] = link[as].filter(url => url !== null && url.length > 0);
640
+ nPreload += link[as].length;
641
+ }
642
+
643
+ if(nPreload == 0)
644
+ return callback();
645
+
646
+ var preloads = [];
647
+ var nPreloaded = 0;
648
+ for (var as in link) {
649
+
650
+ for (var i = 0; i < link[as].length; i++) {
651
+
652
+ var url = link[as][i];
653
+ if(url === null || url === "") continue;
654
+
655
+ var preload = document.createElement("link");
656
+ preloads.push(preload);
657
+
658
+ preload.onload = function () {
659
+
660
+ if (++nPreloaded == nPreload)
661
+ return callback(preloads);
662
+ };
663
+
664
+ preload.setAttribute("rel", "preload");
665
+ preload.setAttribute("as", as);
666
+ preload.setAttribute("crossorigin","");
667
+ preload.href = url;
668
+
669
+ document.head.append(preload);
670
+ }
671
+ }
672
+ }
673
+
674
+ Transparent.findElementFromParents = function(el, parents, from = -1) {
675
+
676
+ var that = $(el).find(Settings.identifier);
677
+ for (var i = parents.length - 1, j = 0; i >= 0; i--) {
678
+
679
+ if (j++ < from) continue;
680
+
681
+ var that = that.children(parents[i].tagName);
682
+ if (that.length != 1) return undefined;
683
+ }
684
+
685
+ return that;
686
+ }
687
+
688
+ Transparent.isPage = function(dom) {
689
+
690
+ // Check if page block found
691
+ var page = $(dom).find(Settings.identifier)[0] || undefined;
692
+ if (page === undefined) return false;
693
+
694
+ return true;
695
+ }
696
+
697
+ var knownLayout = [];
698
+ Transparent.isKnownLayout = function(dom)
699
+ {
700
+ var page = (dom ? $(dom).find(Settings.identifier) : $(Settings.identifier))[0];
701
+ if (page === undefined) return false;
702
+
703
+ var layout = $(page).data("layout");
704
+ return knownLayout.indexOf(layout) !== -1;
705
+ }
706
+
707
+ Transparent.isCompatiblePage = function(dom, method = null, data = null)
708
+ {
709
+ // If no html response.. skip
710
+ if(!dom) return false;
711
+
712
+ // An exception applies here..
713
+ // in case the page contains data transferred to the server
714
+ if(method == "POST" && !jQuery.isEmptyObject(data)) return true;
715
+
716
+ var page = $(dom).find(Settings.identifier)[0] || undefined;
717
+ if (page === undefined) return false;
718
+
719
+ var currentPage = $(Settings.identifier)[0] || undefined;
720
+ if (currentPage === undefined) return false;
721
+
722
+ var name = currentPage.getAttribute("data-name") || "default";
723
+ var newName = page.getAttribute("data-name") || "default";
724
+
725
+ return name == newName;
726
+ }
727
+
728
+ Transparent.parseDuration = function(str) {
729
+
730
+ var array = String(str).split(", ");
731
+ array = array.map(function(t) {
732
+
733
+ if(String(t).endsWith("ms")) return parseFloat(String(t))/1000;
734
+ return parseFloat(String(t));
735
+ });
736
+
737
+ return Math.max(...array);
738
+ }
739
+
740
+ Transparent.callback = function(fn = function() {}, delay = 0) {
741
+
742
+ if(delay == 0) fn();
743
+ else setTimeout(fn, delay);
744
+ }
745
+
746
+ Transparent.activeTime = function(el = undefined) {
747
+
748
+ var delay = 0, duration = 0;
749
+ if(el === undefined)
750
+ el = Transparent.loader[0];
751
+
752
+ var style = window.getComputedStyle(el);
753
+ delay = Math.max(delay, 1000*Math.max(Transparent.parseDuration(style["animation-delay"]), Transparent.parseDuration(style["transition-delay"])));
754
+ duration = Math.max(duration, 1000*Math.max(Transparent.parseDuration(style["animation-duration"]), Transparent.parseDuration(style["transition-duration"])));
755
+
756
+ var style = window.getComputedStyle(el, ":before");
757
+ delay = Math.max(delay, 1000*Math.max(Transparent.parseDuration(style["animation-delay"]), Transparent.parseDuration(style["transition-delay"])));
758
+ duration = Math.max(duration, 1000*Math.max(Transparent.parseDuration(style["animation-duration"]), Transparent.parseDuration(style["transition-duration"])));
759
+
760
+ var style = window.getComputedStyle(el, ":after");
761
+ delay = Math.max(delay, 1000*Math.max(Transparent.parseDuration(style["animation-delay"]), Transparent.parseDuration(style["transition-delay"])));
762
+ duration = Math.max(duration, 1000*Math.max(Transparent.parseDuration(style["animation-duration"]), Transparent.parseDuration(style["transition-duration"])));
763
+
764
+ return {delay:delay, duration:duration};
765
+ }
766
+
767
+ Transparent.activeIn = function(activeCallback = function() {}) {
768
+
769
+ if(!Transparent.html.hasClass(Transparent.state.PREACTIVE)) {
770
+ Transparent.html.addClass(Transparent.state.PREACTIVE);
771
+ dispatchEvent(new Event('transparent:'+Transparent.state.PREACTIVE));
772
+ }
773
+
774
+ var active = Transparent.activeTime();
775
+
776
+ Transparent.html.removeClass(Transparent.state.PREACTIVE);
777
+ if(!Transparent.html.hasClass(Transparent.state.ACTIVEIN)) {
778
+ Transparent.html.addClass(Transparent.state.ACTIVEIN);
779
+ dispatchEvent(new Event('transparent:'+Transparent.state.ACTIVEIN));
780
+ }
781
+
782
+ Transparent.callback(function() {
783
+
784
+ Transparent.html.removeClass(Transparent.state.ACTIVEIN);
785
+ if(!Transparent.html.hasClass(Transparent.state.ACTIVE)) {
786
+ Transparent.html.addClass(Transparent.state.ACTIVE);
787
+ dispatchEvent(new Event('transparent:'+Transparent.state.ACTIVE));
788
+ }
789
+
790
+ var active = Transparent.activeTime();
791
+ Transparent.callback(function() {
792
+
793
+ activeCallback();
794
+
795
+ }.bind(this), active.duration);
796
+
797
+ }.bind(this), active.delay);
798
+ }
799
+
800
+ Transparent.activeOut = function(activeCallback = function() {}) {
801
+
802
+ if(!Transparent.html.hasClass(Transparent.state.ACTIVE)) {
803
+ Transparent.html.addClass(Transparent.state.ACTIVE);
804
+ dispatchEvent(new Event('transparent:'+Transparent.state.ACTIVE));
805
+ }
806
+
807
+ if(!Transparent.html.hasClass(Transparent.state.ACTIVEOUT)) {
808
+ Transparent.html.addClass(Transparent.state.ACTIVEOUT);
809
+ dispatchEvent(new Event('transparent:'+Transparent.state.ACTIVEOUT));
810
+ }
811
+
812
+ var active = Transparent.activeTime();
813
+ Transparent.callback(function() {
814
+
815
+ activeCallback();
816
+ Transparent.html.removeClass(Transparent.state.ACTIVE);
817
+
818
+ var active = Transparent.activeTime();
819
+ Transparent.callback(function() {
820
+
821
+ Transparent.html.removeClass(Transparent.state.ACTIVEOUT);
822
+ if(!Transparent.html.hasClass(Transparent.state.POSTACTIVE)){
823
+
824
+ Transparent.html.removeClass(Transparent.state.POSTACTIVE);
825
+ dispatchEvent(new Event('transparent:'+Transparent.state.POSTACTIVE));
826
+ }
827
+
828
+ if(Transparent.html.hasClass(Transparent.state.LOADING)) {
829
+
830
+ dispatchEvent(new Event('transparent:load'));
831
+
832
+ Object.values(Transparent.state).forEach(e => Transparent.html.removeClass(e));
833
+ Transparent.html.addClass(Transparent.state.ROOT + " " + Transparent.state.READY);
834
+
835
+ } else {
836
+
837
+ Transparent.html.removeClass(Transparent.state.POSTACTIVE);
838
+ }
839
+
840
+ }, active.duration);
841
+
842
+ }.bind(this), active.delay);
843
+ }
844
+
845
+ Transparent.replaceCanvases = function(dom) {
846
+
847
+ // Extract existing canvas to avoid redrawing them.. (time consuming)
848
+ $.each($('html').find("canvas"), function () {
849
+
850
+ var parent = $(this).parent();
851
+ if(!parent.length) return;
852
+
853
+ var id = this.getAttribute("id");
854
+ if (id) {
855
+
856
+ var canvas = $(dom).find("#page #" + id);
857
+ canvas.replaceWith(this);
858
+
859
+ } else {
860
+
861
+ if(dom === undefined)
862
+ console.alert("Response missing..");
863
+
864
+ var parent = Transparent.findElementFromParents(dom, $(this).parents(), 3);
865
+ if (parent === undefined) {
866
+ console.error("Unexpected canvas without ID found..", this)
867
+ return false;
868
+ }
869
+
870
+ parent.append(this);
871
+ }
872
+ });
873
+ }
874
+
875
+ Transparent.evalScript = function(el)
876
+ {
877
+ function scriptCloneEl(el){
878
+ var script = document.createElement("script");
879
+ script.text = el.innerHTML;
880
+
881
+ var i = -1, attrs = el.attributes, attr;
882
+ var N = attrs.length;
883
+ while ( ++i < N ) {
884
+ script.setAttribute( (attr = attrs[i]).name, attr.value );
885
+ }
886
+
887
+ eval($(script).text());
888
+ return script;
889
+ }
890
+
891
+ if (el.tagName === 'SCRIPT' ) el.parentNode.replaceChild( scriptCloneEl(el) , el );
892
+ else {
893
+
894
+ var i = -1, children = el.childNodes;
895
+ var N = children.length;
896
+ while ( ++i < N ) {
897
+ Transparent.evalScript( children[i] );
898
+ }
899
+ }
900
+
901
+ return el;
902
+ }
903
+
904
+ Transparent.rescue = function(dom)
905
+ {
906
+ console.error("Rescue mode.. called");
907
+ rescueMode = true;
908
+
909
+ var head = $(dom).find("head").html();
910
+ var body = $(dom).find("body").html();
911
+ if(head == undefined || body == undefined)
912
+ window.reload();
913
+
914
+ document.head.innerHTML = $(dom).find("head").html();
915
+ document.body.innerHTML = $(dom).find("body").html();
916
+
917
+ Transparent.evalScript($("head")[0]);
918
+ Transparent.evalScript($("body")[0]);
919
+ }
920
+
921
+ Transparent.userScroll = function(el = undefined) { return $(el === undefined ? document.documentElement : el).closestScrollable().prop("user-scroll") ?? true; }
922
+ Transparent.scrollTo = function(dict, el = window, callback = function() {})
923
+ {
924
+ el = $(el).length ? $(el)[0] : window;
925
+ if (el === window )
926
+ el = document.documentElement;
927
+ if (el === document)
928
+ el = document.documentElement;
929
+
930
+ var maxScrollX = $(el).prop("scrollWidth") - Math.round($(el).prop("clientWidth"));
931
+ if (maxScrollX == 0) maxScrollX = Math.round($(el).prop("clientWidth"));
932
+ var maxScrollY = $(el).prop("scrollHeight") - Math.round($(el).prop("clientHeight"));
933
+ if (maxScrollY == 0) maxScrollY = Math.round($(el).prop("clientHeight"));
934
+
935
+ scrollTop = Math.max(0, Math.min(dict["top"] ?? $(el).prop("scrollTop"), maxScrollY));
936
+ scrollLeft = Math.max(0, Math.min(dict["left"] ?? $(el).prop("scrollLeft"), maxScrollX));
937
+
938
+ speed = parseFloat(dict["speed"] ?? 0);
939
+ easing = dict["easing"] ?? "swing";
940
+ debounce = dict["debounce"] ?? 0;
941
+
942
+ duration = 1000*Transparent.parseDuration(dict["duration"] ?? 0);
943
+ durationX = 1000*Transparent.parseDuration(dict["duration-x"] ?? dict["duration"] ?? 0);
944
+ durationY = 1000*Transparent.parseDuration(dict["duration-y"] ?? dict["duration"] ?? 0);
945
+
946
+ if(speed) {
947
+
948
+ var currentScrollX = $(el)[0].scrollLeft;
949
+ if(currentScrollX < scrollLeft || scrollLeft == 0) // Going to the right
950
+ distanceX = Math.abs(scrollLeft - currentScrollX);
951
+ else // Going back to 0 position
952
+ distanceX = currentScrollX;
953
+
954
+ var currentScrollY = $(el)[0].scrollTop;
955
+ if(currentScrollY <= scrollTop || scrollTop == 0) // Going to the right
956
+ distanceY = Math.abs(scrollTop - currentScrollY);
957
+ else // Going back to 0 position
958
+ distanceY = currentScrollY;
959
+
960
+ durationX = speed ? 1000*distanceX/speed : durationX;
961
+ durationY = speed ? 1000*distanceY/speed : durationY;
962
+ duration = durationX+durationY;
963
+ }
964
+
965
+ var callbackWrapper = function() {
966
+
967
+ el.dispatchEvent(new Event('scroll'));
968
+ callback();
969
+
970
+ $(el).prop("user-scroll", true);
971
+ };
972
+
973
+ if(duration == 0) {
974
+
975
+ el.scrollTo(scrollLeft, scrollTop);
976
+ el.dispatchEvent(new Event('scroll'));
977
+ callback();
978
+
979
+ $(el).prop("user-scroll", true);
980
+
981
+ } else {
982
+
983
+ $(el).animate({scrollTop: scrollTop}, durationY, easing,
984
+ () => $(el).animate({scrollLeft: scrollLeft}, durationX, easing, Transparent.debounce(callbackWrapper, debounce))
985
+ );
986
+ }
987
+
988
+ return this;
989
+ }
990
+
991
+ Transparent.debounce = function(func, wait, immediate) {
992
+
993
+ var timeout;
994
+
995
+ return function() {
996
+
997
+ var context = this, args = arguments;
998
+ var later = function() {
999
+
1000
+ timeout = null;
1001
+ if (!immediate) func.apply(context, args);
1002
+ };
1003
+
1004
+ var callNow = immediate && !timeout;
1005
+ clearTimeout(timeout);
1006
+
1007
+ timeout = setTimeout(later, wait);
1008
+ if (callNow) func.apply(context, args);
1009
+ };
1010
+ };
1011
+
1012
+
1013
+ Transparent.findImages = function () {
1014
+
1015
+ var doc = document.documentElement;
1016
+ const srcChecker = /url\(\s*?['"]?\s*?(\S+?)\s*?["']?\s*?\)/i
1017
+ return Array.from(doc.querySelectorAll('*'))
1018
+ .reduce((collection, node) => {
1019
+
1020
+ let prop = window.getComputedStyle(node, null).getPropertyValue('background-image')
1021
+ let match = srcChecker.exec(prop);
1022
+ if (match) collection.add(match[1]);
1023
+
1024
+ if (/^img$/i.test(node.tagName)) collection.add(node.src)
1025
+ else if (/^frame$/i.test(node.tagName)) {
1026
+
1027
+ try {
1028
+ searchDOM(node.contentDocument || node.contentWindow.document)
1029
+ .forEach(img => { if (img) collection.add(img); })
1030
+ } catch (e) {}
1031
+ }
1032
+
1033
+ return collection;
1034
+
1035
+ }, new Set());
1036
+ }
1037
+
1038
+ Transparent.lazyLoad = function (lazyloadImages = undefined)
1039
+ {
1040
+ lazyloadImages = lazyloadImages || document.querySelectorAll("img[data-src]:not(.loaded)");
1041
+ if ("IntersectionObserver" in window) {
1042
+
1043
+ var imageObserver = new IntersectionObserver(function (entries, observer) {
1044
+ entries.forEach(function (entry) {
1045
+ if (entry.isIntersecting) {
1046
+ var image = entry.target;
1047
+ var lazybox = image.closest(".lazybox");
1048
+
1049
+ image.onload = function() {
1050
+ this.classList.add("loaded");
1051
+ this.classList.remove("loading");
1052
+ if(lazybox) lazybox.classList.add("loaded");
1053
+ if(lazybox) lazybox.classList.remove("loading");
1054
+ };
1055
+
1056
+ if(lazybox) lazybox.classList.add("loading");
1057
+ image.classList.add("loading");
1058
+ image.src = image.dataset.src;
1059
+
1060
+ imageObserver.unobserve(image);
1061
+ }
1062
+ });
1063
+ });
1064
+
1065
+ lazyloadImages.forEach(function (image) {
1066
+ imageObserver.observe(image);
1067
+ });
1068
+
1069
+ } else {
1070
+
1071
+ var lazyloadThrottleTimeout;
1072
+
1073
+ function lazyload() {
1074
+ if (lazyloadThrottleTimeout) {
1075
+ clearTimeout(lazyloadThrottleTimeout);
1076
+ }
1077
+
1078
+ lazyloadThrottleTimeout = setTimeout(function () {
1079
+ var scrollTop = window.pageYOffset;
1080
+ lazyloadImages.forEach(function (img) {
1081
+ if (img.offsetTop < (window.innerHeight + scrollTop)) {
1082
+ img.src = img.dataset.src;
1083
+ img.classList.add('loaded');
1084
+ }
1085
+ });
1086
+ if (lazyloadImages.length == 0) {
1087
+ document.removeEventListener("scroll", lazyload);
1088
+ window.removeEventListener("resize", lazyload);
1089
+ window.removeEventListener("orientationChange", lazyload);
1090
+ }
1091
+ }, 20);
1092
+ }
1093
+
1094
+ document.addEventListener("scroll", lazyload);
1095
+ window.addEventListener("resize", lazyload);
1096
+ window.addEventListener("orientationChange", lazyload);
1097
+ }
1098
+ }
1099
+
1100
+ Transparent.loadImages = function()
1101
+ {
1102
+ function loadImg (src, timeout = 500) {
1103
+ var imgPromise = new Promise((resolve, reject) => {
1104
+
1105
+ let img = new Image()
1106
+ img.onload = () => {
1107
+ resolve({
1108
+ src: src,
1109
+ width: img.naturalWidth,
1110
+ height: img.naturalHeight
1111
+ })
1112
+ }
1113
+
1114
+ img.onerror = reject
1115
+ img.src = src
1116
+ })
1117
+
1118
+ var timer = new Promise((resolve, reject) => { setTimeout(reject, timeout) })
1119
+ return Promise.race([imgPromise, timer])
1120
+ }
1121
+
1122
+ function loadImgAll (imgList, timeout = 500) {
1123
+ return new Promise((resolve, reject) => {
1124
+ Promise.all(imgList
1125
+ .map(src => loadImg(src, timeout))
1126
+ .map(p => p.catch(e => false))
1127
+ ).then(results => resolve(results.filter(r => r)))
1128
+ })
1129
+ }
1130
+
1131
+ return new Promise((resolve, reject) => {
1132
+ loadImgAll(Array.from(Transparent.findImages(document.documentElement))).then(resolve, reject)
1133
+ })
1134
+ }
1135
+
1136
+ Transparent.transferAttributes = function(dom) {
1137
+
1138
+ var html = $(dom).find("html");
1139
+ $($("html")[0].attributes).each(function(i, attr) {
1140
+ if(attr.name == "class") return;
1141
+ $("html").removeAttr(attr.name);
1142
+ });
1143
+
1144
+ $($(html)[0].attributes).each(function(i, attr) {
1145
+ if(attr.name == "class") return;
1146
+ $("html").attr(attr.name, attr.value);
1147
+ });
1148
+
1149
+ var head = $(dom).find("head");
1150
+ $($("head")[0].attributes).each(function(i, attr) {
1151
+ $("head").removeAttr(attr.name);
1152
+ });
1153
+
1154
+ $($(head)[0].attributes).each(function(i, attr) {
1155
+ $("head").attr(attr.name, attr.value);
1156
+ });
1157
+
1158
+ var body = $(dom).find("body");
1159
+ $($("body")[0].attributes).each(function(i, attr) {
1160
+ $("body").removeAttr(attr.name);
1161
+ });
1162
+ $($(body)[0].attributes).each(function(i, attr) {
1163
+ $("body").attr(attr.name, attr.value);
1164
+ });
1165
+ }
1166
+
1167
+ Transparent.onLoad = function(uuid, dom, callback = null, scrollTo = false) {
1168
+
1169
+ window.previousHash = window.location.hash;
1170
+ window.previousLocation = window.location.toString();
1171
+ if(callback === null) callback = function() {};
1172
+
1173
+ // Transfert attributes
1174
+ Transparent.transferAttributes(dom);
1175
+
1176
+ // Replace head..
1177
+ var head = $(dom).find("head");
1178
+ $("head").children().each(function() {
1179
+
1180
+ var el = this;
1181
+ var found = false;
1182
+
1183
+ head.children().each(function() {
1184
+
1185
+ found = this.isEqualNode(el);
1186
+ return !found;
1187
+ });
1188
+
1189
+ if(!found) this.remove();
1190
+ });
1191
+
1192
+ head.children().each(function() {
1193
+
1194
+ var el = this;
1195
+ var found = false;
1196
+
1197
+ $("head").children().each(function() { found |= this.isEqualNode(el); });
1198
+ if(!found) {
1199
+
1200
+ if(this.tagName != "SCRIPT" || Settings["global_code"] == true) $("head").append(this.cloneNode(true));
1201
+ else $("head").append(this);
1202
+ }
1203
+ });
1204
+
1205
+ // Replace canvases..
1206
+ Transparent.replaceCanvases(dom);
1207
+
1208
+ // Extract page block to be loaded
1209
+ var page = $(dom).find(Settings.identifier);
1210
+ if(dom == undefined || page == undefined) window.reload(); // Error a posteriori
1211
+
1212
+ var oldPage = $(Settings.identifier);
1213
+
1214
+ // Make sure name/layout keep the same after a page change (tolerance for POST or GET requests)
1215
+ if(oldPage.attr("data-layout") != undefined && page.attr("data-layout") != undefined) {
1216
+
1217
+ var switchLayout = Transparent.state.SWITCH.replace("X", page.attr("data-layout")).replace("Y", oldPage.attr("data-layout"));
1218
+ page.attr("data-layout-prev", oldPage.attr("data-layout"));
1219
+ }
1220
+
1221
+ var states = Object.values(Transparent.state);
1222
+ var htmlClass = Array.from(($(dom).find("html").attr("class") || "").split(" ")).filter(x => !states.includes(x));
1223
+ var oldHtmlClass = Array.from(($(Transparent.html).attr("class") || "").split(" "));
1224
+ var removeHtmlClass = oldHtmlClass.filter(x => !htmlClass.includes(x) && switchLayout != x && !states.includes(x));
1225
+
1226
+ Transparent.html.removeClass(removeHtmlClass).addClass(htmlClass);
1227
+ $(page).insertBefore(oldPage);
1228
+
1229
+ oldPage.remove();
1230
+
1231
+ if(Settings["global_code"] == true) Transparent.evalScript($(page)[0]);
1232
+ dispatchEvent(new Event('DOMContentLoaded'));
1233
+
1234
+ Transparent.addLayout();
1235
+
1236
+ if(scrollTo) {
1237
+
1238
+ // Go back to top of the page..
1239
+ var scrollableElements = Transparent.getScrollableElement();
1240
+ var scrollableElementsXY = Transparent.getResponsePosition(uuid);
1241
+
1242
+ for(i = 0; i < scrollableElements.length; i++) {
1243
+
1244
+ var el = scrollableElements[i];
1245
+ var positionXY = undefined;
1246
+
1247
+ if(scrollableElementsXY.length == scrollableElements.length)
1248
+ positionXY = scrollableElementsXY[i] || undefined;
1249
+
1250
+ if(el == window || el == document.documentElement) {
1251
+
1252
+ if(positionXY != undefined) Transparent.scrollTo({top:positionXY[0], left:positionXY[1], duration:0});
1253
+ else if (location.hash) Transparent.scrollToHash(location.hash, {duration:0});
1254
+ else Transparent.scrollTo({top:0, left:0, duration:0});
1255
+
1256
+ } else {
1257
+
1258
+ if(positionXY != undefined) Transparent.scrollTo({top:positionXY[0], left:positionXY[1], duration:0}, el);
1259
+ else Transparent.scrollTo({top:0, left:0, duration:0}, el);
1260
+ }
1261
+ }
1262
+ }
1263
+
1264
+ $('head').append(function() {
1265
+
1266
+ $(Settings.identifier).append(function() {
1267
+
1268
+ setTimeout(function() {
1269
+
1270
+ // Callback if needed, or any other actions
1271
+ callback();
1272
+
1273
+ // Trigger onload event
1274
+ dispatchEvent(new Event('transparent:load'));
1275
+ dispatchEvent(new Event('load'));
1276
+
1277
+ }.bind(this), 1);
1278
+ });
1279
+ });
1280
+ }
1281
+
1282
+ function uuidv4() {
1283
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
1284
+ var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
1285
+ return v.toString(16);
1286
+ });
1287
+ }
1288
+
1289
+ function isLocalStorageNameSupported() {
1290
+
1291
+ var testKey = 'test', storage = window.localStorage;
1292
+ try {
1293
+
1294
+ storage.setItem(testKey, '1');
1295
+ storage.removeItem(testKey);
1296
+ return true;
1297
+
1298
+ } catch (error) { return false; }
1299
+ }
1300
+
1301
+ Transparent.remToPixel = function(rem) { return parseFloat(rem) * parseFloat(getComputedStyle(document.documentElement).fontSize); }
1302
+ Transparent.emToPixel = function(em, el) { return parseFloat(em ) * parseFloat(getComputedStyle(el.parentElement).fontSize); }
1303
+ Transparent.percentToPixel = function(p , el) { return parseFloat(p ) * el.outerWidth(); }
1304
+ Transparent.parseToPixel = function(str, el) {
1305
+
1306
+ if(str === undefined) return undefined;
1307
+
1308
+ var array = String(str).split(", ");
1309
+ array = array.map(function(s) {
1310
+
1311
+ if(s.endsWith("rem")) return Transparent.remToPixel (s);
1312
+ else if(s.endsWith("em") ) return Transparent.emToPixel (s, el);
1313
+ else if(s.endsWith("%") ) return Transparent.percentToPixel(s, el);
1314
+ return parseFloat(s);
1315
+ });
1316
+
1317
+ return Math.max(...array);
1318
+ }
1319
+
1320
+ Transparent.getScrollPadding = function(el = document.documentElement) {
1321
+
1322
+ var style = window.getComputedStyle(el);
1323
+ var dict = {};
1324
+ dict["top" ] = Transparent.parseToPixel(style["scroll-padding-top" ] || 0, el);
1325
+ dict["left" ] = Transparent.parseToPixel(style["scroll-padding-left" ] || 0, el);
1326
+ dict["right" ] = Transparent.parseToPixel(style["scroll-padding-right" ] || 0, el);
1327
+ dict["bottom"] = Transparent.parseToPixel(style["scroll-padding-bottom"] || 0, el);
1328
+
1329
+ if(isNaN(dict["top" ])) dict["top"] = 0;
1330
+ if(isNaN(dict["left" ])) dict["left"] = 0;
1331
+ if(isNaN(dict["right" ])) dict["right"] = 0;
1332
+ if(isNaN(dict["bottom"])) dict["bottom"] = 0;
1333
+
1334
+ return dict;
1335
+ }
1336
+
1337
+ Transparent.scrollToHash = function(hash = window.location.hash, options = {}, callback = function() {}, el = window)
1338
+ {
1339
+ if (hash !== "") {
1340
+
1341
+ if ((''+hash).charAt(0) !== '#')
1342
+ hash = '#' + hash;
1343
+
1344
+ var hashElement = $(hash)[0] ?? undefined;
1345
+ if (hashElement !== undefined) {
1346
+
1347
+ var scrollTop = hashElement.getBoundingClientRect().top + document.documentElement.scrollTop - Transparent.getScrollPadding().top;
1348
+ var scrollLeft = hashElement.getBoundingClientRect().left + document.documentElement.scrollLeft - Transparent.getScrollPadding().left;
1349
+
1350
+ options = Object.assign({duration: Settings["smoothscroll_duration"], speed: Settings["smoothscroll_speed"]}, options, {left:scrollLeft, top:scrollTop});
1351
+ }
1352
+
1353
+ var bottomReach = document.body.scrollHeight - (window.scrollY + window.innerHeight) < 1;
1354
+ var bottomOverflow = scrollTop > window.scrollY + window.innerHeight;
1355
+ }
1356
+
1357
+ if(hash === "" || (bottomReach && bottomOverflow)) callback({}, el);
1358
+ else Transparent.scrollTo(options, el, callback);
1359
+
1360
+ return this;
1361
+ }
1362
+
1363
+ Transparent.isElement = function(obj){
1364
+ try { return Boolean(obj.constructor.__proto__.prototype.constructor.name); }
1365
+ catch(e) { return false; }
1366
+ };
1367
+
1368
+ Transparent.getScrollableElement = function(el = document.documentElement)
1369
+ {
1370
+ return $(el).find('*').add(el).filter(function() { return $(this).isScrollable(); });
1371
+ };
1372
+
1373
+ Transparent.getScrollableElementXY = function() {
1374
+
1375
+ var elementsXY = [];
1376
+ var elements = Transparent.getScrollableElement();
1377
+
1378
+ for(i = 0; i < elements.length; i++)
1379
+ elementsXY.push([$(elements[i]).scrollTop(), $(elements[i]).scrollLeft()]);
1380
+
1381
+ return elementsXY;
1382
+ }
1383
+
1384
+ function __main__(e) {
1385
+
1386
+ // Disable transparent JS (e.g. during development..)
1387
+ if(Settings.disable) return;
1388
+
1389
+ // Determine link
1390
+ const link = Transparent.findLink(e);
1391
+ if (link == null) return;
1392
+
1393
+ dispatchEvent(new CustomEvent('transparent:link', {link:link}));
1394
+
1395
+ const uuid = uuidv4();
1396
+ const type = link[0];
1397
+ const url = link[1];
1398
+
1399
+ var target = Transparent.isElement(link[2]) ? link[2] : undefined;
1400
+ var data = Transparent.isElement(link[2]) ? undefined : link[2];
1401
+
1402
+ var form = target != undefined && target.tagName == "FORM" ? target : undefined;
1403
+ if (form) {
1404
+ data = $(form).serialize();
1405
+ $(form).find(':submit').attr('disabled', 'disabled');
1406
+ }
1407
+
1408
+ // Wait for transparent window event to be triggered
1409
+ if (!isReady) return;
1410
+
1411
+ if (e.type != Transparent.state.POPSTATE &&
1412
+ e.type != Transparent.state.HASHCHANGE && !$(this).find(Settings.identifier).length) return;
1413
+
1414
+ // Specific page exception
1415
+ for(i = 0; i < Settings.exceptions.length; i++) {
1416
+
1417
+ exception = Settings.exceptions[i];
1418
+ if (url.pathname.startsWith(exception)) return;
1419
+ }
1420
+
1421
+ // Ressources files rejected
1422
+ if (url.pathname.startsWith("/css")) return;
1423
+ if (url.pathname.startsWith("/js")) return;
1424
+ if (url.pathname.startsWith("/images")) return;
1425
+ if (url.pathname.startsWith("/vendor")) return;
1426
+
1427
+ // Unsecure url
1428
+ if (url.origin != location.origin) return;
1429
+
1430
+ e.preventDefault();
1431
+
1432
+ if (url == location) return;
1433
+
1434
+ if((e.type == Transparent.state.CLICK || e.type == Transparent.state.HASHCHANGE) && url.pathname == location.pathname && url.search == location.search && type != "POST") {
1435
+
1436
+ if(!url.hash) return;
1437
+ Transparent.scrollToHash(url.hash ?? "", {easing:Settings["smoothscroll_easing"], duration:Settings["smoothscroll_duration"], speed:Settings["smoothscroll_speed"]}, function() {
1438
+
1439
+ if (e.target != undefined && $(e.target).data("skip-hash") != true)
1440
+ window.replaceHash(url.hash);
1441
+
1442
+ }, $(e.target).closestScrollable());
1443
+
1444
+ return;
1445
+ }
1446
+
1447
+ if(e.metaKey && e.altKey) return window.open(url).focus();
1448
+ if(e.metaKey && e.shiftKey) return window.open(url, '_blank').focus(); // Safari not focusing..
1449
+ if(e.metaKey || $(target).attr("target") == "_blank") return window.open(url, '_blank');
1450
+
1451
+ dispatchEvent(new Event('transparent:onbeforeunload'));
1452
+ dispatchEvent(new Event('onbeforeunload'));
1453
+
1454
+ $(Transparent.html).prop("user-scroll", true);
1455
+ $(Transparent.html).stop();
1456
+
1457
+ function isJsonResponse(str) {
1458
+ try { JSON.parse(str); return true; }
1459
+ catch (e) { return false; }
1460
+ }
1461
+
1462
+ function handleResponse(uuid, status = 200, method = null, data = null, xhr = null, request = null) {
1463
+
1464
+ var responseURL;
1465
+ responseURL = xhr !== null ? xhr.responseURL : url.href;
1466
+
1467
+ responseText = Transparent.getResponseText(uuid);
1468
+
1469
+ var fragmentPos = responseURL.indexOf("#");
1470
+ var strippedResponseUrl = (fragmentPos < 0 ? responseURL : responseURL.substring(0, fragmentPos)).trimEnd("/");
1471
+
1472
+ var fragmentPos = url.href.indexOf("#");
1473
+ var strippedUrlHref = (fragmentPos < 0 ? url.href : url.href.substring(0, fragmentPos)).trimEnd("/");
1474
+ if( strippedUrlHref == strippedResponseUrl )
1475
+ responseURL = url.href; // NB: xhr.responseURL strips away #fragments
1476
+
1477
+ if(!responseText) {
1478
+
1479
+ if(!request && responseText === null) {
1480
+
1481
+ setTimeout(function() { window.location.href = responseURL; }, Settings["throttle"]);
1482
+ return;
1483
+ }
1484
+
1485
+ responseText = request.responseText;
1486
+ if(status >= 500) {
1487
+
1488
+ console.error("Unexpected XHR response from "+uuid+": error code "+request.status);
1489
+ console.error(sessionStorage);
1490
+ }
1491
+
1492
+ if(!Transparent.hasResponse(uuid))
1493
+ Transparent.setResponse(uuid, responseText);
1494
+ }
1495
+
1496
+ var dom = new DOMParser().parseFromString(responseText, "text/html");
1497
+ if(request && request.getResponseHeader("Content-Type") == "application/json") {
1498
+
1499
+ if(!isJsonResponse(responseText)) {
1500
+ console.error("Invalid response received for "+ responseURL);
1501
+ if(Settings.debug) return Transparent.rescue(dom);
1502
+ }
1503
+
1504
+ if(form) {
1505
+ $(form).find(':submit').removeAttr('disabled');
1506
+ form.reset();
1507
+ }
1508
+
1509
+ var response = JSON.parse(responseText);
1510
+ if(Settings.debug) console.log(response);
1511
+
1512
+ if(response.code == "302") {
1513
+
1514
+ if(response.target) location.href = response.target;
1515
+ else location.reload();
1516
+ }
1517
+
1518
+ return dispatchEvent(new Event('load'));
1519
+ }
1520
+
1521
+ // From here the page is valid..
1522
+ // so the new page is added to history..
1523
+ if(xhr)
1524
+ history.pushState({uuid: uuid, status:status, method: method, data: data, href: responseURL}, '', responseURL);
1525
+
1526
+ var dom = new DOMParser().parseFromString(responseText, "text/html");
1527
+ if(status != 200) // Blatant error received..
1528
+ return Transparent.rescue(dom);
1529
+
1530
+ // Page not recognized.. just go there.. no POST information transmitted..
1531
+ if(!Transparent.isPage(dom))
1532
+ return window.location.href = url;
1533
+
1534
+ // Layout not compatible.. needs to be reloaded (exception when POST is detected..)
1535
+ if(!Transparent.isCompatiblePage(dom, method, data))
1536
+ return window.location.href = url;
1537
+
1538
+ // Mark layout as known
1539
+ if(!Transparent.isKnownLayout(dom)) {
1540
+
1541
+ Transparent.html.addClass(Transparent.state.NEW);
1542
+ dispatchEvent(new Event('transparent:'+Transparent.state.NEW));
1543
+ }
1544
+
1545
+ // Mark active as popstate or submit
1546
+ if(e.type == Transparent.state.POPSTATE) {
1547
+
1548
+ Transparent.html.addClass(Transparent.state.POPSTATE);
1549
+ dispatchEvent(new Event('transparent:'+Transparent.state.POPSTATE));
1550
+
1551
+ } else if(e.type == Transparent.state.SUBMIT) {
1552
+
1553
+ Transparent.html.addClass(Transparent.state.SUBMIT);
1554
+ dispatchEvent(new Event('transparent:'+Transparent.state.SUBMIT));
1555
+ }
1556
+
1557
+ // Callback active
1558
+ var prevLayout = Transparent.getLayout();
1559
+ var newLayout = Transparent.getLayout(dom);
1560
+ if(prevLayout == newLayout)
1561
+ Transparent.html.addClass(Transparent.state.SAME);
1562
+
1563
+ var switchLayout = Transparent.state.SWITCH.replace("X", prevLayout).replace("Y", newLayout);
1564
+ Transparent.html.addClass(switchLayout);
1565
+
1566
+ dispatchEvent(new Event('transparent:'+switchLayout));
1567
+
1568
+ if($(dom).find("html").hasClass(Transparent.state.RELOAD))
1569
+ window.location.reload();
1570
+
1571
+ Transparent.html.addClass(Transparent.state.LOADING);
1572
+
1573
+ return Transparent.activeIn(function() {
1574
+
1575
+ if($(dom).find("html").hasClass(Transparent.state.RELOAD))
1576
+ return; // Reload state found..
1577
+
1578
+ Transparent.onLoad(uuid, dom, function() {
1579
+
1580
+ Transparent.activeOut(function() {
1581
+
1582
+ Transparent.html
1583
+ .removeClass(switchLayout)
1584
+ .removeClass(Transparent.state.SUBMIT)
1585
+ .removeClass(Transparent.state.POPSTATE)
1586
+ .removeClass(Transparent.state.NEW);
1587
+ });
1588
+
1589
+ }, type != "POST");
1590
+ });
1591
+ }
1592
+
1593
+ if(history.state && !Transparent.hasResponse(history.state.uuid))
1594
+ Transparent.setResponse(history.state.uuid, Transparent.html[0], Transparent.getScrollableElementXY());
1595
+
1596
+ // This append on user click (e.g. when user push a link)
1597
+ // It is null when dev is pushing or replacing state
1598
+ var addNewState = !e.state;
1599
+ if (addNewState) {
1600
+
1601
+ if(history.state)
1602
+ Transparent.setResponsePosition(history.state.uuid, Transparent.getScrollableElementXY());
1603
+
1604
+ $(Transparent.html).prop("user-scroll", false); // make sure to avoid page jump during transition (cancelled in activeIn callback)
1605
+
1606
+ // Submit ajax request..
1607
+ var xhr = new XMLHttpRequest();
1608
+ return jQuery.ajax({
1609
+ url: url.href,
1610
+ type: type,
1611
+ data: data,
1612
+ dataType: 'html',
1613
+ headers: Settings["headers"] || {},
1614
+ xhr: function () { return xhr; },
1615
+ success: function (html, status, request) { return handleResponse(uuid, request.status, type, data, xhr, request); },
1616
+ error: function (request, ajaxOptions, thrownError) { return handleResponse(uuid, request.status, type, data, xhr, request); }
1617
+ });
1618
+ }
1619
+
1620
+ return handleResponse(history.state.uuid, history.state.status, history.state.method, history.state.data);
1621
+ }
1622
+
1623
+ // Update history if not refreshing page or different page (avoid double pushState)
1624
+ var href = history.state ? history.state.href : null;
1625
+ if (href != location.origin + location.pathname + location.hash)
1626
+ history.replaceState({uuid: uuidv4(), status: history.state ? history.state.status : 200, data:{}, method: history.state ? history.state.method : "GET", href: location.origin + location.pathname + location.hash}, '', location.origin + location.pathname + location.hash);
1627
+
1628
+ // Overload onpopstate
1629
+ if(Settings.disable) {
1630
+
1631
+ var states = Object.values(Transparent.state);
1632
+ var htmlClass = Array.from(($("html").attr("class") || "").split(" ")).filter(x => !states.includes(x));
1633
+ Transparent.html.removeClass(states).addClass(htmlClass.join(" ")+" "+Transparent.state.ROOT+" "+Transparent.state.READY+" "+Transparent.state.DISABLE);
1634
+
1635
+ } else {
1636
+
1637
+ window.onpopstate = __main__; // Onpopstate pop out straight to previous page.. this creates a jump while changing pages with hash..
1638
+ window.onhashchange = __main__;
1639
+ document.addEventListener('click', __main__, false);
1640
+
1641
+ $("form").submit(__main__);
1642
+ }
1643
+
1644
+ return Transparent;
1645
+ });