|
|
| Line 1: |
Line 1: |
| (function () {
| |
| 'use strict';
| |
|
| |
| const urlCache = new Map();
| |
|
| |
| function getSize(element) {
| |
| const match = element.className.match(/model_size_(\d+)/);
| |
| return match ? parseInt(match[1], 10) : 500;
| |
| }
| |
|
| |
| function getFileUrl(fileName) {
| |
| if (urlCache.has(fileName)) {
| |
| return urlCache.get(fileName);
| |
| }
| |
|
| |
| const apiUrl =
| |
| mw.config.get('wgScriptPath') +
| |
| '/api.php?action=query' +
| |
| '&titles=File:' + encodeURIComponent(fileName) +
| |
| '&prop=imageinfo' +
| |
| '&iiprop=url' +
| |
| '&format=json';
| |
|
| |
| const promise = fetch(apiUrl)
| |
| .then(function (response) {
| |
| if (!response.ok) {
| |
| throw new Error('API request failed');
| |
| }
| |
| return response.json();
| |
| })
| |
| .then(function (data) {
| |
| const page = Object.values(data.query.pages)[0];
| |
| return page && page.imageinfo ? page.imageinfo[0].url : null;
| |
| })
| |
| .catch(function () {
| |
| return null;
| |
| });
| |
|
| |
| urlCache.set(fileName, promise);
| |
| return promise;
| |
| }
| |
|
| |
| function loadModel(preview) {
| |
| if (preview.dataset.loaded) {
| |
| return;
| |
| }
| |
|
| |
| preview.dataset.loaded = '1';
| |
|
| |
| const fileName = preview.dataset.model;
| |
| const size = getSize(preview);
| |
|
| |
| getFileUrl(fileName + '.glb')
| |
| .then(function (url) {
| |
| if (!url) {
| |
| return;
| |
| }
| |
|
| |
| const wrap = document.createElement('div');
| |
| wrap.className = 'model-viewer-wrap';
| |
|
| |
| const container = document.createElement('div');
| |
| container.className = 'model-viewer-container';
| |
| container.style.width = size + 'px';
| |
| container.style.height = size + 'px';
| |
|
| |
| const viewer = document.createElement('model-viewer');
| |
| viewer.src = url;
| |
| viewer.setAttribute('camera-controls', '');
| |
| viewer.setAttribute('touch-action', 'pan-y');
| |
|
| |
| container.appendChild(viewer);
| |
| wrap.appendChild(container);
| |
|
| |
| const name = document.createElement('div');
| |
| name.className = 'model-name';
| |
| name.textContent = fileName;
| |
|
| |
| wrap.appendChild(name);
| |
|
| |
| preview.replaceWith(wrap);
| |
| });
| |
| }
| |
|
| |
| function initPreview(preview) {
| |
| if (preview.dataset.initialized) {
| |
| return;
| |
| }
| |
|
| |
| preview.dataset.initialized = '1';
| |
|
| |
| const button = document.createElement('button');
| |
| button.className = 'model-load-button';
| |
| button.type = 'button';
| |
| button.addEventListener('click', function () {
| |
| loadModel(preview);
| |
| });
| |
|
| |
| preview.appendChild(button);
| |
| }
| |
|
| |
| function scan() {
| |
| document
| |
| .querySelectorAll('.model-placeholder')
| |
| .forEach(initPreview);
| |
| }
| |
|
| |
| let scanTimer = null;
| |
|
| |
| function debouncedScan() {
| |
| clearTimeout(scanTimer);
| |
| scanTimer = setTimeout(scan, 100);
| |
| }
| |
|
| |
| scan();
| |
|
| |
| const observer = new MutationObserver(debouncedScan);
| |
| observer.observe(document.body, {
| |
| childList: true,
| |
| subtree: true
| |
| });
| |
|
| |
| })();
| |
|
| |
|
| |
|
|
| |
|