1class WBSCalculator { 2 constructor(el) { 3 this.container = el; 4 5 this.senateCalculatorUrl = 'https://ssl.stadtentwicklung.berlin.de/wohnen/wbs/wbsformular.shtml'; 6 this.currentImmoProjectsUrl = 'https://www.gewobag.de/bauen-in-berlin/bauprojekte#aktuell'; 7 this.searchSubscriptionUrl = 'https://www.gewobag.de/suchauftrag'; 8 this.officialRequestUrl = 'https://service.berlin.de/dienstleistung/120671/'; 9 this.privacyPolicyUrl = 'http://www.gewobag.de/datenschutz'; 10 this.apartmentsUrl = 'https://www.gewobag.de/mieten'; 11 12 this.form = this.container.querySelector('.js-dd-wbs-calculator-form'); 13 this.resultContainer = this.container.querySelector('.js-dd-wbs-calculator-result'); 14 this.resultComponent = this.resultContainer.querySelector('.gw-wbs-result'); 15 this.errorMessageContainer = this.container.querySelector('.js-dd-wbs-calculator-error'); 16 this.topAnchor = this.container.querySelector('.js-dd-wbs-calculator-top-anchor'); 17 this.disabledPersonsCount = 0; 18 this.init(); 19 } 20 21 init() { 22 this.initSubmitButton(); 23 this.initResetButton(); 24 this.initDynamicFields(); 25 this.initToggles(); 26 this.initChildrenSelect(); 27 this.initQuestionInfo(); 28 } 29 30 initSubmitButton() { 31 const submitButton = this.container.querySelector('.js-dd-wbs-calculator-submit'); 32 33 submitButton.addEventListener('click', (e) => { 34 e.preventDefault(); 35 const validation = this.validateForm(); 36 if (validation.validates) { 37 this.errorMessageContainer.innerHTML = ''; 38 this.showResult(); 39 } else { 40 this.errorMessageContainer.innerHTML = validation.errorMessages.join(' '); 41 } 42 }); 43 } 44 45 initResetButton() { 46 const resetButton = this.container.querySelector('.js-dd-wbs-calculator-reset'); 47 resetButton.addEventListener('click', (e) => { 48 e.preventDefault(); 49 this.resetForm(); 50 }); 51 } 52 53 initChildrenSelect() { 54 const childrenSelect = this.container.querySelector('.js-dd-wbs-calculator-children-count'); 55 const childrenExtraCostsSection = this.container.querySelector('.js-dd-wbs-calculator-children-extra-costs-section'); 56 57 childrenExtraCostsSection.classList.add('d-none'); 58 childrenSelect.addEventListener('change', (e) => { 59 const childrenCount = this.getFieldValue('dd-wbs-calculator-children-count', '0'); 60 childrenExtraCostsSection.classList[childrenCount > 0 ? 'remove' : 'add']('d-none'); 61 }) 62 } 63 64 validateForm() { 65 let errorMessages = []; 66 const netIncome = this.getFieldValue('dd-wbs-calculator-net-income', '0'); 67 68 if (netIncome === 0) errorMessages.push('Bitte geben Sie ihr monatliches Einkommen an.') 69 70 if (errorMessages.length > 0) { 71 return { 72 validates: false, 73 errorMessages 74 } 75 } else { 76 return { 77 validates: true 78 } 79 } 80 } 81 82 getFieldValue(fieldName, defaultValue = null) { 83 let field = this.container.querySelector(`[name=${fieldName}]`); 84 if (field.getAttribute('type') === 'radio') { 85 field = this.container.querySelector(`[name=${fieldName}]:checked`); 86 } 87 return parseInt(field ? field.value : defaultValue); 88 } 89 90 showResult() { 91 const income = this.calculate(); 92 const threshold = this.getThreshold(); 93 let wbsResult; 94 let wbsNumber = 0; 95 const wbsNumbers = [220, 180, 160, 140, 100]; 96 97 if (income < threshold) { 98 wbsNumber = 100; 99 } else if (income < threshold * 1.4) { 100 wbsNumber = 140; 101 } else if (income < threshold * 1.6) { 102 wbsNumber = 160; 103 } else if (income < threshold * 1.8) { 104 wbsNumber = 180; 105 } else if (income < threshold * 2.2) { 106 wbsNumber = 220; 107 } else { 108 wbsNumber = 0; 109 } 110 111 const resultMessage = this.container.querySelector('.js-dd-wbs-calculator-result-message'); 112 const resultNotice = this.container.querySelector('.js-dd-wbs-calculator-result-notice'); 113 const resultLevels = this.container.querySelector('.js-dd-wbs-calculator-result-levels'); 114 Array.from(resultLevels?.children || []).forEach(level => { 115 level.classList.remove('gw-wbs-result__level--active'); 116 }); 117 118 if (wbsNumber) { 119 const nthChild = resultLevels.querySelector(`:nth-child(${wbsNumbers.indexOf(wbsNumber) + 1})`); 120 if (nthChild) { 121 nthChild.classList.add('gw-wbs-result__level--active'); 122 } 123 wbsResult = 'Sie haben vermutlich Anspruch auf einen WBS ' + wbsNumber; 124 125 resultMessage.innerHTML = `Falls Sie noch keinen WBS besitzen oder beantragt haben, können Sie hier einen <a href="${this.officialRequestUrl}" target="_blank" rel="noreferrer noopener">WBS-Antrag stellen</a>. BesitzerInnen eines WBS ${wbsNumber} können sich neben freifinanzierten Wohnungen auch auf geförderte Wohnungen bewerben. Bitte beachten Sie unsere entsprechenden <a target="_blank" href="${this.apartmentsUrl}">Wohnungsinserate</a>. Hier können Sie einen entsprechenden <a href="${this.searchSubscriptionUrl}">Suchauftrag anlegen</a>. Beachten Sie insbesondere unsere <a href="${this.currentImmoProjectsUrl}">Neubauprojekte</a>, bei welchen ein groÃer Anteil der Wohnungen WBS-BesitzerInnen vorbehalten ist.`; 126 resultNotice.innerHTML = `<strong>Wichtig</strong>: Für die Bewerbung auf eine WBS-Wohnung ist es nicht ausreichend, dass ein/e BewerberIn die Einkommensgrenzen einhält oder einen WBS-Antrag gestellt hat. Der WBS muss zum Zeitpunkt der Bewerbung <strong>bereits vorliegen</strong>. Bevor Sie einen WBS-Antrag stellen, sollten Sie ebenfalls den <a target="_blank" href="${this.senate
126CalculatorUrl}">WBS-Rechner</a> des Berliner Senats heranziehen.<br><a href="${this.privacyPolicyUrl}">Disclaimer Datenschutz</a>`; 127 } else { 128 wbsResult = 'Sie haben vermutlich keinen Anspruch auf einen WBS'; 129 resultMessage.innerHTML = `Während wir Ihnen vermutlich keine belegungsgebundene Wohnung anbieten können, würden wir uns sehr über Ihre Bewerbung auf eine freifinanzierte Wohnung freuen. <br>Bitte beachten Sie unsere entsprechenden <a target="_blank" href="${this.apartmentsUrl}">Wohnungsinserate</a>. Hier können Sie einen entsprechenden <a href="${this.searchSubscriptionUrl}">Suchauftrag anlegen</a>. Beachten Sie insbesondere unsere <a href="${this.currentImmoProjectsUrl}">Neubauprojekte</a>, bei welchen ein Anteil der Wohnungen auch ohne WBS angemietet werden kann.`; 130 resultNotice.innerHTML = `<strong>Wichtig</strong>: Bitte überprüfen Sie Ihren WBS-Anspruch ebenfalls mit dem <a target="_blank" href="${this.senateCalculatorUrl}">WBS-Rechner</a> des Berliner Senats.<br><a href="${this.privacyPolicyUrl}">Disclaimer Datenschutz</a>`; 131 } 132 133 const resultTitle = this.container.querySelector('.js-dd-wbs-calculator-result-title'); 134 resultTitle.innerHTML = wbsResult; 135 this.resultContainer.classList.remove('d-none'); 136 this.resultComponent.classList.remove('gw-wbs-result--failed'); 137 if (wbsNumber === 0) this.resultComponent.classList.add('gw-wbs-result--failed'); 138 this.form.classList.add('d-none'); 139 this.topAnchor.scrollIntoView({ behavior: 'smooth' }); 140 } 141 142 getThreshold() { 143 let parentCount = this.getFieldValue('dd-wbs-calculator-parent-count'); 144 let childrenCount = this.getFieldValue('dd-wbs-calculator-children-count', '0'); 145 const personCount = parentCount + childrenCount; 146 let threshold = 12000; 147 148 threshold += personCount > 1 ? 6000 : 0; 149 threshold += personCount > 2 ? (personCount - 2) * 4100 : 0; 150 threshold += childrenCount * 500; 151 152 return threshold; 153 } 154 155 calculate() { 156 let parentCount = this.getFieldValue('dd-wbs-calculator-parent-count'); 157 let childrenCount = this.getFieldValue('dd-wbs-calculator-children-count', '0'); 158 let netIncome = this.getFieldValue('dd-wbs-calculator-net-income', '0'); 159 let childrenExtraCosts = this.getFieldValue('dd-wbs-calculator-extra-costs', '0'); 160 let childrenExtraCostsValue = this.getFieldValue('dd-wbs-calculator-children-extra-costs', '0'); 161 let alimony = this.getFieldValue('dd-wbs-calculator-alimony', '0'); 162 let partnerAlimonyValue = this.getFieldValue('dd-wbs-calculator-partner-alimony', '0'); 163 let childrenAlimonyValue = this.getFieldValue('dd-wbs-calculator-children-alimony', '0'); 164 let allowanceYoungCouples = this.getFieldValue('dd-wbs-calculator-allowance-young-couples'); 165 let disability = this.getFieldValue('dd-wbs-calculator-disability'); 166 167 let disabledPersonsStatus = (() => { 168 const fields = this.container.querySelectorAll('.js-dd-wbs-calculator-disabled-person-field'); 169 return Array.from(fields).map((field) => parseInt(field.value)); 170 })(); 171 172 const deductibleChildrenExtraCosts = Math.min(childrenCount > 0 && childrenExtraCosts ? childrenCount * 4000 : 0, (childrenCount > 0 && childrenExtraCosts && childrenExtraCostsValue > 0) ? childrenExtraCostsValue * 12 * 0.667 : 0); 173 const deductibleAlimony = alimony ? (childrenAlimonyValue > 0 ? childrenAlimonyValue * 12 : 0) + (partnerAlimonyValue > 0 ? partnerAlimonyValue * 12 : 0) : 0; 174 175 let result = (netIncome * 12) 176 - (parentCount === 1 ? 1230 : 2460) 177 - deductibleChildrenExtraCosts 178 - (childrenCount > 0 ? childrenCount * 600 : 0) 179 - deductibleAlimony 180 - (allowanceYoungCouples === 1 ? 4000 : 0); 181 182 if (disability === 1) { 183 disabledPersonsStatus.forEach(status => { 184 result = result - ([1, 2].includes(status) ? 4500 : (status === 3 ? 2100 : 0)); 185 }); 186 } 187 188 return result; 189 } 190 191 addDynamicField() { 192 const container = this.container.querySelector('.js-dd-wbs-calculator-disabled-person-fields'); 193 const field = this.container.querySelector('.js-dd-wbs-calculator-disabled-person-field-template').innerHTML; 194 const personField = document.createElement('div'); 195 this.disabledPersonsCount++; 196 197 personField.classList.add('third-set'); 198 personField.insertAdjacentHTML('beforeend', field); 199 200 const personLabel = personField.querySelector('.js-dd-wbs-calculator-disabled-person-field-label'); 201 personLabel.textContent = `Person ${this.disabledPersonsCount}`; 202 203 container.insertAdjacentHTML('beforeend', personField.outerHTML); 204 } 205 206 initDynamicFields() { 207 const button = this.container.querySelector('.js-dd-wbs-calculator-disabled-person-add'); 208 button.addEventListener('click', (e) => { 209 e.preventDefault(); 210 this.addDynamicField(); 211 }); 212 this.addDynamicField(); 213 } 214 215 initToggles() { 216 const toggles = this.container.querySelectorAll('.js-dd-wbs-calculator-toggle'); 217
218 toggles.forEach(toggle => { 219 const sections = toggle.dataset.toggle.split(',').map(section => this.container.querySelector(`.${section}`)); 220 const yesRadio = toggle.querySelector("[value='1']"); 221 const noRadio = toggle.querySelector("[value='0']"); 222 223 yesRadio.addEventListener("change", (e) => { 224 sections.forEach((section) => { 225 if (yesRadio.checked) { 226 section.classList.remove('d-none'); 227 } 228 }); 229 }); 230 231 noRadio.addEventListener("change", (e) => { 232 sections.forEach((section) => { 233 if (noRadio.checked) { 234 section.classList.add('d-none'); 235 } 236 }); 237 }); 238 239 sections.forEach((section) => { 240 section.classList.add('d-none'); 241 }); 242 }); 243 } 244 245 initQuestionInfo() { 246 const infoButtons = this.container.querySelectorAll('.js-dd-question-info'); 247 const closeAllButtons = () => infoButtons.forEach(infoButton => { 248 const overlay = infoButton.querySelector('.dd-question-info__overlay'); 249 overlay.classList.add('d-none'); 250 infoButton.classList.remove('dd-question-info--active'); 251 }); 252 infoButtons.forEach(infoButton => { 253 const overlay = infoButton.querySelector('.dd-question-info__overlay'); 254 const openOverlay = () => { 255 closeAllButtons(); 256 overlay.classList.remove('d-none'); 257 infoButton.classList.add('dd-question-info--active'); 258 }; 259 const closeOverlay = () => { 260 overlay.classList.add('d-none'); 261 infoButton.classList.remove('dd-question-info--active'); 262 }; 263 infoButton.addEventListener('click', () => infoButton.classList.contains('dd-question-info--active') ? closeOverlay() : openOverlay()); 264 infoButton.addEventListener('keydown', (event) => { 265 if (event.key === 'Enter' || event.key === ' ') { 266 event.preventDefault(); 267 if (infoButton.classList.contains('dd-question-info--active')) { 268 closeOverlay(); 269 } else { 270 openOverlay(); 271 } 272 } 273 if (event.key === 'Escape' || event.key === 'Tab') { 274 closeOverlay(); 275 } 276 }); 277 const closeButton = overlay.querySelector('.dd-question-info__overlay-close'); 278 closeButton.addEventListener('click', (e) => { 279 e.stopPropagation(); 280 closeOverlay(); 281 }); 282 }); 283 } 284 285 resetForm() { 286 this.form.classList.remove('d-none'); 287 this.resultContainer.classList.add('d-none'); 288 this.topAnchor.scrollIntoView({ behavior: 'smooth' }); 289 } 290} 291 292document.querySelectorAll('.js-dd-wbs-calculator').forEach(calculatorInstance => { 293 new WBSCalculator(calculatorInstance); 294});
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.