PageSourceSearch

https://www.georgetowncoc.com/assets/controllers/employee_count_price_tiers_controller-6a282703.js

js georgetowncoc.com collected 2026-09-25 04:04:29 UTC 2,790 bytes, 97 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2
3export default class extends Controller {
4  static targets = ["rows", "row", "template", "removeButton"]
5  static values = { nextIndex: Number }
6
7  connect() {
8    this.ensureTrailingBlankRow()
9  }
10
11  fieldChanged() {
12    this.ensureTrailingBlankRow()
13  }
14
15  remove(event) {
16    event.preventDefault()
17
18    const row = event.currentTarget.closest("[data-employee-count-price-tiers-target~='row']")
19    if (!row) return
20
21    const destroyInput = row.querySelector("input[name$='[_destroy]']")
22    if (destroyInput) destroyInput.value = "1"
23
24    row.dataset.employeeCountPriceTiersRemoved = "true"
25    row.classList.add("hidden")
26    this.ensureTrailingBlankRow()
27  }
28
29  ensureTrailingBlankRow() {
30    this.removeExtraTrailingBlankRows()
31
32    const rows = this.activeRows()
33    if (rows.length === 0 || this.rowHasContent(rows[rows.length - 1])) {
34      this.addBlankRow()
35    }
36
37    this.updateRemoveButtons()
38  }
39
40  addBlankRow() {
41    if (!this.hasTemplateTarget || !this.hasRowsTarget) return
42
43    const index = this.nextIndexValue
44    const html = this.templateTarget.innerHTML.replaceAll("__INDEX__", String(index))
45    this.nextIndexValue = index + 1
46    this.rowsTarget.insertAdjacentHTML("beforeend", html)
47  }
48
49  removeExtraTrailingBlankRows() {
50    const rows = this.activeRows()
51    let lastMeaningfulIndex = -1
52    rows.forEach((row, index) => {
53      if (this.rowHasContent(row) || this.persistedRow(row)) lastMeaningfulIndex = index
54    })
55
56    const trailingBlankRows = rows.slice(lastMeaningfulIndex + 1)
57    const extraBlankRows = lastMeaningfulIndex === -1 ? trailingBlankRows.slice(1) : trailingBlankRows.slice(0, -1)
58
59    extraBlankRows.forEach((row) => row.remove())
60  }
61
62  updateRemoveButtons() {
63    this.removeButtonTargets.forEach((button) => {
64      const row = button.closest("[data-employee-count-price-tiers-target~='row']")
65      const shouldShow = row && !this.removedRow(row) && (this.persistedRow(row) || this.rowHasContent(row))
66      button.classList.toggle("invisible", !shouldShow)
67      button.disabled = !shouldShow
68    })
69  }
70
71  activeRows() {
72    return this.rowTargets.filter((row) => !this.removedRow(row))
73  }
74
75  removedRow(row) {
76    return row.dataset.employeeCountPriceTiersRemoved === "true"
77  }
78
79  persistedRow(row) {
80    return this.idInput(row)?.value.trim().length > 0
81  }
82
83  rowHasContent(row) {
84    return this.editableInputs(row).some((input) => input.value.trim().length > 0)
85  }
86
87  editableInputs(row) {
88    return Array.from(row.querySelectorAll("input")).filter((input) => {
89      const name = input.getAttribute("name") || ""
90      return input.type !== "hidden" && !name.endsWith("[_destroy]")
91    })
92  }
93
94  idInput(row) {
95    return row.querySelector("input[name$='[id]']")
96  }
97}

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.