PageSourceSearch

https://www.georgetowncoc.com/assets/controllers/embedded_payment_center_controller-f268d153.js

js georgetowncoc.com collected 2026-09-25 04:04:28 UTC 5,790 bytes, 194 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2
3const CONNECT_JS_SRC = "https://connect-js.stripe.com/v1.0/connect.js"
4
5function ensureConnectJsLoaded() {
6  if (window.StripeConnect?.init) return Promise.resolve()
7  if (window.__chConnectJsPromise) return window.__chConnectJsPromise
8
9  window.__chConnectJsPromise = new Promise((resolve, reject) => {
10    const existing = document.querySelector(`script[src="${CONNECT_JS_SRC}"]`)
11    const script = existing || document.createElement("script")
12    let settled = false
13    let timeout
14
15    const finish = (callback) => {
16      if (settled) return
17
18      settled = true
19      window.clearTimeout(timeout)
20      callback()
21    }
22    timeout = window.setTimeout(() => {
23      finish(() => reject(new Error("Payment management did not load")))
24    }, 10_000)
25
26    script.addEventListener("load", () => {
27      if (window.StripeConnect?.init) {
28        finish(resolve)
29      } else {
30        finish(() => reject(new Error("Payment management did not load")))
31      }
32    }, { once: true })
33    script.addEventListener("error", () => {
34      finish(() => reject(new Error("Payment management did not load")))
35    }, { once: true })
36
37    if (!existing) {
38      script.src = CONNECT_JS_SRC
39      script.async = true
40      document.head.appendChild(script)
41    }
42  })
43
44  return window.__chConnectJsPromise
45}
46
47export default class extends Controller {
48  static values = {
49    publishableKey: String,
50    sessionUrl: String,
51    refreshUrl: String,
52  }
53
54  static targets = ["error", "panel", "tab"]
55
56  async connect() {
57    this.components = []
58    this.handleDocumentSubmit = this.handleDocumentSubmit.bind(this)
59    document.addEventListener("submit", this.handleDocumentSubmit, true)
60    this.clearError()
61
62    try {
63      await ensureConnectJsLoaded()
64      if (!this.element.isConnected) return
65
66      this.connectInstance = window.StripeConnect.init({
67        publishableKey: this.publishableKeyValue,
68        fetchClientSecret: () => this.fetchClientSecret(),
69        locale: "en-US",
70        appearance: {
71          overlays: "dialog",
72          variables: {
73            colorPrimary: "#18181b",
74            borderRadius: "8px",
75            fontFamily: "ui-sans-serif, system-ui, sans-serif",
76          },
77        },
78        displayOptions: { showObjectIds: false },
79      })
80
81      this.element.querySelectorAll("[data-embedded-payment-component]").forEach((container) => {
82        this.mountComponent(container)
83      })
84    } catch (_error) {
85      this.showError("Payment management failed to load. Refresh the page and try again.")
86    }
87  }
88
89  disconnect() {
90    document.removeEventListener("submit", this.handleDocumentSubmit, true)
91    this.components?.forEach((component) => component.remove())
92    this.components = []
93  }
94
95  async handleDocumentSubmit(event) {
96    const form = event.target
97    if (!(form instanceof HTMLFormElement)) return
98    if (new URL(form.action, window.location.href).pathname !== "/logout") return
99    if (!this.connectInstance?.logout || form.dataset.paymentCenterLogoutComplete === "true") return
100
101    event.preventDefault()
102    event.stopImmediatePropagation()
103    form.dataset.paymentCenterLogoutComplete = "true"
104
105    try {
106      await this.connectInstance.logout()
107    } finally {
108      HTMLFormElement.prototype.submit.call(form)
109    }
110  }
111
112  showPanel(event) {
113    const selected = event.currentTarget.dataset.panel
114
115    this.tabTargets.forEach((tab) => {
116      const active = tab.dataset.panel === selected
117      tab.setAttribute("aria-selected", active.toString())
118      tab.classList.toggle("border-zinc-900", active)
119      tab.classList.toggle("text-zinc-900", active)
120      tab.classList.toggle("border-transparent", !active)
121      tab.classList.toggle("text-zinc-500", !active)
122    })
123
124    this.panelTargets.forEach((panel) => {
125      panel.classList.toggle("hidden", panel.dataset.panel !== selected)
126    })
127  }
128
129  async fetchClientSecret() {
130    const response = await fetch(this.sessionUrlValue, {
131      method: "POST",
132      credentials: "same-origin",
133      headers: {
134        "Accept": "application/json",
135        "Content-Type": "application/json",
136        "X-CSRF-Token": document.querySelector("meta[name='csrf-token']")?.content || "",
137      },
138    })
139    const payload = await response.json()
140
141    if (!response.ok || !payload.client_secret) {
142      throw new Error("Payment management is unavailable")
143    }
144
145    return payload.client_secret
146  }
147
148  mountComponent(container) {
149    const component = this.connectInstance.create(container.dataset.embeddedPaymentComponent)
150    const loading = container.querySelector("[data-component-loading]")
151
152    component.setOnLoaderStart?.(() => loading?.remove())
153    component.setOnLoadError?.(() => {
154      loading?.remove()
155      this.showError("Part of the payment center could not load. Refresh the page and try again.")
156    })
157
158    if (container.dataset.embeddedPaymentComponent === "account-onboarding") {
159      component.setOnExit?.(() => this.refreshAfterOnboarding())
160    }
161
162    container.appendChild(component)
163    this.components.push(component)
164  }
165
166  async refreshAfterOnboarding() {
167    try {
168      await fetch(this.refreshUrlValue, {
169        method: "POST",
170        credentials: "same-origin",
171        headers: {
172          "Accept": "application/json",
173          "X-CSRF-Token": document.querySelector("meta[name='csrf-token']")?.content || "",
174        },
175      })
176    } finally {
177      window.location.reload()
178    }
179  }
180
181  showError(message) {
182    if (!this.hasErrorTarget) return
183
184    this.errorTarget.textContent = message
185    this.errorTarget.classList.remove("hidden")
186  }
187
188  clearError() {
189    if (!this.hasErrorTarget) return
190
191    this.errorTarget.textContent = ""
192    this.errorTarget.classList.add("hidden")
193  }
194}

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.