User:Monster Domosed/common.js: Difference between revisions

No edit summary
load on demand
Line 5: Line 5:
     const HQ_ICON = '/images/1/10/HQ_icon.png';
     const HQ_ICON = '/images/1/10/HQ_icon.png';


     function updateViewerSize(viewer, wrapper) {
     function getSize(wrapper) {
         const match = wrapper.className.match(/model_size_(\d+)/);
         const match = wrapper.className.match(/model_size_(\d+)/);
        return match ? parseInt(match[1], 10) : 500;
    }


        if (!match) {
    function updateViewerSize(viewer, wrapper) {
            return;
         const size = getSize(wrapper);
        }
 
         const size = parseInt(match[1], 10);


         viewer.style.width = size + 'px';
         viewer.style.width = size + 'px';
Line 18: Line 17:
     }
     }


     function updateButtonSize(button, wrapper) {
     function updateQualityButtonSize(button, wrapper) {
        const match = wrapper.className.match(/model_size_(\d+)/);
         const size = getSize(wrapper);
 
        if (!match) {
            return;
        }
 
         const size = parseInt(match[1], 10);


        // 14% of viewer size, minimum 28px, maximum 56px
         const buttonSize = Math.max(28, Math.min(56, Math.round(size * 0.14)));
         const buttonSize = Math.max(28, Math.min(56, Math.round(size * 0.14)));


Line 34: Line 26:
     }
     }


     function getHQUrl(fileName) {
     function getFileUrl(fileName) {
         const apiUrl =
         const apiUrl =
             mw.config.get('wgScriptPath') +
             mw.config.get('wgScriptPath') +
             '/api.php?action=query' +
             '/api.php?action=query' +
             '&titles=File:' + encodeURIComponent(fileName) +
             '&titles=File:' + encodeURIComponent(fileName) +
            '.glb' +
             '&prop=imageinfo' +
             '&prop=imageinfo' +
             '&iiprop=url' +
             '&iiprop=url' +
Line 49: Line 40:
             })
             })
             .then(function (data) {
             .then(function (data) {
                const pages = data.query.pages;
                 const page = Object.values(data.query.pages)[0];
                 const page = Object.values(pages)[0];


                 if (!page.imageinfo) {
                 if (!page.imageinfo) {
Line 60: Line 50:
     }
     }


     function switchQuality(viewer, icon, lqUrl, hqUrl) {
     function addQualityButton(wrapper, viewer, lqUrl, hqUrl) {
         const isLQ = viewer.src === lqUrl;
         if (!hqUrl || lqUrl === hqUrl) {
        viewer.src = isLQ ? hqUrl : lqUrl;
        icon.src = isLQ ? HQ_ICON : LQ_ICON;
    }
 
    function initViewer(viewer) {
        if (viewer.dataset.qualityButtonAdded) {
             return;
             return;
         }
         }


         const wrapper = viewer.closest('[class*="model_size_"]');
         const button = document.createElement('button');
        button.className = 'mv-quality-toggle';
        button.type = 'button';


         if (!wrapper) {
         updateQualityButtonSize(button, wrapper);
            return;
        }


         const src = viewer.getAttribute('src');
         const icon = document.createElement('img');
        icon.src = LQ_ICON;
        icon.alt = '';


         if (!src || !src.endsWith('-compressed.glb')) {
         button.appendChild(icon);
            return;
        }


         viewer.dataset.qualityButtonAdded = '1';
         button.addEventListener('click', function () {
            const isLQ = viewer.src === lqUrl;


         updateViewerSize(viewer, wrapper);
            viewer.src = isLQ ? hqUrl : lqUrl;
            icon.src = isLQ ? HQ_ICON : LQ_ICON;
         });


         const fileMatch = src.match(/\/([^\/]+)-compressed\.glb$/);
         wrapper.appendChild(button);
    }


         if (!fileMatch) {
    function loadModel(wrapper) {
         if (wrapper.dataset.loaded) {
             return;
             return;
         }
         }


         const baseName = fileMatch[1];
         wrapper.dataset.loaded = '1';


         getHQUrl(baseName)
         Promise.all([
             .then(function (hqUrl) {
            getFileUrl(wrapper.dataset.lq),
             getFileUrl(wrapper.dataset.hq)
        ]).then(function (urls) {


                if (!hqUrl) {
            const lqUrl = urls[0] || urls[1];
                    return;
            const hqUrl = urls[1];
                }


                const container = document.createElement('div');
            if (!lqUrl) {
                 container.className = 'mv-quality-container';
                 return;
            }


                viewer.parentNode.insertBefore(container, viewer);
            wrapper.innerHTML = '';
                container.appendChild(viewer);


                const button = document.createElement('button');
            const viewer = document.createElement('model-viewer');
                button.className = 'mv-quality-toggle';
                button.type = 'button';


                updateButtonSize(button, wrapper);
            viewer.src = lqUrl;


                const icon = document.createElement('img');
            viewer.setAttribute('camera-controls', '');
                icon.src = LQ_ICON;
            viewer.setAttribute('touch-action', 'pan-y');
                icon.alt = '';
            viewer.setAttribute('loading', 'lazy');


                button.appendChild(icon);
            updateViewerSize(viewer, wrapper);
                container.appendChild(button);


                button.addEventListener('click', function () {
            wrapper.appendChild(viewer);
                    switchQuality(
                        viewer,
                        icon,
                        src,
                        hqUrl
                    );
                });


            viewer.addEventListener('load', function () {
                addQualityButton(wrapper, viewer, lqUrl, hqUrl);
             });
             });
        });
    }
    function initPlaceholder(wrapper) {
        if (wrapper.dataset.initialized) {
            return;
        }
        wrapper.dataset.initialized = '1';
        wrapper.style.position = 'relative';
        const button = document.createElement('button');
        button.type = 'button';
        button.className = 'model-load-button';
        button.innerHTML =
            '<span class="model-load-label">▶ Load 3D Model</span>';
        button.addEventListener('click', function () {
            loadModel(wrapper);
        });
        wrapper.appendChild(button);
     }
     }


     function scan() {
     function scan() {
         document.querySelectorAll('model-viewer').forEach(initViewer);
         document
            .querySelectorAll('.model-placeholder')
            .forEach(initPlaceholder);
     }
     }


     scan();
     scan();


     const observer = new MutationObserver(function () {
     new MutationObserver(scan).observe(document.body, {
        scan();
    });
 
    observer.observe(document.body, {
         childList: true,
         childList: true,
         subtree: true
         subtree: true
Line 149: Line 155:


     window.addEventListener('resize', function () {
     window.addEventListener('resize', function () {
         document.querySelectorAll('model-viewer').forEach(function (viewer) {
         document.querySelectorAll('model-viewer').forEach(function (viewer) {
             const wrapper = viewer.closest('[class*="model_size_"]');
 
             const wrapper = viewer.closest('.model-placeholder');


             if (!wrapper) {
             if (!wrapper) {
Line 158: Line 166:
             updateViewerSize(viewer, wrapper);
             updateViewerSize(viewer, wrapper);


             const button = viewer.parentNode.querySelector('.mv-quality-toggle');
             const button = wrapper.querySelector('.mv-quality-toggle');


             if (button) {
             if (button) {
                 updateButtonSize(button, wrapper);
                 updateQualityButtonSize(button, wrapper);
             }
             }
         });
         });
     });
     });