PluginProbe
WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress / 9.2.0
WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress v9.2.0
9.2.0 9.1.4 9.1.3 9.1.2 9.1.1 9.1.0 9.0.3 9.0.2 9.0.1 9.0.0 8.5.79 8.5.78 8.5.77 8.5.76 8.5.75 8.5.74 8.5.73 8.5.72 8.5.71 8.5.70 8.5.69 8.5.68 8.5.35 8.5.36 8.5.37 All 224 releases
wpvr / legacy / public / js / wpvr-accessibility.js

wpvr-accessibility.js in WPVR – 360 Panorama viewer and Virtual Tour Builder for WordPress 9.2.0, at legacy/public/js/wpvr-accessibility.js

767 lines 26.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function ($) {
2 'use strict';
3
4 var defaults = {
5 tour: 'Virtual tour',
6 instructions: 'Use the arrow keys or W, A, S, and D to look around. Use Tab to reach tour controls and hotspots.',
7 load: 'Load virtual tour',
8 hotspot: 'Tour hotspot',
9 scene: 'View scene',
10 scene_menu: 'Scene menu',
11 previous_scene: 'Previous scene',
12 next_scene: 'Next scene',
13 pan_up: 'Look up',
14 pan_down: 'Look down',
15 pan_left: 'Look left',
16 pan_right: 'Look right',
17 zoom_in: 'Zoom in',
18 zoom_out: 'Zoom out',
19 fullscreen: 'Toggle fullscreen',
20 home: 'Return to starting scene',
21 orientation: 'Toggle device orientation',
22 audio: 'Toggle tour audio',
23 gallery: 'Toggle scene gallery',
24 explainer: 'Open tour video',
25 form: 'Open form',
26 floor_plan: 'Open floor plan',
27 floor_plan_point: 'View scene from floor plan',
28 company_info: 'Company information',
29 close: 'Close dialog',
30 dialog: 'Tour dialog'
31 };
32 var labels = $.extend({}, defaults, window.wpvrAccessibility || {});
33 var dialogSelector = [
34 '.explainer',
35 '.wpvr-generic-form',
36 '.wpvr-floor-map',
37 '.custom-ifram-wrapper',
38 '.wpvr-hotspot-tweak-contents-wrapper'
39 ].join(',');
40 var controlSelector = [
41 '.pnlm-load-button',
42 '.pnlm-zoom-in',
43 '.pnlm-zoom-out',
44 '.pnlm-fullscreen-toggle-button',
45 '.pnlm-orientation-button',
46 '.ctrl',
47 '.explainer_button',
48 '.fullscreen-button',
49 '.vrgcontrols',
50 '.generic_form_button',
51 '.floor_map_button',
52 '.custom-scene-navigation',
53 '.scene-navigation-list',
54 '.floor-plan-pointer',
55 '.scctrl',
56 '.wpvr_owl_prev',
57 '.wpvr_owl_next',
58 '.close-explainer-video',
59 '.close-generic-form',
60 '.close-floor-map-plan',
61 '.cross',
62 '.cp-logo-ctrl'
63 ].join(',');
64 var focusableSelector = [
65 'a[href]',
66 'button:not([disabled])',
67 'input:not([disabled]):not([type="hidden"])',
68 'select:not([disabled])',
69 'textarea:not([disabled])',
70 'iframe',
71 '[tabindex]:not([tabindex="-1"])'
72 ].join(',');
73 var enhancedControls = new WeakSet();
74 var enhancedDialogs = new WeakSet();
75 var externalHotspotLinks = new WeakMap();
76 var dialogStates = new WeakMap();
77 var pendingOpeners = new WeakMap();
78 var movementStates = new WeakMap();
79 var tooltipIndex = 0;
80 var scanQueued = false;
81
82 function isVisible(element) {
83 if (!element || element.hidden) {
84 return false;
85 }
86
87 var current = element;
88 while (current && current.nodeType === 1) {
89 var style = window.getComputedStyle(current);
90 if (style.display === 'none' || style.visibility === 'hidden') {
91 return false;
92 }
93 current = current.parentElement;
94 }
95 return true;
96 }
97
98 function getTour(element) {
99 return element && element.closest ? element.closest('.pano-wrap') : null;
100 }
101
102 function getViewer(container) {
103 if (!container || !window.wpvrViewers) {
104 return null;
105 }
106 return window.wpvrViewers[container.id] || null;
107 }
108
109 function instructionsId(container) {
110 return container.id + '-keyboard-instructions';
111 }
112
113 function enhanceContainer(container) {
114 if (!container.id || container.getAttribute('data-wpvr-a11y') === 'true') {
115 return;
116 }
117
118 container.setAttribute('data-wpvr-a11y', 'true');
119 container.setAttribute('tabindex', '0');
120 container.setAttribute('role', 'region');
121 if (!container.getAttribute('aria-label')) {
122 container.setAttribute('aria-label', labels.tour);
123 }
124
125 var instructions = document.createElement('span');
126 instructions.id = instructionsId(container);
127 instructions.className = 'wpvr-a11y-instructions';
128 instructions.textContent = labels.instructions;
129 container.insertBefore(instructions, container.firstChild);
130 container.setAttribute('aria-describedby', instructions.id);
131 container.addEventListener('keydown', handleViewerKeydown, true);
132 container.addEventListener('keyup', handleViewerKeyup, true);
133 container.addEventListener('blur', stopViewerMovement, true);
134 container.addEventListener('focusin', keepRenderContainerAligned, true);
135 }
136
137 function keepRenderContainerAligned(event) {
138 var viewerContainer = event.currentTarget;
139 var renderContainer = event.target.closest
140 ? event.target.closest('.pnlm-render-container')
141 : null;
142
143 if (!viewerContainer || !renderContainer) {
144 return;
145 }
146
147 function resetScrollPosition() {
148 if (viewerContainer.scrollLeft || viewerContainer.scrollTop) {
149 viewerContainer.scrollLeft = 0;
150 viewerContainer.scrollTop = 0;
151 }
152 if (renderContainer.scrollLeft || renderContainer.scrollTop) {
153 renderContainer.scrollLeft = 0;
154 renderContainer.scrollTop = 0;
155 }
156 }
157
158 resetScrollPosition();
159 window.requestAnimationFrame(resetScrollPosition);
160 window.setTimeout(resetScrollPosition, 0);
161 }
162
163 function handleViewerKeydown(event) {
164 var container = event.currentTarget;
165 if (event.target !== container || event.altKey || event.ctrlKey || event.metaKey) {
166 return;
167 }
168
169 var key = event.key.toLowerCase();
170 var viewer = getViewer(container);
171 var handled = true;
172 var step = event.shiftKey ? 15 : 5;
173
174 if (key === 'enter' || key === ' ') {
175 var loadButton = container.querySelector('.pnlm-load-button');
176 if (loadButton && isVisible(loadButton)) {
177 loadButton.click();
178 focusTourAfterLoad(container);
179 } else {
180 handled = false;
181 }
182 } else if (!viewer) {
183 handled = false;
184 } else if (isMovementKey(key)) {
185 stopAutoRotate(viewer);
186 startViewerMovement(container, key, event.shiftKey);
187 } else if (key === '+' || key === '=') {
188 stopAutoRotate(viewer);
189 viewer.setHfov(viewer.getHfov() - step, false);
190 } else if (key === '-' || key === '_') {
191 stopAutoRotate(viewer);
192 viewer.setHfov(viewer.getHfov() + step, false);
193 } else {
194 handled = false;
195 }
196
197 if (handled) {
198 event.preventDefault();
199 event.stopImmediatePropagation();
200 }
201 }
202
203 function isMovementKey(key) {
204 return key === 'arrowup' || key === 'w' ||
205 key === 'arrowdown' || key === 's' ||
206 key === 'arrowleft' || key === 'a' ||
207 key === 'arrowright' || key === 'd';
208 }
209
210 function movementState(container) {
211 var state = movementStates.get(container);
212 if (!state) {
213 state = {
214 keys: {},
215 fast: false,
216 frame: null,
217 lastFrame: null
218 };
219 movementStates.set(container, state);
220 }
221 return state;
222 }
223
224 function startViewerMovement(container, key, fast) {
225 var state = movementState(container);
226 state.keys[key] = true;
227 state.fast = fast;
228
229 if (state.frame !== null) {
230 return;
231 }
232
233 state.lastFrame = null;
234 state.frame = window.requestAnimationFrame(function move(timestamp) {
235 var viewer = getViewer(container);
236 var current = movementState(container);
237 var moving = Object.keys(current.keys).some(function (pressedKey) {
238 return current.keys[pressedKey];
239 });
240
241 if (!viewer || !moving || document.activeElement !== container) {
242 current.frame = null;
243 current.lastFrame = null;
244 return;
245 }
246
247 if (current.lastFrame === null) {
248 current.lastFrame = timestamp;
249 }
250 var elapsed = Math.min(timestamp - current.lastFrame, 50);
251 var distance = (current.fast ? 90 : 45) * elapsed / 1000;
252 var pitchDirection =
253 (current.keys.arrowup || current.keys.w ? 1 : 0) -
254 (current.keys.arrowdown || current.keys.s ? 1 : 0);
255 var yawDirection =
256 (current.keys.arrowright || current.keys.d ? 1 : 0) -
257 (current.keys.arrowleft || current.keys.a ? 1 : 0);
258
259 current.lastFrame = timestamp;
260 if (pitchDirection) {
261 viewer.setPitch(viewer.getPitch() + pitchDirection * distance, false);
262 }
263 if (yawDirection) {
264 viewer.setYaw(viewer.getYaw() + yawDirection * distance, false);
265 }
266 current.frame = window.requestAnimationFrame(move);
267 });
268 }
269
270 function handleViewerKeyup(event) {
271 var container = event.currentTarget;
272 var key = event.key.toLowerCase();
273 var state = movementStates.get(container);
274
275 if (key === 'shift' && state) {
276 state.fast = false;
277 return;
278 }
279 if (!isMovementKey(key) || !state) {
280 return;
281 }
282
283 state.keys[key] = false;
284 event.preventDefault();
285 event.stopImmediatePropagation();
286 }
287
288 function stopViewerMovement(event) {
289 var container = event.currentTarget || event;
290 var state = movementStates.get(container);
291 if (!state) {
292 return;
293 }
294 state.keys = {};
295 state.lastFrame = null;
296 if (state.frame !== null) {
297 window.cancelAnimationFrame(state.frame);
298 state.frame = null;
299 }
300 }
301
302 function stopAutoRotate(viewer) {
303 if (typeof viewer.stopAutoRotate === 'function') {
304 viewer.stopAutoRotate();
305 }
306 }
307
308 function focusTourAfterLoad(container) {
309 window.setTimeout(function () {
310 container.focus();
311 }, 50);
312 window.setTimeout(function () {
313 if (!container.contains(document.activeElement) || document.activeElement === document.body) {
314 container.focus();
315 }
316 }, 500);
317 }
318
319 function labelFor(element) {
320 var title = element.getAttribute('aria-label') || element.getAttribute('title');
321 if (title) {
322 return title;
323 }
324 if (element.matches('.pnlm-load-button')) {
325 return labels.load;
326 }
327 if (element.matches('.pnlm-zoom-in, [id^="zoom-in"]')) {
328 return labels.zoom_in;
329 }
330 if (element.matches('.pnlm-zoom-out, [id^="zoom-out"]')) {
331 return labels.zoom_out;
332 }
333 if (element.matches('.pnlm-fullscreen-toggle-button, [id^="fullscreen"], .fullscreen-button')) {
334 return labels.fullscreen;
335 }
336 if (element.matches('.pnlm-orientation-button, [id^="gyroscope"]')) {
337 return labels.orientation;
338 }
339 if (element.matches('[id^="pan-up"]')) {
340 return labels.pan_up;
341 }
342 if (element.matches('[id^="pan-down"]')) {
343 return labels.pan_down;
344 }
345 if (element.matches('[id^="pan-left"]')) {
346 return labels.pan_left;
347 }
348 if (element.matches('[id^="pan-right"]')) {
349 return labels.pan_right;
350 }
351 if (element.matches('[id^="backToHome"]')) {
352 return labels.home;
353 }
354 if (element.matches('.audio_control')) {
355 return labels.audio;
356 }
357 if (element.matches('.vrgcontrols')) {
358 return labels.gallery;
359 }
360 if (element.matches('.explainer_button')) {
361 return labels.explainer;
362 }
363 if (element.matches('.generic_form_button')) {
364 return labels.form;
365 }
366 if (element.matches('.floor_map_button')) {
367 return labels.floor_plan;
368 }
369 if (element.matches('.floor-plan-pointer')) {
370 return labels.floor_plan_point;
371 }
372 if (element.matches('.custom-scene-navigation')) {
373 return labels.scene_menu;
374 }
375 if (element.matches('.wpvr_owl_prev')) {
376 return labels.previous_scene;
377 }
378 if (element.matches('.wpvr_owl_next')) {
379 return labels.next_scene;
380 }
381 if (element.matches('.close-explainer-video, .close-generic-form, .close-floor-map-plan, .cross')) {
382 return labels.close;
383 }
384 if (element.matches('.cp-logo-ctrl')) {
385 return labels.company_info;
386 }
387 if (element.matches('.scctrl')) {
388 var sceneTitle = element.closest('li');
389 sceneTitle = sceneTitle ? sceneTitle.querySelector('.scene-title') : null;
390 return sceneTitle && sceneTitle.textContent.trim()
391 ? labels.scene + ': ' + sceneTitle.textContent.trim()
392 : labels.scene;
393 }
394 if (element.matches('.scene-navigation-list')) {
395 return labels.scene + ': ' + element.textContent.trim();
396 }
397 if (element.matches('.pnlm-hotspot-base, .pnlm-hotspot')) {
398 var hotspotText = element.textContent.replace(/\s+/g, ' ').trim();
399 return hotspotText ? hotspotText.slice(0, 160) : labels.hotspot;
400 }
401
402 var text = element.textContent.replace(/\s+/g, ' ').trim();
403 return text ? text.slice(0, 160) : labels.hotspot;
404 }
405
406 function isNativeControl(element) {
407 return /^(A|BUTTON|INPUT|SELECT|TEXTAREA)$/.test(element.tagName);
408 }
409
410 function prepareExternalHotspotLink(element) {
411 if (!element.matches('.pnlm-hotspot-base, .pnlm-hotspot')) {
412 return false;
413 }
414
415 if (element.matches('.wpvr-hs-sticker, .wpvr-hs--sticker') || element.querySelector('.wpvr-sticker-card, .wpvr-sticker-scale-wrapper')) {
416 return false;
417 }
418
419 var anchor = null;
420 if (element.parentElement && element.parentElement.matches('a[href]')) {
421 anchor = element.parentElement;
422 } else {
423 anchor = Array.prototype.find.call(element.children, function (child) {
424 return child.matches && child.matches('a[href]');
425 }) || null;
426 }
427
428 if (!anchor) {
429 return externalHotspotLinks.has(element);
430 }
431
432 externalHotspotLinks.set(element, {
433 url: anchor.href,
434 target: anchor.getAttribute('target') || '_self'
435 });
436
437 anchor.removeAttribute('href');
438 anchor.removeAttribute('target');
439 anchor.setAttribute('tabindex', '-1');
440 anchor.setAttribute('role', 'presentation');
441
442 element.addEventListener('click', activateExternalHotspotLink);
443 return true;
444 }
445
446 function activateExternalHotspotLink(event) {
447 var link = externalHotspotLinks.get(event.currentTarget);
448 if (!link || !link.url) {
449 return;
450 }
451
452 event.preventDefault();
453
454 var openInNewContext = link.target === '_blank' || event.ctrlKey || event.metaKey || event.shiftKey;
455 if (openInNewContext) {
456 var openedWindow = window.open(link.url, '_blank', 'noopener,noreferrer');
457 if (openedWindow) {
458 openedWindow.opener = null;
459 }
460 } else {
461 window.location.assign(link.url);
462 }
463 }
464
465 function enhanceControl(element) {
466 if (enhancedControls.has(element)) {
467 syncControlState(element);
468 return;
469 }
470
471 if (
472 (element.matches('.ctrl') &&
473 element.parentElement &&
474 element.parentElement.matches('.explainer_button, .floor_map_button')) ||
475 element.matches('.wpvr-hs-sticker, .wpvr-hs--sticker') ||
476 element.querySelector('.wpvr-sticker-card, .wpvr-sticker-scale-wrapper')
477 ) {
478 return;
479 }
480
481 enhancedControls.add(element);
482 element.classList.add('wpvr-a11y-control');
483 var isExternalHotspotLink = prepareExternalHotspotLink(element);
484 if (!isNativeControl(element)) {
485 element.setAttribute(
486 'role',
487 isExternalHotspotLink ? 'link' : 'button'
488 );
489 element.setAttribute('tabindex', '0');
490 element.addEventListener('keydown', activateControlFromKeyboard);
491 } else if (element.getAttribute('role') === 'presentation') {
492 element.removeAttribute('role');
493 }
494 if (!element.getAttribute('aria-label')) {
495 element.setAttribute('aria-label', labelFor(element));
496 }
497
498 associateControl(element);
499 associateTooltip(element);
500 syncControlState(element);
501 }
502
503 function activateControlFromKeyboard(event) {
504 if (event.key !== 'Enter' && event.key !== ' ') {
505 return;
506 }
507 event.preventDefault();
508 event.stopPropagation();
509 event.currentTarget.click();
510 }
511
512 function associateControl(element) {
513 var tour = getTour(element);
514 if (!tour || !tour.id) {
515 return;
516 }
517 var suffix = tour.id.replace(/^pano/, '');
518 var target = null;
519
520 if (element.matches('.explainer_button')) {
521 target = document.getElementById('explainer' + suffix);
522 } else if (element.matches('.generic_form_button')) {
523 target = document.getElementById('wpvr-generic-form' + suffix);
524 } else if (element.matches('.floor_map_button')) {
525 target = document.getElementById('wpvr-floor-map' + suffix);
526 } else if (element.matches('.custom-scene-navigation')) {
527 target = document.getElementById('custom-scene-navigation-nav' + suffix);
528 } else if (element.matches('.vrgcontrols')) {
529 target = document.getElementById('sccontrols' + suffix);
530 }
531
532 if (target && target.id) {
533 element.setAttribute('aria-controls', target.id);
534 element.setAttribute('aria-expanded', isVisible(target) ? 'true' : 'false');
535 }
536 }
537
538 function associateTooltip(element) {
539 if (!element.matches('.pnlm-hotspot-base, .pnlm-hotspot')) {
540 return;
541 }
542 var tooltip = element.querySelector(':scope > p');
543 if (!tooltip) {
544 return;
545 }
546 if (!tooltip.id) {
547 tooltipIndex += 1;
548 tooltip.id = 'wpvr-hotspot-tooltip-' + tooltipIndex;
549 }
550 element.setAttribute('aria-describedby', tooltip.id);
551 }
552
553 function syncControlState(element) {
554 if (element.matches('.audio_control')) {
555 element.setAttribute('aria-pressed', element.getAttribute('data-play') === 'on' ? 'true' : 'false');
556 }
557 if (element.matches('.scene-navigation-list, .floor-plan-pointer')) {
558 element.setAttribute(
559 'aria-current',
560 element.classList.contains('active') || element.classList.contains('add-pulse') ? 'true' : 'false'
561 );
562 }
563 if (element.matches('.scctrl')) {
564 var galleryItem = element.closest('.owl-item');
565 element.setAttribute('aria-current', galleryItem && galleryItem.classList.contains('clicked') ? 'true' : 'false');
566 }
567 if (element.matches('.pnlm-fullscreen-toggle-button, [id^="fullscreen"], .fullscreen-button')) {
568 var tour = getTour(element);
569 var fullscreenElement = document.fullscreenElement || document.webkitFullscreenElement;
570 element.setAttribute(
571 'aria-pressed',
572 fullscreenElement && (fullscreenElement === tour || fullscreenElement.contains(tour)) ? 'true' : 'false'
573 );
574 }
575 if (element.matches('.pnlm-orientation-button, [id^="gyroscope"]')) {
576 var orientationTour = getTour(element);
577 var orientationViewer = getViewer(orientationTour);
578 element.setAttribute(
579 'aria-pressed',
580 orientationViewer && typeof orientationViewer.isOrientationActive === 'function' &&
581 orientationViewer.isOrientationActive() ? 'true' : 'false'
582 );
583 }
584 if (element.hasAttribute('aria-controls')) {
585 var controlled = document.getElementById(element.getAttribute('aria-controls'));
586 if (controlled) {
587 element.setAttribute('aria-expanded', isVisible(controlled) ? 'true' : 'false');
588 }
589 }
590 }
591
592 function enhanceDialog(dialog) {
593 if (!enhancedDialogs.has(dialog)) {
594 enhancedDialogs.add(dialog);
595 dialog.classList.add('wpvr-a11y-dialog');
596 dialog.setAttribute('role', 'dialog');
597 dialog.setAttribute('aria-modal', 'true');
598 dialog.setAttribute('tabindex', '-1');
599 if (!dialog.getAttribute('aria-label')) {
600 dialog.setAttribute('aria-label', labels.dialog);
601 }
602 dialog.addEventListener('keydown', handleDialogKeydown);
603 }
604 syncDialog(dialog);
605 }
606
607 function syncDialog(dialog) {
608 var visible = isVisible(dialog);
609 var previous = dialogStates.get(dialog);
610 dialog.setAttribute('aria-hidden', visible ? 'false' : 'true');
611 dialogStates.set(dialog, visible);
612
613 if (visible && previous !== true) {
614 focusDialog(dialog);
615 } else if (!visible && previous === true) {
616 restoreDialogFocus(dialog);
617 }
618 }
619
620 function focusDialog(dialog) {
621 var tour = getTour(dialog);
622 var opener = tour ? pendingOpeners.get(tour) : null;
623 if (!opener || !document.documentElement.contains(opener)) {
624 opener = tour;
625 }
626 dialog._wpvrOpener = opener;
627 window.setTimeout(function () {
628 var focusable = getFocusable(dialog);
629 (focusable[0] || dialog).focus();
630 }, 0);
631 }
632
633 function restoreDialogFocus(dialog) {
634 var opener = dialog._wpvrOpener;
635 if (opener && document.documentElement.contains(opener)) {
636 opener.focus();
637 }
638 dialog._wpvrOpener = null;
639 }
640
641 function getFocusable(dialog) {
642 return Array.prototype.filter.call(dialog.querySelectorAll(focusableSelector), function (element) {
643 return isVisible(element) && element.getAttribute('aria-hidden') !== 'true';
644 });
645 }
646
647 function handleDialogKeydown(event) {
648 var dialog = event.currentTarget;
649 if (event.key === 'Escape') {
650 event.preventDefault();
651 closeDialog(dialog);
652 return;
653 }
654 if (event.key !== 'Tab') {
655 return;
656 }
657
658 var focusable = getFocusable(dialog);
659 if (!focusable.length) {
660 event.preventDefault();
661 dialog.focus();
662 return;
663 }
664 var first = focusable[0];
665 var last = focusable[focusable.length - 1];
666 if (event.shiftKey && document.activeElement === first) {
667 event.preventDefault();
668 last.focus();
669 } else if (!event.shiftKey && document.activeElement === last) {
670 event.preventDefault();
671 first.focus();
672 }
673 }
674
675 function closeDialog(dialog) {
676 var close = dialog.querySelector('.close-explainer-video, .close-generic-form, .close-floor-map-plan, .cross');
677 if (close) {
678 close.click();
679 } else {
680 $(dialog).hide();
681 syncDialog(dialog);
682 return;
683 }
684
685 window.setTimeout(function () {
686 if (isVisible(dialog)) {
687 $(dialog).hide();
688 $(dialog).closest('.pano-wrap').removeClass('show-modal');
689 }
690 syncDialog(dialog);
691 }, 0);
692 }
693
694 function scan(root) {
695 var scope = root && root.querySelectorAll ? root : document;
696 var tours = [];
697 if (scope.matches && scope.matches('.pano-wrap.pnlm-container')) {
698 tours.push(scope);
699 }
700 tours = tours.concat(Array.prototype.slice.call(scope.querySelectorAll('.pano-wrap.pnlm-container')));
701
702 tours.forEach(function (tour) {
703 enhanceContainer(tour);
704 tour.querySelectorAll(controlSelector + ', .pnlm-hotspot-base, .pnlm-hotspot').forEach(enhanceControl);
705 tour.querySelectorAll(dialogSelector).forEach(enhanceDialog);
706 });
707 }
708
709 function queueScan(root) {
710 if (scanQueued) {
711 return;
712 }
713 scanQueued = true;
714 window.requestAnimationFrame(function () {
715 scanQueued = false;
716 scan(root || document);
717 });
718 }
719
720 document.addEventListener('click', function (event) {
721 var opener = event.target.closest(controlSelector + ', .pnlm-hotspot-base, .pnlm-hotspot');
722 var tour = getTour(event.target);
723 if (opener && tour) {
724 pendingOpeners.set(tour, opener);
725 }
726 if (event.target.closest('.pnlm-load-button') && tour) {
727 focusTourAfterLoad(tour);
728 }
729 window.setTimeout(function () {
730 if (tour) {
731 scan(tour);
732 }
733 }, 0);
734 }, true);
735
736 document.addEventListener('wpvr:viewer-ready', function (event) {
737 var container = event.detail && document.getElementById(event.detail.containerId);
738 if (container) {
739 scan(container);
740 }
741 });
742
743 document.addEventListener('fullscreenchange', function () {
744 queueScan(document);
745 });
746
747 $(function () {
748 scan(document);
749 var observer = new MutationObserver(function (mutations) {
750 var shouldScan = mutations.some(function (mutation) {
751 return mutation.type === 'childList' || mutation.attributeName === 'style' ||
752 mutation.attributeName === 'class' || mutation.attributeName === 'hidden' ||
753 mutation.attributeName === 'data-play';
754 });
755 if (shouldScan) {
756 queueScan(document);
757 }
758 });
759 observer.observe(document.body, {
760 childList: true,
761 subtree: true,
762 attributes: true,
763 attributeFilter: ['style', 'class', 'hidden', 'data-play']
764 });
765 });
766 })(jQuery);
767