PageSourceSearch

https://platform.isms.online/assets/controllers/common_controls/domain_toggle_controller-13823720.js

js isms.online collected 2026-09-24 15:35:00 UTC 4,147 bytes, 169 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2
3export default class extends Controller {
4  static targets = [
5    "button",
6    "indicator",
7    "container",
8    "content",
9    "spinner",
10    "error",
11    "progress",
12    "summaryIndicator"
13  ]
14
15  static values = {
16    url: String,
17    completedCount: Number,
18    totalCount: Number,
19    progressTemplate: String
20  }
21
22  connect() {
23    this.open = false
24    this.loaded = false
25    this.progressListener = (event) => this.handleProgress(event)
26    this.element.addEventListener("common-controls:progress", this.progressListener)
27    this.updateProgressLabel()
28    this.updateSummaryIndicator()
29  }
30
31  disconnect() {
32    if (this.progressListener) {
33      this.element.removeEventListener("common-controls:progress", this.progressListener)
34    }
35  }
36
37  toggle(event) {
38    event.preventDefault()
39    if (this.open) {
40      this.close()
41    } else {
42      this.openSection()
43    }
44  }
45
46  openSection() {
47    this.open = true
48    this.containerTarget.hidden = false
49    this.updateButton(true)
50
51    if (!this.loaded) {
52      this.loadControls()
53    }
54  }
55
56  close() {
57    this.open = false
58    this.containerTarget.hidden = true
59    this.updateButton(false)
60  }
61
62  async loadControls() {
63    if (!this.urlValue) return
64
65    this.showSpinner(true)
66    this.showError(false)
67
68    try {
69      const response = await fetch(this.urlValue, {
70        headers: {"Accept": "application/json"}
71      })
72
73      if (!response.ok) throw new Error(`Request failed with status ${response.status}`)
74
75      const payload = await response.json()
76      this.contentTarget.innerHTML = payload.html || ""
77      this.loaded = true
78    } catch (error) {
79      this.showError(true)
80      this.loaded = false
81
82      console.error("Failed to load common controls", error)
83    } finally {
84      this.showSpinner(false)
85    }
86  }
87
88  handleProgress(event) {
89    const detail = event.detail || {}
90    const completed = this.normalizeCount(detail.completedCount)
91    const total = this.normalizeCount(detail.totalCount)
92
93    if (completed !== null) this.completedCountValue = completed
94    if (total !== null) this.totalCountValue = total
95  }
96
97  completedCountValueChanged() {
98    this.updateProgressLabel()
99    this.updateSummaryIndicator()
100  }
101
102  totalCountValueChanged() {
103    this.updateProgressLabel()
104    this.updateSummaryIndicator()
105  }
106
107  updateButton(expanded) {
108    if (this.hasButtonTarget) {
109      this.buttonTarget.setAttribute("aria-expanded", expanded)
110    }
111
112    if (this.hasIndicatorTarget) {
113      this.indicatorTarget.textContent = expanded ? "▾" : "▸"
114    }
115  }
116
117  showSpinner(visible) {
118    if (this.hasSpinnerTarget) {
119      this.spinnerTarget.hidden = !visible
120    }
121  }
122
123  showError(visible) {
124    if (this.hasErrorTarget) {
125      this.errorTarget.hidden = !visible
126    }
127  }
128
129  updateProgressLabel() {
130    if (!this.hasProgressTarget) return
131
132    const template = this.hasProgressTemplateValue
133      ? this.progressTemplateValue
134      : "%{completed} / %{total} completed"
135
136    const formatter = new Intl.NumberFormat()
137    const total = this.safeTotalCount()
138    const completed = Math.min(this.safeCompletedCount(), total)
139
140    const formatted = template
141      .replace("%{completed}", formatter.format(completed))
142      .replace("%{total}", formatter.format(total))
143
144    this.progressTarget.textContent = formatted
145  }
146
147  updateSummaryIndicator() {
148    if (!this.hasSummaryIndicatorTarget) return
149    const total = this.safeTotalCount()
150    const completed = this.safeCompletedCount()
151    this.summaryIndicatorTarget.textContent = total > 0 && completed === total ? "✅" : ""
152  }
153
154  safeCompletedCount() {
155    const value = Number.isFinite(this.completedCountValue) ? this.completedCountValue : 0
156    return Math.max(0, Math.trunc(value))
157  }
158
159  safeTotalCount() {
160    const value = Number.isFinite(this.totalCountValue) ? this.totalCountValue : 0
161    return Math.max(0, Math.trunc(value))
162  }
163
164  normalizeCount(value) {
165    if (value === undefined || value === null) return null
166    const number = Number(value)
167    return Number.isFinite(number) ? Math.max(0, Math.trunc(number)) : null
168  }
169}

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.