PageSourceSearch

https://survivalbrasil.org/assets/controllers/numiko/vimeo_controller-5acf86ed.js

js survivalbrasil.org collected 2026-09-25 18:53:42 UTC 4,898 bytes, 137 lines download raw bytes

1import { Controller } from "@hotwired/stimulus";
2import Player from "@vimeo/player";
3import urlify from "utils/urlify";
4
5// Connects to data-controller="numiko--vimeo"
6export default class extends Controller {
7  static targets = [
8    "vimeoModal",
9    "iframe",
10    "ifrmContainer",
11    "modalName",
12    "modalDescription",
13  ];
14
15  connect() {
16    const vimeoModalElem = this.vimeoModalTarget;
17    new bootstrap.Modal(vimeoModalElem, { keyboard: false });
18
19    vimeoModalElem.addEventListener("show.bs.modal", (event) => {
20      const selectedVimeoObj = event.relatedTarget;
21      if (selectedVimeoObj.dataset.vimeoVideoId) {
22        const vimeoId = selectedVimeoObj.dataset.vimeoVideoId;
23        const name = selectedVimeoObj.dataset.vimeoVideoName;
24        const description = selectedVimeoObj.dataset.vimeoVideoDescription;
25        this.#createVimeoVideoIframe(vimeoId, name, description);
26      } else {
27        const vimeoId = selectedVimeoObj.dataset.vimeoShowcaseId;
28        const name = selectedVimeoObj.dataset.vimeoShowcaseName;
29        const description = selectedVimeoObj.dataset.vimeoShowcaseDescription;
30        const videosJson = selectedVimeoObj.dataset.vimeoShowcaseVideos;
31        const videosMap = videosJson ? JSON.parse(videosJson) : {};
32        this.#createVimeoShowcaseIframe(vimeoId, name, description, videosMap);
33      }
34    });
35
36    vimeoModalElem.addEventListener("hide.bs.modal", (_event) => {
37      if (this.player) {
38        this.player.destroy();
39        this.player = null;
40      }
41      this.showcaseVideosMap = null;
42      this.iframeTarget.remove();
43    });
44  }
45
46  #createVimeoVideoIframe(vimeoId, name, description) {
47    const ifrm = document.createElement("iframe");
48    ifrm.setAttribute(
49      "src",
50      `https://player.vimeo.com/video/${vimeoId}?color=B33D26&autoplay=1&texttrack=${locale}`,
51    );
52    ifrm.setAttribute("frameborder", "0");
53    ifrm.setAttribute("webkitallowfullscreen", true);
54    ifrm.setAttribute("mozallowfullscreen", true);
55    ifrm.setAttribute("allowfullscreen", true);
56    ifrm.dataset["numiko-VimeoTarget"] = "iframe"; // Note: how this translates to: data-numiko--vimeo-target="iframe"
57    ifrm.style.width = "100%";
58    ifrm.style.height = "400px";
59    ifrm.style.top = 0;
60    ifrm.style.left = 0;
61    this.ifrmContainerTarget.appendChild(ifrm);
62    this.modalNameTarget.innerHTML = name;
63    this.modalDescriptionTarget.innerHTML = urlify(description);
64  }
65
66  #createVimeoShowcaseIframe(vimeoId, name, description, videosMap) {
67    this.showcaseVideosMap = videosMap;
68
69    const ifrm = document.createElement("iframe");
70    ifrm.setAttribute(
71      "src",
72      `https://vimeo.com/showcase/${vimeoId}/embed?color=B33D26&autoplay=1&texttrack=${locale}`,
73    );
74    ifrm.setAttribute("frameborder", "0");
75    ifrm.setAttribute("webkitallowfullscreen", true);
76    ifrm.setAttribute("mozallowfullscreen", true);
77    ifrm.setAttribute("allowfullscreen", true);
78    ifrm.dataset["numiko-VimeoTarget"] = "iframe"; // Note: how tis translates to: data-numiko--vimeo-target="iframe"
79    ifrm.style.width = "100%";
80    ifrm.style.height = "400px";
81    ifrm.style.top = 0;
82    ifrm.style.left = 0;
83    this.ifrmContainerTarget.appendChild(ifrm);
84    this.modalNameTarget.innerHTML = name;
85    this.modalDescriptionTarget.innerHTML = urlify(description);
86
87    this.player = new Player(ifrm);
88    this.player.on("loaded", () => {
89      this.#updateDescriptionFromCurrentVideo();
90    });
91  }
92
93  #updateDescriptionFromCurrentVideo() {
94    if (!this.player || !this.showcaseVideosMap) return;
95
96    this.player.getVideoId().then((videoId) => {
97      const videoDescription = this.showcaseVideosMap[videoId];
98      if (videoDescription !== undefined) {
99        this.modalDescriptionTarget.innerHTML =
100          this.#formatDescription(videoDescription);
101      }
102    });
103  }
104
105  #formatDescription(description) {
106    const parts = description.split(/\n\n--\n\n/);
107
108    if (parts.length === 1) {
109      return urlify(description);
110    }
111
112    const mainContent = parts[0];
113    const ctaSection = parts[1].trim();
114    const additionalContent = parts.slice(2).join("\n\n");
115
116    // Match "Action Text: url" where URL may or may not have protocol
117    const linkMatch = ctaSection.match(
118      /^(.+?):\s*((?:https?:\/\/)?[\w.-]+(?:\/\S*)?)\s*$/,
119    );
120
121    if (!linkMatch) {
122      return urlify(description);
123    }
124
125    const [, linkText, rawUrl] = linkMatch;
126    const linkUrl = rawUrl.startsWith("http") ? rawUrl : `https://${rawUrl}`;
127    const buttonHtml = `<a href="${linkUrl}" class="btn btn-primary" target="_blank" rel="noopener noreferrer">${linkText.trim()}</a>`;
128
129    let fullDescription = mainContent.trim();
130    if (additionalContent) {
131      fullDescription += "\n\n" + additionalContent.trim();
132    }
133
134    fullDescription = fullDescription.replace(/\n/g, "<br>");
135    return urlify(fullDescription) + "<br><br>" + buttonHtml;
136  }
137}

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.