PageSourceSearch

https://www.vzome.com/modules/vzome-viewer.js

js vzome.com collected 2026-10-02 07:04:26 UTC 14,719 bytes, 464 lines download raw bytes

1import {
2  vZomeViewerCSS
3} from "./chunk-BXRCXLRH.js";
4import {
5  decodeEntities
6} from "./chunk-J3RXXGCJ.js";
7import "./chunk-JSBRDJBE.js";
8
9// src/wc/index-buttons.js
10var debug = false;
11var VZomeViewerIndexButton = class extends HTMLElement {
12  #next;
13  #end;
14  #viewerId;
15  #viewer;
16  #loadCamera;
17  #button;
18  #maxSceneIndex;
19  constructor(next = true, end = false) {
20    super();
21    this.#next = next;
22    this.#end = end;
23    this.#loadCamera = false;
24  }
25  #disabled(index) {
26    if (this.#next)
27      return index === this.#maxSceneIndex;
28    else
29      return index === 0;
30  }
31  connectedCallback() {
32    if (!!this.#viewerId) {
33      this.#viewer = document.querySelector(`#${this.#viewerId}`);
34      if (!this.#viewer) {
35        console.error(`No vzome-viewer with id "${this.#viewerId}" found.`);
36      } else if (this.#viewer.nextScene === void 0) {
37        console.error(`Element with id "${this.#viewerId}" is not a vzome-viewer.`);
38        return;
39      }
40    }
41    if (!this.#viewer) {
42      this.#viewer = document.querySelector("vzome-viewer");
43    }
44    if (!this.#viewer) {
45      console.error(`No vzome-viewer found.`);
46      return;
47    }
48    this.#button = document.createElement("button");
49    this.#button.textContent = this.getAttribute("label");
50    this.appendChild(this.#button);
51    this.#button.classList.add("vzome-viewer-index-button");
52    this.#viewer.addEventListener("vzome-design-rendered", (e) => {
53      const { index } = e.detail;
54      if (this.#disabled(index))
55        this.#button.setAttribute("disabled", "true");
56      else
57        this.#button.removeAttribute("disabled");
58    });
59    this.#viewer.addEventListener("vzome-scenes-discovered", (e) => {
60      const titles = e.detail;
61      this.#maxSceneIndex = titles.length - 1;
62    });
63    const loadParams = { camera: this.#loadCamera };
64    const handler = () => {
65      if (this.#end) {
66        if (this.#next) {
67          this.#viewer.selectScene(-1, loadParams);
68        } else {
69          this.#viewer.selectScene(1, loadParams);
70        }
71      } else if (this.#next) {
72        this.#viewer.nextScene(loadParams);
73      } else {
74        this.#viewer.previousScene(loadParams);
75      }
76    };
77    this.#button.addEventListener("click", handler);
78  }
79  static get observedAttributes() {
80    return ["viewer", "load-camera"];
81  }
82  attributeChangedCallback(attributeName, _oldValue, _newValue) {
83    debug && console.log("VZomeViewerIndexButton attribute changed");
84    switch (attributeName) {
85      case "viewer":
86        this.#viewerId = _newValue;
87        break;
88      case "load-camera":
89        this.#loadCamera = _newValue === "true";
90        break;
91    }
92  }
93};
94var VZomeViewerNextButton = class extends VZomeViewerIndexButton {
95  constructor() {
96    super(true);
97  }
98};
99var VZomeViewerPrevButton = class extends VZomeViewerIndexButton {
100  constructor() {
101    super(false);
102  }
103};
104var VZomeViewerFirstButton = class extends VZomeViewerIndexButton {
105  constructor() {
106    super(false, true);
107  }
108};
109var VZomeViewerLastButton = class extends VZomeViewerIndexButton {
110  constructor() {
111    super(true, true);
112  }
113};
114
115// src/wc/vzome-viewer.js
116var debug2 = false;
117var VZomeViewer = class extends HTMLElement {
118  #root;
119  #container;
120  #config;
121  #reactive;
122  #urlChanged;
123  #sceneChanged;
124  #updateCalled;
125  #loadFlags;
126  #indexed;
127  #numScenes;
128  #sceneIndex;
129  // ranges from 1 to #numScenes
130  #sceneTitles;
131  #whenApiReady;
132  #resolveApiReady;
133  #designPromise;
134  constructor() {
135    super();
136    this.#root = this.attachShadow({ mode: "open" });
137    this.#root.appendChild(document.createElement("style")).textContent = vZomeViewerCSS;
138    this.#container = document.createElement("div");
139    this.#root.appendChild(this.#container);
140    this.#config = {
141      preview: true,
142      showScenes: "none",
143      camera: true,
144      lighting: true,
145      design: true,
146      labels: false,
147      showSettings: true,
148      download: true,
149      useSpinner: false
150    };
151    this.#whenApiReady = new Promise((resolve) => this.#resolveApiReady = resolve);
152    this.#designPromise = null;
153    this.#indexed = false;
154    this.#urlChanged = true;
155    this.#sceneChanged = false;
156    this.#reactive = true;
157    this.#updateCalled = false;
158    this.#loadFlags = {};
159    debug2 && console.log("custom element constructed");
160  }
161  #showIndexedScene(index, loadFlags = { camera: true }) {
162    this.#whenApiReady.then((api) => {
163      api.showIndexedScene(index, loadFlags);
164    });
165  }
166  selectScene(index, loadFlags = { camera: true }) {
167    debug2 && console.log("User called selectScene()");
168    if (!this.#indexed) {
169      console.log("This selectScene call ignored; the viewer is not indexed.  Set indexed to true if you want to use selectScene.");
170      return;
171    }
172    if (this.#numScenes === void 0) {
173      console.log("This selectScene call ignored; no scenes were discovered.");
174      return;
175    }
176    this.#sceneIndex = index < 0 ? this.#numScenes : 1;
177    this.#showIndexedScene(this.#sceneIndex, loadFlags);
178  }
179  previousScene(loadFlags = { camera: true }) {
180    debug2 && console.log("User called previousScene()");
181    if (!this.#indexed) {
182      console.log("This previousScene call ignored; the viewer is not indexed.  Set indexed to true if you want to use previousScene.");
183      return;
184    }
185    if (this.#numScenes === void 0) {
186      console.log("This previousScene call ignored; no scenes were discovered.");
187      return;
188    }
189    this.#sceneIndex = Math.max(this.#sceneIndex - 1, 1);
190    this.#showIndexedScene(this.#sceneIndex, loadFlags);
191  }
192  nextScene(loadFlags = { camera: true }) {
193    debug2 && console.log("User called nextScene()");
194    this.#loadFlags = loadFlags;
195    if (!this.#indexed) {
196      console.log("This nextScene call ignored; the viewer is not indexed.  Set indexed to true if you want to use nextScene.");
197      return;
198    }
199    if (this.#numScenes === void 0) {
200      console.log("This nextScene call ignored; no scenes were discovered.");
201      return;
202    }
203    this.#sceneIndex = Math.min(this.#sceneIndex + 1, this.#numScenes);
204    this.#showIndexedScene(this.#sceneIndex, loadFlags);
205  }
206  update(loadFlags = { camera: true }) {
207    debug2 && console.log("User called update()");
208    this.#loadFlags = loadFlags;
209    if (this.#reactive) {
210      console.log("This update call ignored; the viewer is reactive to attribute changes.  Set reactive to false if you want programmatic control of the viewer.");
211      return;
212    }
213    this.#updateCalled = true;
214    this.#triggerWorker();
215  }
216  #triggerWorker() {
217    this.#whenApiReady.then((api) => {
218      const { camera = true, lighting = true, design = true } = this.#loadFlags;
219      const load = { camera, lighting, design };
220      const config = { ...this.#config, load };
221      if (this.#config.url && this.#urlChanged) {
222        debug2 && console.log("requesting design");
223        api.requestDesign(this.#config.url, config);
224        this.#urlChanged = false;
225      } else if (this.#sceneChanged) {
226        debug2 && console.log("requesting scene");
227        api.showTitledScene(this.#config.sceneTitle, load);
228      }
229    });
230  }
231  async loadFromText(name, contents) {
232    return this.#whenApiReady.then((api) => {
233      if (!!this.#designPromise) {
234        const errorMsg = "loadFromText ignored; Waiting for previous design to load";
235        console.log(errorMsg);
236        return Promise.reject(new Error(errorMsg));
237      }
238      return new Promise((resolve, reject) => {
239        this.#designPromise = { resolve, reject };
240        api.resetScenes();
241        api.openText(name, contents);
242      });
243    });
244  }
245  async captureImage(format, params = {}) {
246    return this.#whenApiReady.then((api) => {
247      return api.captureImage(format, params);
248    });
249  }
250  async exportText(format, params = {}) {
251    return this.#whenApiReady.then((api) => {
252      return api.exportAs(format, params);
253    });
254  }
255  connectedCallback() {
256    const callbacks = {
257      setApi: (api) => {
258        this.#resolveApiReady(api);
259      },
260      onAlert: (error) => {
261        this.dispatchEvent(new CustomEvent("vzome-design-failed"));
262        if (!!this.#designPromise) {
263          this.#designPromise.reject(error);
264          this.#designPromise = null;
265        }
266      },
267      onSceneRendered: (sceneIndex) => {
268        --sceneIndex;
269        let scene = {};
270        if (this.#indexed && !!this.#sceneTitles)
271          scene = { index: sceneIndex, title: this.#sceneTitles[sceneIndex] };
272        this.dispatchEvent(new CustomEvent("vzome-design-rendered", { detail: scene }));
273        if (!!this.#designPromise) {
274          this.#designPromise.resolve();
275          this.#designPromise = null;
276        }
277      },
278      onScenesDiscovered: (scenes) => {
279        this.#numScenes = scenes.length - 1;
280        this.#sceneTitles = scenes.map((scene, i) => scene.title ? decodeEntities(scene.title) : `#${i}`);
281        if (this.#indexed)
282          this.#sceneTitles = this.#sceneTitles.slice(1);
283        this.dispatchEvent(new CustomEvent("vzome-scenes-discovered", { detail: this.#sceneTitles }));
284        this.dispatchEvent(new CustomEvent("vzome-scenes", { detail: scenes.slice(1) }));
285      }
286    };
287    debug2 && console.log("custom element connected");
288    import("./vzome-viewer-dynamic.js").then((module) => {
289      debug2 && console.log("dynamic module loaded");
290      module.renderViewer(this.#container, this.#config, callbacks);
291      if (this.#reactive || this.#updateCalled || this.#indexed)
292        this.#triggerWorker();
293    });
294  }
295  static get observedAttributes() {
296    return ["src", "show-scenes", "scene", "load-camera", "reactive", "labels", "show-perspective", "show-settings", "tween-duration", "indexed", "download", "progress"];
297  }
298  // This callback can happen *before* connectedCallback()!
299  attributeChangedCallback(attributeName, _oldValue, _newValue) {
300    debug2 && console.log("custom element attribute changed");
301    switch (attributeName) {
302      case "src":
303        const newUrl = new URL(_newValue, window.location).toString();
304        if (newUrl !== this.#config.url) {
305          this.#config.url = newUrl;
306          this.#urlChanged = true;
307          if (this.#reactive)
308            this.#triggerWorker();
309        }
310        break;
311      case "scene":
312        if (this.#indexed)
313          break;
314        if (_newValue !== this.#config.sceneTitle) {
315          this.#config = { ...this.#config, sceneTitle: _newValue, showScenes: "given" };
316          this.#sceneChanged = true;
317          if (this.#reactive)
318            this.#triggerWorker();
319        }
320        break;
321      case "show-scenes":
322        if (this.#indexed)
323          break;
324        const showScenes = _newValue === "true" ? "all" : _newValue === "fal
324se" ? "none" : _newValue === "named" ? "titled" : _newValue;
325        this.#config = { ...this.#config, showScenes };
326        break;
327      case "labels":
328        const labels = _newValue === "true";
329        this.#config = { ...this.#config, labels };
330        break;
331      case "download":
332        const download = _newValue === "true";
333        this.#config = { ...this.#config, download };
334        break;
335      case "progress":
336        const useSpinner = _newValue === "true";
337        this.#config = { ...this.#config, useSpinner };
338        break;
339      case "show-perspective":
340      case "show-settings":
341        const showSettings = _newValue === "true";
342        this.#config = { ...this.#config, showSettings };
343        break;
344      case "tween-duration":
345        const duration = _newValue;
346        this.#config = { ...this.#config, tweening: { duration } };
347        this.#whenApiReady.then((api) => api.setTweenDuration(duration));
348        break;
349      case "reactive":
350        if (this.#indexed)
351          break;
352        this.#reactive = _newValue === "true";
353        break;
354      case "indexed":
355        if (_newValue !== "true")
356          break;
357        this.#indexed = true;
358        this.#reactive = false;
359        this.#sceneIndex = 1;
360        this.#config = { ...this.#config, showScenes: "indexed" };
361        break;
362    }
363  }
364  set src(newSrc) {
365    if (newSrc === null) {
366      this.removeAttribute("src");
367    } else {
368      this.setAttribute("src", newSrc);
369    }
370  }
371  get src() {
372    return this.getAttribute("src");
373  }
374  set scene(newScene) {
375    if (newScene === null) {
376      this.removeAttribute("scene");
377    } else {
378      this.setAttribute("scene", newScene);
379    }
380  }
381  get scene() {
382    return this.getAttribute("scene");
383  }
384  set showScenes(newValue) {
385    if (newValue === null) {
386      this.removeAttribute("show-scenes");
387    } else {
388      this.setAttribute("show-scenes", newValue);
389    }
390  }
391  get showScenes() {
392    return this.getAttribute("show-scenes");
393  }
394  set labels(newValue) {
395    if (newValue === null) {
396      this.removeAttribute("labels");
397    } else {
398      this.setAttribute("labels", newValue);
399    }
400  }
401  get labels() {
402    return this.getAttribute("labels");
403  }
404  set download(newValue) {
405    if (newValue === null) {
406      this.removeAttribute("download");
407    } else {
408      this.setAttribute("download", newValue);
409    }
410  }
411  get download() {
412    return this.getAttribute("download");
413  }
414  set progress(newValue) {
415    if (newValue === null) {
416      this.removeAttribute("progress");
417    } else {
418      this.setAttribute("progress", newValue);
419    }
420  }
421  get progress() {
422    return this.getAttribute("progress");
423  }
424  set showPerspective(newValue) {
425    if (newValue === null) {
426      this.removeAttribute("show-settings");
427    } else {
428      this.setAttribute("show-settings", newValue);
429    }
430  }
431  get showPerspective() {
432    return this.getAttribute("show-settings");
433  }
434  set showSettings(newValue) {
435    if (newValue === null) {
436      this.removeAttribute("show-settings");
437    } else {
438      this.setAttribute("show-settings", newValue);
439    }
440  }
441  get showSettings() {
442    return this.getAttribute("show-settings");
443  }
444  set reactive(value) {
445    this.setAttribute("reactive", value);
446  }
447  get reactive() {
448    return this.getAttribute("reactive");
449  }
450  // These were briefly supported, so I don't want to break anyone's client code.
451  //   We don't support the behavior any more.
452  set loadCamera(newValue) {
453    console.log("loadCamera is no longer supported.");
454  }
455  get loadCamera() {
456    console.log("loadCamera is no longer supported.");
457    return void 0;
458  }
459};
460customElements.define("vzome-viewer", VZomeViewer);
461customElements.define("vzome-viewer-end", VZomeViewerLastButton);
462customElements.define("vzome-viewer-next", VZomeViewerNextButton);
463customElements.define("vzome-viewer-previous", VZomeViewerPrevButton);
464customElements.define("vzome-viewer-start", VZomeViewerFirstButton);

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.