PageSourceSearch

https://villency.com/wp-content/plugins/pausar-3d-ar-for-elementor-premium/assets/js/pausar.js?ver=1.3.5

js villency.com collected 2026-10-09 02:56:11 UTC 41,451 bytes, 1,068 lines download raw bytes

1import { QueryList } from "./queryModule.js?ver=1.2";//{ QueryList as queryList }
2import { PausAR_Popup as Popup } from "./popupModule.js?ver=1.2";
3
4//Global variables, which are used throughout all AR functions
5let startARButtons = [];         //Array of all available AR Buttons (or for current version)
6let widgetContainers = [];       //Array of all available Widgets (or for current version)
7let modelPreviews = [];          //JSON Array with ALL ModelViewers and sources (needed for fullscreen mode)
8let currentLoadingPreviews = new Map(); //A KeyMap of all "modelPreviews" 3D viewers (not just the current widget) that are currently loading a 3D model
9let queryPopup;
10let xrarSupport = false;
11let ignoreAutostart = false;    //Set to FALSE, if the autostart was already triggert or a other ar scene did start
12//fullscreen Variables
13let originalOverflow = null;
14let originalTouchAction = null;
15const fullscreenFallbackStyles = document.createElement("style");    //Additional StylesheetRules for the fallback fullscreen
16let escapeListener;
17let fullscreenListener;
18let fullscreenTransition = false;
19
20//window.onmessage = function(event) {
21//    iframeLinkOverwrite = event.data;
22//};
23
24if (document.readyState === "complete" || document.readyState === "loaded") {
25    initAdditionalEvents();
26} else {
27    document.addEventListener("DOMContentLoaded", initAdditionalEvents);
28}
29
30function initAdditionalEvents() {
31
32    QueryList.isXRARDevice().then(function(result) {
33        xrarSupport = result;
34        //Add Elementor Popup EventListener
35        if(typeof jQuery !== 'undefined' && jQuery != null) {
36            jQuery( document ).on( 'elementor/popup/show', ( event, id, instance ) => {
37                init();
38            } );
39        }
40        //run init() once, after DOMContentLoaded
41        init();
42    }).catch(
43        function(err) {
44            xrarSupport = false;
45            console.warn("XR Query could not perform a navigator.xr test");
46            init();
47        }
48    );
49    
50}
51
52/**
53 * Adds Eventlisteners to all PausAR Elements
54 */
55function init() {
56
57    let arSupport = true;//WebXR exluded
58    queryPopup = new Popup();
59    
60    //Non-Support Query (Device and OS)
61    if(QueryList.isAndroid()) {
62        if(QueryList.getAndroidVersion() < 7) {
63            arSupport = false;
64        }
65    } else if(QueryList.isIOS()) {
66        //if(!QueryList.isQuicklook()) {
67        //    arSupport = false;
68        //}
69    } else if(!QueryList.isDesktop() && QueryList.hasTouchScreen()) {
70        //Windows-Phone or other misc mobile devices
71        arSupport = false;
72    }
73
74    //startARButtons = document.querySelectorAll(".pausAR-UI-ArButton[pausar_version='elementor']");
75    //widgetContainers = document.querySelectorAll("div.pausAR-UI-widgetContainer[pausar_version='elementor']");
76    startARButtons = document.querySelectorAll(".pausAR-UI-ArButton[pausar]");
77    widgetContainers = document.querySelectorAll("div.pausAR-UI-widgetContainer[pausar]");
78    for(let x = 0; x < widgetContainers.length; x++) {
79        let modelPreview = widgetContainers[x].querySelector("model-viewer.pausAR-UI-modelViewer[pausar]");
80        if(modelPreview) {
81            modelPreviews.push({
82                "viewer": modelPreview,
83                "widget": widgetContainers[x],
84                "touchAction" : modelPreview.getAttribute('touch-action'),
85            });
86        }
87    }
88
89    //-------------
90    //Start Button Handler
91    //-------------
92    
93    for(let i = 0; i < startARButtons.length; i++) {
94
95        validatePaths(startARButtons[i]);
96
97        let androidModel = (startARButtons[i].getAttribute("paus_glb") != null && startARButtons[i].getAttribute("paus_glb") != "") ? true : false;
98        let iosModel = (startARButtons[i].getAttribute("paus_ios") != null && startARButtons[i].getAttribute("paus_ios") != "") ? true : false;
99        
100
101        //3D Model Query (Missing files?)
102        if((QueryList.isAndroid() && !androidModel) || (QueryList.isIOS() && !iosModel)) {
103            startARButtons[i].remove();
104        }
105        
106        if(!arSupport) {
107            startARButtons[i].remove();
108        } else {
109            startARButtons[i].addEventListener('click', startModelSession);
110        }
111
112    }
113
114    //-------------
115    //Fullscreen Handler
116    //-------------
117    
118    for(let j = 0; j < widgetContainers.length; j++) {
119        let innerFullscreenButton = widgetContainers[j].querySelector("div.pausAR-UI-modelPreviewContainer .pausAR-UI-modelPreviewInterface button.pausAR-UI-fullscreenButton[pausar]");
120        if(innerFullscreenButton) {
121            innerFullscreenButton.addEventListener('click', function() {
122                toggleFullscreen(widgetContainers[j], innerFullscreenButton);
123            });
124            innerFullscreenButton.classList.remove('pausAR-UI-elementHide');
125            innerFullscreenButton.disabled = false;
126        }
127        
128    }
129
130    //-------------
131    //Autostart
132    //-------------
133    
134    let autoStartButton = null;
135    if(QueryList.getSearchParameter) {
136        let searchParameter = QueryList.getSearchParameter(false);
137        for(let q = 0; q < searchParameter.length; q++) {
138            if(searchParameter[q].name == QueryList.autostartParameterName) {
139                //Remove parameter
140                let sanatizedSearchParameter = removeArrayElement(searchParameter, q);
141                let replacedSearchParameter = "";
142                for(let j = 0; j < sanatizedSearchParameter.length; j++) {
143
144                    if(sanatizedSearchParameter[j].value != "") {
145                        replacedSearchParameter += sanatizedSearchParameter[j].name + "=" + sanatizedSearchParameter[j].value;
146                    } else {
147                        replacedSearchParameter += sanatizedSearchParameter[j].name;
148                    }
149
150                    if(j < (sanatizedSearchParameter.length-1)) {
151                        replacedSearchParameter += "&";
152                    }
153                }
154                if(replacedSearchParameter != "") {
155                    replacedSearchParameter = "?" + replacedSearchParameter;
156                }
157                window.history.replaceState( {} , "", window.location.origin + window.location.pathname + replacedSearchParameter + window.location.hash );
158                for(let r = 0; r < startARButtons.length; r++) {
159                    if(startARButtons[r].getAttribute('id') == searchParameter[q].value) {
160                        autoStartButton = startARButtons[r];
161                        break;
162                    }
163                }
164            }
165        }
166    }
167    //Check if autostart can be activated
168    let autostartSupport = false;
169    if(QueryList.supportAutostart && autoStartButton != null) {
170        autostartSupport = QueryList.supportAutostart(autoStartButton);
171    }
172    if(autostartSupport == true) {
173        console.log("[PausAR]: Start AR scene \"" + autoStartButton.getAttribute("id") + "\" by autostart parameter.");
174        if(document.body.contains(autoStartButton)) {
175            if(modelPreviews.length > 0) {
176                queryPopup.openPopup('autostartLoader');
177                queryPopup.addExternalCloseEventListener(function () {
178                    ignoreAutostart = true;//aborting autostart
179                });
180                function detectPreviewLoading(event) {
181                    if (event.detail.reason == 'model-load') {
182                        currentLoadingPreviews.set(event.target, true);
183                        event.target.removeEventListener('progress', detectPreviewLoading);//Remove itself as a listener
184                    }
185                }
186                function detectPreviewLoadingFinished(event) {
187                    if (currentLoadingPreviews.has(event.target)) {//Loading of target has finished
188                        currentLoadingPreviews.delete(event.target);
189                    }
190                    if (currentLoadingPreviews.size <= 0 && event.detail.visible == true) {
191                        //All visible models have loaded. Trigger autostart
192                        for (let xy = 0; xy < modelPreviews.length; xy++) {
193                            if(modelPreviews[xy].viewer != null) {
194                                modelPreviews[xy].viewer.removeEventListener("progress", detectPreviewLoading);
195                                modelPreviews[xy].viewer.removeEventListener("model-visibility", detectPreviewLoadingFinished);
196                            }
197                        }
198                        queryPopup.closePopup();
199                        if (!ignoreAutostart) {
200                            startModelSession(autoStartButton);
201                        }
202                        event.target.removeEventListener('model-visibility', detectPreviewLoadingFinished);//Remove itself as a listener
203                    }
204                }
205                for (let xy = 0; xy < modelPreviews.length; xy++) {
206                    modelPreviews[xy].viewer.addEventListener("progress", detectPreviewLoading);
207                    modelPreviews[xy].viewer.addEventListener("model-visibility", detectPreviewLoadingFinished);
208                }
209            } else {
210                //No 3D Preview on the website
211                if(!ignoreAutostart) {
212                    startModelSession(autoStartButton);
213                }
214            }
215        } else {
216            console.warn("[PausAR]: Autostart failed. The ar scene is not fully configured.");
217        }
218    }
219
220
221}//init
222
223function startModelSession(ev) {
224
225    ignoreAutostart = true;
226
227    let arButton = null
228
229    if(typeof ev.getAttribute === 'function') {
230        if(typeof ev.getAttribute("pausar") === "string") {
231            arButton = ev;
232        } else {
233            return ;
234        }
235    } else {
236        arButton = getTargetButton(ev.target);
237    }
238
239    if(arButton == null) {
240        return ;
241    }
242    
243    let widgetID = arButton.getAttribute("pausar");
244    if(!widgetID) {
245        return ;
246    }
247
248    if(arButton.getAttribute('editmode') == 'true' || arButton.getAttribute('editmode') == true) {
249        console.info("[PausAR] AR Session could not be started inside the Editor");
250        return ;
251    }
252
253    queryPopup.cloneColor(arButton);
254
255    if(arButton.getAttribute('paus_iframe') !== null) {
256        queryPopup.setFullscreen();
257        let iframeHostLink = arButton.getAttribute('paus_iframe');
258        if(typeof iframeHostLink === 'string' && iframeHostLink != "") {
259            if(QueryList.validateURL) {
260                iframeHostLink = QueryList.validateURL(decodeURIComponent(iframeHostLink));
261            } else {
262                iframeHostLink = decodeURIComponent(iframeHostLink);
263            }
264            if(typeof iframeHostLink === 'string') {
265                queryPopup.setAnchor(arButton, iframeHostLink);//Overwrite Anchor for usage inside an iframe
266            } else {
267                queryPopup.setAnchor(arButton);
268            }
269        } else {
270            queryPopup.setAnchor(arButton);
271        }
272    } else {
273        queryPopup.setAnchor(arButton);
274    }    
275    
276    //Query
277    
278    if(QueryList.isAndroid()) {
279        if(QueryList.getAndroidVersion() >= 7) {
280            //AR Core Device (Android)
281            
282            if(QueryList.isFirefox()) {
283                queryPopup.openPopup("missingSupportAndroid");
284            } else {
285                startSceneViewer();
286            }
287        } else {
288            //Android Device without AR Core (no support) (not reachble)
289            queryPopup.openPopup("missingSupport");
290        }
291    } else {
292        if(QueryList.isIOS()) {
293            //IOSDevice
294            if(QueryList.isLinkedIn() || QueryList.isInstagram() || QueryList.isFacebook()) {
295                queryPopup.openPopup("missingSupportQuicklookSocial");
296            } else {
297                //iOS nonSocialBrowsers
298                //if(QueryList.isQuicklook()) {
299                    //Quicklook
300                    startQuicklook();
301                //} else {
302                //    //nonQuicklook (not reachable)
303                //    queryPopup.openPopup("missingSupportQuicklook");
304                //}
305            }
306            
307        } else {
308            if(QueryList.isDesktop() || !QueryList.hasTouchScreen()) {
309                //Desktop Hint (QR Code)
310                queryPopup.openPopup("desktop");
311            } else {
312                //Windows-Phones, misc Handheld devices, etc. (not reachable)
313                //queryPopup.setPopupState();
314                queryPopup.openPopup("missingSupport");
315            }
316        }
317    }
318    
319
320     //internal Helpers
321     
322     function startSceneViewer() {
323
324        let androidModelFile = (arButton.getAttribute("paus_glb") != null && arButton.getAttribute("paus_glb") != "") ? "file=" + arButton.getAttribute("paus_glb") : "";
325        let androidModelTitle = (arButton.getAttribute("paus_scene_title") != null && arButton.getAttribute("paus_scene_title") != "") ? "title=" + (arButton.getAttribute("paus_scene_title")) + "&" : "";   //UserInput -> Encoded
326        let androidModelLink = (arButton.getAttribute("paus_scene_link") != null && arButton.getAttribute("paus_scene_link") != "") ? (arButton.getAttribute("paus_scene_link")) : "";                        //UserInput -> Encoded
327        let androidModelResizable = (arButton.getAttribute("paus_scene_resizable") == true || arButton.getAttribute("paus_scene_resizable") == 'true' || arButton.getAttribute("paus_scene_resizable") == 1) ? "resizable=true&" : "resizable=false&";
328        let verticalPlacement = arButton.getAttribute("paus_scene_placement") == 'vertical' ? "enable_vertical_placement=true&" : "enable_vertical_placement=false&";
329        let androidSoundFile = (arButton.getAttribute("paus_scene_sound") != null && arButton.getAttribute("paus_scene_sound") != "") ? ("sound=" + arButton.getAttribute("paus_scene_sound") + "&") : "";
330        let androidModelOcclusion = (arButton.getAttribute("paus_scene_occlusion") == true || arButton.getAttribute("paus_scene_occlusion") == 'true' || arButton.getAttribute("paus_scene_occlusion") == 1) ? "disable_occlusion=false" : "disable_occlusion=true";
331
332        if(androidModelFile == "") {
333            return ;
334        }
335
336        //Validate the CallToAction Link:
337        
338        //===================================
339        //temporarily removed, due to an error when using the “contains” condition. Naive validation is therefore preferred for the URL.
340        //-----------------------------------
341        /*
342        if(QueryList.validateURL) {
343            androidModelLink = QueryList.validateURL(decodeURIComponent(androidModelLink), false, true);
344            if(androidModelLink != null) {
345                androidModelLink = "link=" + encodeURIComponent(androidModelLink) + "&";
346            } else {
347                androidModelLink = "";
348            }
349        } else {
350            androidModelLink = "";
351        }
352        */
353        //-----------------------------------
354        //temporary replaced with naive validation (add protocol, if missing from string)
355        //-----------------------------------
356        androidModelLink = decodeURIComponent(androidModelLink);
357        if(androidModelLink.indexOf('http://') != 0 && androidModelLink.indexOf('https://') != 0) {
358            //adding missing https:// protocol
359            androidModelLink = "https://" + androidModelLink;
360        }
361        if(QueryList.validateURL) {
362            androidModelLink = QueryList.validateURL(androidModelLink, false, true);
363            if(androidModelLink != null && androidModelLink != "") {
364                androidModelLink = "link=" + androidModelLink + "&";
365            } else {
366                androidModelLink = "";
367            }
368        } else {
369            androidModelLink = "";
370        }
371        //===================================
372        
373        const intentURL = "arvr.google.com/scene-viewer/1.1";
374        const mode = "mode=ar_preferred";
375        const scheme = "scheme=https";
376        const svPackage = "package=com.google.ar.core";//"package=com.google.android.googlequicksearchbox;";
377        const action = "action=android.intent.action.VIEW";
378        const fallbackURL = "https://play.google.com/store/apps/details?id=com.google.ar.core&pcampaignid=web_share";
379
380        //Link
381        let intentLink =
382            "intent://" +
383            intentURL + "?" +
384            androidModelFile + "&" +
385            mode + "&" +
386            androidModelTitle + //& included
387            androidModelLink + //& included
388            androidModelResizable + //& included
389            verticalPlacement + //& included
390            androidSoundFile + //& included
391            androidModelOcclusion +
392            "#Intent;" +
393            scheme + ";" +
394            svPackage + ";" +
395            action + ";" +
396            "end;";
397            window.location.href = intentLink;
398    }
399
400    function startQuicklook() {
401
402        let iosModelFile = (arButton.getAttribute("paus_ios") != null && arButton.getAttribute("paus_ios") != "") ? arButton.getAttribute("paus_ios") : "";
403        let iOSModelTitle = (arButton.getAttribute("paus_scene_title") != null && arButton.getAttribute("paus_scene_title") != "") ? "checkoutTitle=" + (arButton.getAttribute("paus_scene_title")) : "";                           //UserInput -> Encoded
404        let iOSModelDescription = (arButton.getAttribute("paus_scene_description") != null && arButton.getAttribute("paus_scene_description") != "") ? "checkoutSubtitle=" + (arButton.getAttribute("paus_scene_description")) : "";//UserInput -> Encoded
405        let iOSModelLink = (arButton.getAttribute("paus_scene_link") != null && arButton.getAttribute("paus_scene_link") != "") ? arButton.getAttribute("paus_scene_link") : "";                                                    //UserInput -> Encoded
406        let buttonText = (arButton.getAttribute("paus_scene_cta") != null && arButton.getAttribute("paus_scene_cta") != "") ? "callToAction=" + (arButton.getAttribute("paus_scene_cta")) : "callToAction=Visit";                   //UserInput -> Encoded
407        let iosModelResizable = (arButton.getAttribute("paus_scene_resizable") == true || arButton.getAttribute("paus_scene_resizable") == 'true' || arButton.getAttribute("paus_scene_resizable") == 1) ? "allowsContentScaling=1" : "allowsContentScaling=0";
408        if(iosModelFile == "") {
409            return ;
410        }
411
412        //Validate the CallToAction Link:
413
414        //===================================
415        //temporarily removed, due to an error when using the “contains” condition. Naive validation is therefore preferred for the URL.
416        //-----------------------------------
417        /*
418        if(QueryList.validateURL) {
419            iOSModelLink = QueryList.validateURL(decodeURIComponent(iOSModelLink), false, true);
420            if(iOSModelLink == null) {
421                buttonText = "callToAction=Close";
422            }
423        } else {
424            buttonText = "callToAction=Close";
425        }
426        */
427        //-----------------------------------
428        //temporary replaced with naive validation (add protocol, if missing from string)
429        //-----------------------------------
430        iOSModelLink = decodeURIComponent(iOSModelLink);
431        if(iOSModelLink.indexOf('http://') != 0 && iOSModelLink.indexOf('https://') != 0) {
432            //adding missing https:// protocol
433            iOSModelLink = "https://" + iOSModelLink;
434        }
435        if(QueryList.validateURL) {
436            iOSModelLink = QueryList.validateURL(iOSModelLink, false, true);
437            if(iOSModelLink == null) {
438                buttonText = "callToAction=Close";
439            }
440        } else {
441            iOSModelLink = null;
442            buttonText = "callToAction=Close";
443        }
444        //===================================
445                
446        let filename = iosModelFile;
447
448        for(let i = filename.length-1; i >= 0; i--) {
449            if(filename[i] == "/" || filename[i] == "\\") {
450                filename = filename.substring(i+1, filename.length);
451                break;
452            }    
453        }
454
455        let usdzURL = 
456        iosModelFile            +"#"+
457        iosModelResizable       +"&"+
458        iOSModelTitle           +"&"+
459        iOSModelDescription     +"&"+
460        buttonText;
461        
462        let downloadElement = null;
463        downloadElement = document.querySelector(".pausAR-UI-HiddenButtonQuicklook[pausar=" + arButton.getAttribute("pausar") + "]");
464
465        if(downloadElement == null || typeof downloadElement === 'undefined') {
466            downloadElement = document.createElement("a");
467            downloadElement.className = "pausAR-UI-HiddenButtonQuicklook";
468            downloadElement.setAttribute("pausar", arButton.getAttribute("pausar"));
469            downloadElement.style.display = "none";
470            downloadElement.style.zIndex = -1;
471            downloadElement.style.position = "fixed";
472            downloadElement.style.left = -1000;
473            downloadElement.style.top = -1000;
474            downloadElement.style.visibility = "hidden";
475            downloadElement.style.height = 0;
476            downloadElement.style.width = 0;
477            downloadElement.style.opacity = 0;
478
479            arButton.parentNode.appendChild(downloadElement);
480            downloadElement.appendChild(document.createElement("img"));
481            
482        }
483        downloadElement.addEventListener("message", customCallToAction, false);
484        downloadElement.setAttribute("rel", "ar");
485        downloadElement.href = usdzURL;
486        downloadElement.download = filename;
487        downloadElement.dispatchEvent(
488            new MouseEvent('click', {
489                bubbles: true,
490                cancelable: true,
491                view: window
492            })
493        );
494        
495        function customCallToAction(event) {
496            if (event.data == "_apple_ar_quicklook_button_tapped") {
497                if(typeof iOSModelLink === 'string' && iOSModelLink != "") {
498
499                    if(arButton.getAttribute('paus_iframe') !== null) {
500                        //iframe mode
501                        if (typeof top === 'object') {
502                            try {
503                                top.window.location.href = iOSModelLink;
504                                return ;
505                            } catch (err) {
506                                console.warn("[PausAR]: iframe could not open link in the top window.");                                
507                            }
508
509                        }
510                        if (window.parent == window.top) {//iframe is embedded directly on the host page and not nested in other iframes
511                            try {
512                                window.parent.location.href = iOSModelLink;
513                                return ;
514                            } catch (err) {
515                                console.warn("[PausAR]: iframe could not open link in the parent window.");
516                            }
517                        }
518                    } else {
519                        //regular
520                        window.location.href = iOSModelLink;
521                    }
522                    
523                }
524                //downloadElement.remove();
525            }
526        }
527    }
528
529}
530//===
531
532
533//==================================================================
534// Helpers
535//==================================================================
536
537
538/**
539 * Recursively searches (upwards) for the reference to the selected AR button.
540 * Clicking on an icon or label does not return the correct target-element.
541 * @param {Element} target Target element that started the event.
542 * @returns 
543 */
544function getTargetButton(target) {
545
546    let bodyReached = false;
547    do {
548        for(let i = 0; i < startARButtons.length; i++) {
549            if(target === startARButtons[i]) {
550                return target;
551            }
552        }
553        if(target.parentNode) {
554            if(typeof target.parentNode === 'object') {
555                target = target.parentNode;
556                if(target === document.body) {
557                    bodyReached = true;
558                }
559            } else {
560                bodyReached = true;
561            }
562        } else {
563            bodyReached = true;
564        }
565    } while(!bodyReached);
566    return null;
567}
568
569/**
570 * Recursively searches for the reference to the 3D preview of the selected AR button.
571 * @param {Element} target AR Button (DOM element) inside the pausAR widget
572 * @returns 
573 */
574function getTargetPreview(target) {
575    //Anchor
576    if(typeof target !== 'object') {
577        return null;
578    }
579    let validTarget = false;
580    for(let i = 0; i < startARButtons.length; i++) {
581        if(target === startARButtons[i]) {
582            validTarget = true;
583        }
584    }
585    if(!validTarget) {
586        return null;
587    }
588    //---
589    let arButton = null;
590    let currentWidgetID;
591    arButton = target;
592    currentWidgetID = arButton.getAttribute('pausar');
593
594    //get widget
595    let widgetContainer = null;
596    do {
597        for(let j = 0; j < target.classList.length; j++) {
598            if(target.classList[j] == "pausAR-UI-widgetContainer") {
599                widgetContainer = target;
600                break;
601            }
602        }
603        
604        if(target.parentNode) {
605            if(typeof target.parentNode === 'object') {
606                target = target.parentNode;
607                if(target === document.body) {
608                    return null;
609                }
610            } else {
611                return null;
612            }
613        } else {
614            return null;
615        }
616    } while(widgetContainer == null);
617
618    let modelPreview = widgetContainer.querySelector("model-viewer[pausar=" + currentWidgetID + "]");
619    if(modelPreview != null) {
620        return modelPreview;
621    }
622    let imagePreview = widgetContainer.querySelector(".pausAR-UI-previewContainer");
623    if(imagePreview != null) {
624        return imagePreview;
625    }
626    return null;
627    
628}
629
630/**
631 * Checks all file paths (Android, Preview and iOS) for validity and corrects errors in specifying the protocols if the path points to the current host page.
632 * @param {Element} arButton DOM reference of a the AR Button
633 * @returns 
634 */
635function validatePaths(arButton) {
636
637    if(typeof arButton !== 'object') {
638        return ;
639    }
640    let validTarget = false;
641    for(let i = 0; i < startARButtons.length; i++) {
642        if(arButton === startARButtons[i]) {
643            validTarget = true;
644        }
645    }
646    if(!validTarget) {
647        return null;
648    }
649    
650    if(arButton.getAttribute('editmode') == 'true' || arButton.getAttribute('editmode') == true) {
651        return ;
652    }
653    //---
654
655    if(typeof QueryList.validateModelPath === 'function') {
656        //paus_glb
657        let pausAndroidSrc = arButton.getAttribute('paus_glb');
658        if(typeof pausAndroidSrc === "string") {
659            arButton.setAttribute('paus_glb', QueryList.validateModelPath(pausAndroidSrc));
660        }
661
662        //paus_ios
663        let pausIosSrc = arButton.getAttribute('paus_ios');
664        if(typeof pausIosSrc === "string") {
665            arButton.setAttribute('paus_ios', QueryList.validateModelPath(pausIosSrc, true));
666        }
667
668        //Preview src
669        let pausPreviewElement = getTargetPreview(arButton);
670        if(pausPreviewElement != null && typeof pausPreviewElement !== 'undefined') {
671            if(pausPreviewElement.tagName == 'MODEL-VIEWER') {
672                let pausPreviewSrc = pausPreviewElement.getAttribute("src");
673                if(typeof pausPreviewSrc === "string") {
674                    pausPreviewElement.setAttribute('src', QueryList.validateModelPath(pausPreviewSrc));
675                }
676            }
677        }
678
679    }
680}
681
682/**
683 * Removes the element with the specified index from an array
684 * @param {Array} arr The corresponding array
685 * @param {Number} index Index of the element to be removed
686 * @returns {Array | undefined} The reduced array | Invalid parameters return nothing
687 */
688function removeArrayElement(arr, index) {
689
690    if(typeof arr === 'undefined' || arr == null) {
691        return ;
692    }
693    if(typeof arr.length !== 'number' || typeof arr === "string") {
694        return ;
695    }
696    if(typeof index !== 'number') {
697        return arr;
698    }
699    if(index < 0 || index > arr.length) {
700        return arr;
701    }  
702  
703    return arr.slice(0, index).concat(arr.slice(index+1, arr.length));
704    
705  }
706
707//===================================
708// Fullscreen Functions
709//===================================
710
711
712async function toggleFullscreen(container, targetButton) {
713
714
715    if(fullscreenTransition == true) {
716        //console.warn("[PausAR]: Fullscreen in transition");
717        return ;
718    }
719
720    let popupInstance = document.querySelector(".pausAR-UI-popupContainerDialog");
721    if(popupInstance) {
722        return ;
723    }
724
725    if(typeof container === 'undefined' || container == null) {return ;}
726    if(typeof targetButton === 'undefined' || targetButton == null) {return ;}
727    if(container.getAttribute('editmode') == 'true' || container.getAttribute('editmode') == true) {return ;}
728    if(container.getAttribute('paus_iframe') !== null) {return ;}
729    
730    fullscreenTransition = true;
731    
732    if(isWidgetFullscreenActive(container)) {//Deactivate fullscreen
733        
734        if(isFullscreenClassActive()) {
735            closeFullscreenClasses();
736        }
737        if(isFullscreenAPIActive()) {
738            await closeFullscreenAPI();
739        }
740
741        loadViewers();
742        changePanY(container);
743        
744        fullscreenTransition = false;
745
746    } else {//Activate (another) fullscreen
747
748        //Check, if other widgets are in fullscreen mode
749        if(isFullscreenClassActive()) {
750            closeFullscreenClasses();
751        }
752        if(isFullscreenAPIActive()) {
753            await closeFullscreenAPI();
754        }
755        let fullScreenActivation = await openFullscreenAPI(container);
756        if(fullScreenActivation == false) {
757            openFullscreenClasses(container);//Fallback, if fullscreen API is not available
758        } else if(fullScreenActivation == true) {
759            openFullscreenClasses(container, true);//Fullscreen API active. Just add the classes, without the 'fallback'.
760        } else {
761            //console.error("[PausAR]: No container or widget was given for the fullscreen request");
762            fullscreenTransition = false;
763            return ;
764        }
765
766        unloadViewers(container);
767        changePanY(container, true);
768
769        fullscreenTransition = false;
770
771    }
772}
773
774/**
775 * Checks if the given widget Container contains the 'fullscreen' class.
776 * @param {Element} widgetElement Reference to the widget container
777 * @returns 
778 */
779function isWidgetFullscreenActive(widgetElement) {
780    if(typeof widgetElement === 'undefined' || widgetElement == null) {
781        return false;
782    }
783    //
784    for(let j = 0; j < widgetElement.classList.length; j++) {
785        if(widgetElement.classList[j] == "pausAR-UI-widgetFullscreen") {
786            return true;
787        }
788    }
789    return false;
790}
791
792function isFullscreenClassActive() {
793    for(let i = 0; i < widgetContainers.length; i++) {
794        for(let j = 0; j < widgetContainers[i].classList.length; j++) {
795            if(widgetContainers[i].classList[j] == "pausAR-UI-widgetFullscreen") {
796                return true;
797            }
798        }
799    }
800    return false;
801}
802
803function isFullscreenAPIActive() {
804    
805    if(document.fullscreenElement) {
806        if(document.fullscreenElement.getAttribute("pausar") != null) {
807            return true;
808        }
809    }
810    if(document.webkitFullscreenElement) {
811        if(document.webkitFullscreenElement.getAttribute("pausar") != null) {
812            return true;
813        }
814    }
815    if(document.msFullscreenElement) {
816        if(document.msFullscreenElement.getAttribute("pausar") != null) {
817            return true;
818        }
819    }
820    return false;
821}
822
823/**
824 * Removes all fullscreen classes from all widgets and removes the eventListeners.
825 */
826function closeFullscreenClasses() {
827    for(let i = 0; i < widgetContainers.length; i++) {
828        for(let j = 0; j < widgetContainers[i].classList.length; j++) {
829            if(widgetContainers[i].classList[j] == "pausAR-UI-widgetFullscreen") {
830                widgetContainers[i].classList.remove("pausAR-UI-widgetFullscreen");
831                break;
832            }
833        }
834    }
835
836    if(typeof originalOverflow === 'string') {
837        document.body.style.overflow = originalOverflow;
838        originalOverflow = null;
839    }
840    if(typeof originalTouchAction === 'string') {
841        document.body.style.touchAction = originalTouchAction;
842        originalTouchAction = null;    
843    }
844
845
846    if(fullscreenFallbackStyles.isConnected) {//Remove additional Stylesheet for the fallback
847        fullscreenFallbackStyles.remove();//Remove element from DOM
848        if(typeof fullscreenFallbackStyles.sheet !== 'undefined' && fullscreenFallbackStyles.sheet != null) {
849            let currentRules = fullscreenFallbackStyles.sheet.rules;
850            for(let r = 0; r < currentRules.length; r++) {
851                if(typeof fullscreenFallbackStyles.sheet.removeRule === 'function') {
852                    fullscreenFallbackStyles.sheet.removeRule(currentRules[r]);
853                } else if(typeof fullscreenFallbackStyles.sheet.deleteRule === 'function') {
854                    fullscreenFallbackStyles.sheet.deleteRule(currentRules[r]);
855                }
856            }
857        }        
858        
859    }
860
861    if(escapeListener != null) {
862        document.removeEventListener('keydown', escapeListener);
863        escapeListener = null;
864    }
865}
866
867/**
868 * Async Function.
869 * Closes all active fullscreen widgets and removes the eventListeners.
870 */
871async function closeFullscreenAPI() {
872    if(document.fullscreenElement || document.webkitFullscreenElement || document.msFullscreenElement) {
873
874        if (document.exitFullscreen) {
875            await document.exitFullscreen();
876        } else if (document.webkitExitFullscreen) { /* Safari */
877            await document.webkitExitFullscreen();
878        } else if(document.webkitCancelFullScreen) { /* Safari (alternative) */
879            await document.webkitCancelFullScreen();
880        } else if (document.msExitFullscreen) { /* IE11 */
881            await document.msExitFullscreen();
882        }
883
884    }
885
886    if(fullscreenListener != null) {
887        document.removeEventListener('fullscreenchange', fullscreenListener);
888        document.removeEventListener('webkitfullscreenchange', fullscreenListener);
889        fullscreenListener = null;
890    }
891}
892
893function openFullscreenClasses(widget, ignoreEvents) {
894
895    if(typeof widget === 'undefined' || widget == null) {
896        return ;
897    }
898    //
899    widget.classList.add('pausAR-UI-widgetFullscreen');
900
901    originalOverflow = document.body.style.overflow;
902    originalTouchAction = document.body.style.touchAction;
903    //document.body.style.overflow = "hidden";
904    //document.body.style.touchAction = "none";
905    document.body.style.setProperty("overflow", "hidden", "important");
906    document.body.style.setProperty("touch-action", "none", "important");
907
908    if(!ignoreEvents) {//Fallback
909        if (!fullscreenFallbackStyles.isConnected) {
910            document.head.appendChild(fullscreenFallbackStyles);
911            //fullscreenFallbackStyles.textContent = "";
912        }
913        //z-index reset for all other elements, that not belong to the current widget
914        fullscreenFallbackStyles.sheet.insertRule('body *:not(.pausAR-UI-widgetContainer.pausAR-UI-widgetFullscreen[pausar='+widget.getAttribute("pausar")+'], .pausAR-UI-widgetContainer.pausAR-UI-widgetFullscreen[pausar='+widget.getAttribute("pausar")+'] * ) {\
915            z-index: unset !important;\
916        }');
917    }    
918    //-------------------
919
920    //Events
921    if (escapeListener == null && !ignoreEvents) {
922        escapeListener = async function (evt) {
923            evt = evt || window.event;
924            var isEscape = false;
925            if ("key" in evt) {
926                isEscape = (evt.key === "Escape" || evt.key === "Esc");
927            } else {
928                isEscape = (evt.keyCode === 27);
929            }
930            if (isEscape) {
931                //Close Fullscreen
932                if(fullscreenTransition == true) {
933                    return ;
934                }
935                fullscreenTransition = true;
936                if(isFullscreenClassActive()) {
937                    closeFullscreenClasses();
938                }
939                if(isFullscreenAPIActive()) {
940                    await closeFullscreenAPI();
941                }
942                loadViewers();
943                fullscreenTransition = false;
944            }
945        };
946        document.addEventListener("keydown", escapeListener);
947    }
948}
949
950async function openFullscreenAPI(widget) {
951    return new Promise(async function(resolve) {
952        if(typeof widget === 'undefined' || widget == null) {
953            resolve("no widget found");
954            return ;
955        }
956        //Check for API availability or allready activated fullscreen
957        if (document.fullscreenEnabled || document.webkitFullscreenEnabled || document.msFullscreenEnabled) {
958            if (typeof widget.requestFullscreen === 'function') {
959                try {
960                    await widget.requestFullscreen();
961                } catch (error) {
962                    resolve(false);
963                    return ;
964                }
965                fullscreenAPIRequested();
966            } else if (typeof widget.webkitRequestFullscreen === 'function') { /* Safari */
967                try {
968                    await widget.webkitRequestFullscreen();
969                } catch (error) {
970                    resolve(false);
971                    return ;
972                }
973                fullscreenAPIRequested("webkit");
974            } else if (typeof widget.msRequestFullscreen === 'function') { /* IE11 */
975                try {
976                    await widget.msRequestFullscreen();
977                } catch (error) {
978                    resolve(false);
979                    return ;
980                }
981                fullscreenAPIRequested();
982            } else {
983                console.warn("No Fullscreen API found");
984                resolve(false);//No Fullscreen API found
985                return ;
986            }
987            resolve(true);
988            return ;
989            //Listener
990            function fullscreenAPIRequested(apiType) {
991                /*//Lock orientation to landscape
992                if(typeof screen !== 'undefined' || screen != null) {
993                    if(typeof screen.oriantation !== 'undefined' || screen.orientation != null) {
994                        if(typeof screen.orientation.lock === 'function') {
995                            screen.orientation.lock('landscape-primary').catch(function() {console.warn("[PausAR] Screen.orientation.lock is not supported");});
996                        }
997                    }
998                }
999                */
1000                
1001                if(fullscreenListener == null) {
1002                    fullscreenListener = async function() {
1003                        if(!document.fullscreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement) { //fullscreen API closed
1004                            //Clean up style changes
1005                            if(isFullscreenClassActive()) {
1006                                closeFullscreenClasses();
1007                            }
1008                            loadViewers();
1009                            fullscreenTransition = false;
1010                        }
1011                    };
1012                    switch (apiType) {
1013                        case "webkit":
1014                            document.addEventListener('webkitfullscreenchange', fullscreenListener);
1015                            break;
1016                    
1017                        default:
1018                            document.addEventListener('fullscreenchange', fullscreenListener);
1019                            break;
1020                    }   
1021                }            
1022            }
1023        }
1024        //No Fullscreen API found or allready requested
1025        resolve(false);
1026        return ;
1027    });
1028}
1029
1030function unloadViewers(widget) {
1031    if(typeof widget === 'undefined' || widget == null) {
1032        return ;
1033    }
1034    //
1035    for(let i = 0; i < modelPreviews.length; i++) {
1036        if(widget != modelPreviews[i].widget) {
1037            modelPreviews[i].viewer.showPoster();
1038        } else if(modelPreviews[i].viewer.loaded ) {
1039            modelPreviews[i].viewer.dismissPoster();
1040        }
1041    }
1042}
1043
1044function loadViewers() {
1045    for(let i = 0; i < modelPreviews.length; i++) {
1046        modelPreviews[i].viewer.dismissPoster();
1047    }
1048}
1049
1050function changePanY(widget, remove) {
1051    if(typeof widget === 'undefined' || widget == null) {
1052        return ;
1053    }
1054    if(typeof remove !== 'boolean') {
1055        remove = false;
1056    }
1057    //
1058    for(let i = 0; i < modelPreviews.length; i++) {
1059        if(widget == modelPreviews[i].widget) {            
1060            if(remove) {
1061                modelPreviews[i].touchAction = modelPreviews[i].viewer.getAttribute("touch-action");
1062                modelPreviews[i].viewer.removeAttribute("touch-action");
1063            } else if(modelPreviews[i].touchAction != null) {
1064                modelPreviews[i].viewer.setAttribute("touch-action", modelPreviews[i].touchAction);
1065            }
1066        }
1067    }
1068}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.