PageSourceSearch

https://survivalbrasil.org/assets/controllers/numiko/tax_calculator_controller-ea80dd8d.js

js survivalbrasil.org collected 2026-09-25 18:53:39 UTC 4,123 bytes, 125 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2import currency from 'currency.js'
3
4// Connects to data-controller="numiko--tax-calculator"
5export default class extends Controller {
6  static targets = [
7    'amountInput', 'frequencies', 'deductionText',
8    'savingsText', 'differenceText', 'ineligibleText',
9    'frequencyPopupTrigger', 'regularInput', 'regularLabel'
10  ]
11
12  static values = {
13    defaultCurrency: String,
14    baseRate: Number,
15    higherRate: Number,
16    baseLimit: Number,
17    higherLimit: Number,
18    eurConfig: String,
19    frequencies: Object,
20    currentFrequency: { type: String, default: 'monthly' }
21  }
22
23  connect () {
24    const content = document.getElementById(this.frequencyPopupTriggerTarget.getAttribute('data-bs-content-id')).innerHTML
25    new bootstrap.Popover(
26      this.frequencyPopupTriggerTarget,
27      { content: content, html: true, sanitize: false, trigger: 'click' }
28    )
29    this.calculateTaxRelief()
30    document.addEventListener('click', this.clickedOutsidePopover.bind(this))
31  }
32
33  disconnnect () {
34    document.removeEventListener('click', this.clickedOutsidePopover)
35  }
36
37  calculateTaxRelief (event) {
38    this.#hidePopover()
39
40    let frequency = this.currentFrequencyValue
41    if (event && event.params.frequency) {
42      frequency = event.params.frequency
43      this.#setRegularOption(frequency)
44      this.currentFrequencyValue = event.params.frequency
45    } else {
46      this.#setRegularOption(frequency)
47    }
48
49    const amountObj = currency(this.amountInputTarget.value)
50
51    /* Currently NOT taking into account contributions over one (tax) year if regular donation */
52    if (this.higherLimitValue && (amountObj.value > this.higherLimitValue)) {
53      this.ineligibleTextTarget.style.display = 'block'
54      this.deductionTextTarget.style.display = 'none'
55    } else {
56      this.ineligibleTextTarget.style.display = 'none'
57      this.deductionTextTarget.style.display = 'block'
58    }
59
60    this.#calculateEffectiveRelief(amountObj, frequency)
61    return
62  }
63
64  #setRegularOption (frequency) {
65    const title = this.frequenciesValue[frequency]
66    if (frequency != 'single') {
67      this.regularInputTarget.checked = true
68      this.regularInputTarget.id = `tax-calc-frequency-${frequency}`
69      this.regularInputTarget.value = frequency
70      this.regularInputTarget.dataset["numiko-TaxCalculatorFrequencyParam"] = frequency
71
72      this.regularLabelTarget.parentElement.htmlFor = `tax-calc-frequency-${frequency}`
73      this.regularLabelTarget.innerHTML = title
74    }
75  }
76
77  #hidePopover () {
78    bootstrap.Popover.getOrCreateInstance(this.frequencyPopupTriggerTarget).hide()
79  }
80
81  clickedOutsidePopover (event) {
82    if (!event.target.closest('.option')) { this.#hidePopover() }
83  }
84
85  #formatAmount (amount) {
86    const currencyConfig = JSON.parse(this.eurConfigValue)
87    const amountStr = currency(amount, Object.assign(
88      { formatWithSymbol: true },
89      currencyConfig,
90    )).format()
91    return `<em>${amountStr}</em>`
92  }
93
94  #annualizedAmount (amountObj, frequency) {
95    switch (frequency) {
96      case 'monthly':
97        return amountObj.multiply(12)
98      case 'quarterly':
99        return amountObj.multiply(4)
100      case 'biannually':
101        return amountObj.multiply(2)
102      default:
103        return amountObj
104    }
105  }
106
107  #calculateEffectiveRelief (amountObj, frequency) {
108    const annualAmount = this.#annualizedAmount(amountObj, frequency)
109    let deduction = null
110  
111    if (this.baseLimitValue && (annualAmount.value > this.baseLimitValue)) {
112      let baseAmount = currency(this.baseLimitValue)
113      let baseReduction = baseAmount.multiply(this.baseRateValue)
114      let higherAmount = annualAmount.subtract(baseAmount)
115      let higherReduction = higherAmount.multiply(this.higherRateValue)
116  
117      deduction = baseReduction.add(higherReduction)
118    } else {
119      deduction = annualAmount.multiply(this.baseRateValue).value
120    }
121    this.savingsTextTarget.innerHTML = this.#formatAmount(deduction)
122    let difference = annualAmount.subtract(deduction)
123    this.differenceTextTarget.innerHTML = this.#formatAmount(difference)
124  }
125}

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.