├── assets ├── css │ └── main.min.css ├── fonts │ ├── inter-v12-latin-500.woff2 │ ├── inter-v12-latin-600.woff2 │ ├── inter-v12-latin-800.woff2 │ └── inter-v12-latin-regular.woff2 ├── images │ ├── apple-touch-icon.png │ ├── banner-1544x500.png │ ├── banner-772x250.png │ ├── compounding-frequency.png │ ├── favicon.ico │ ├── icon-128.png │ ├── icon-128x128.png │ ├── icon-16.png │ ├── icon-192.png │ ├── icon-196.png │ ├── icon-256x256.png │ ├── icon-32.png │ ├── icon-48.png │ ├── icon-96.png │ ├── icon-og.png │ ├── icon-rounded.png │ ├── icon-square.png │ ├── illustration-1.png │ ├── illustration-1.svg │ ├── illustration-and.png │ ├── illustration-and.svg │ ├── illustration-els.png │ ├── illustration-els.svg │ ├── illustration-og.png │ ├── screenshot-1.png │ └── screenshot-2.png └── js │ ├── all-calculators.js │ ├── app.js │ ├── calculator.js │ ├── chart.js │ ├── datepicker.js │ ├── dialog-table.js │ ├── dropdown-icon.js │ ├── fractions.js │ ├── functions.js │ ├── hourpicker.js │ ├── lib │ ├── air-datepicker.js │ ├── chartjs │ │ ├── chart.js │ │ ├── chart.js.map │ │ ├── chart.min.js │ │ ├── chart.umd.js │ │ ├── chart.umd.js.map │ │ ├── chunks │ │ │ ├── helpers.core.d.ts │ │ │ ├── helpers.segment.js │ │ │ └── helpers.segment.js.map │ │ ├── helpers.d.ts │ │ ├── helpers.js │ │ ├── helpers.js.map │ │ └── types.d.ts │ ├── input-mask.min.js │ ├── katex │ │ ├── auto-render.min.js │ │ ├── fonts │ │ │ ├── KaTeX_AMS-Regular.ttf │ │ │ ├── KaTeX_AMS-Regular.woff │ │ │ ├── KaTeX_AMS-Regular.woff2 │ │ │ ├── KaTeX_Caligraphic-Bold.ttf │ │ │ ├── KaTeX_Caligraphic-Bold.woff │ │ │ ├── KaTeX_Caligraphic-Bold.woff2 │ │ │ ├── KaTeX_Caligraphic-Regular.ttf │ │ │ ├── KaTeX_Caligraphic-Regular.woff │ │ │ ├── KaTeX_Caligraphic-Regular.woff2 │ │ │ ├── KaTeX_Fraktur-Bold.ttf │ │ │ ├── KaTeX_Fraktur-Bold.woff │ │ │ ├── KaTeX_Fraktur-Bold.woff2 │ │ │ ├── KaTeX_Fraktur-Regular.ttf │ │ │ ├── KaTeX_Fraktur-Regular.woff │ │ │ ├── KaTeX_Fraktur-Regular.woff2 │ │ │ ├── KaTeX_Main-Bold.ttf │ │ │ ├── KaTeX_Main-Bold.woff │ │ │ ├── KaTeX_Main-Bold.woff2 │ │ │ ├── KaTeX_Main-BoldItalic.ttf │ │ │ ├── KaTeX_Main-BoldItalic.woff │ │ │ ├── KaTeX_Main-BoldItalic.woff2 │ │ │ ├── KaTeX_Main-Italic.ttf │ │ │ ├── KaTeX_Main-Italic.woff │ │ │ ├── KaTeX_Main-Italic.woff2 │ │ │ ├── KaTeX_Main-Regular.ttf │ │ │ ├── KaTeX_Main-Regular.woff │ │ │ ├── KaTeX_Main-Regular.woff2 │ │ │ ├── KaTeX_Math-BoldItalic.ttf │ │ │ ├── KaTeX_Math-BoldItalic.woff │ │ │ ├── KaTeX_Math-BoldItalic.woff2 │ │ │ ├── KaTeX_Math-Italic.ttf │ │ │ ├── KaTeX_Math-Italic.woff │ │ │ ├── KaTeX_Math-Italic.woff2 │ │ │ ├── KaTeX_SansSerif-Bold.ttf │ │ │ ├── KaTeX_SansSerif-Bold.woff │ │ │ ├── KaTeX_SansSerif-Bold.woff2 │ │ │ ├── KaTeX_SansSerif-Italic.ttf │ │ │ ├── KaTeX_SansSerif-Italic.woff │ │ │ ├── KaTeX_SansSerif-Italic.woff2 │ │ │ ├── KaTeX_SansSerif-Regular.ttf │ │ │ ├── KaTeX_SansSerif-Regular.woff │ │ │ ├── KaTeX_SansSerif-Regular.woff2 │ │ │ ├── KaTeX_Script-Regular.ttf │ │ │ ├── KaTeX_Script-Regular.woff │ │ │ ├── KaTeX_Script-Regular.woff2 │ │ │ ├── KaTeX_Size1-Regular.ttf │ │ │ ├── KaTeX_Size1-Regular.woff │ │ │ ├── KaTeX_Size1-Regular.woff2 │ │ │ ├── KaTeX_Size2-Regular.ttf │ │ │ ├── KaTeX_Size2-Regular.woff │ │ │ ├── KaTeX_Size2-Regular.woff2 │ │ │ ├── KaTeX_Size3-Regular.ttf │ │ │ ├── KaTeX_Size3-Regular.woff │ │ │ ├── KaTeX_Size3-Regular.woff2 │ │ │ ├── KaTeX_Size4-Regular.ttf │ │ │ ├── KaTeX_Size4-Regular.woff │ │ │ ├── KaTeX_Size4-Regular.woff2 │ │ │ ├── KaTeX_Typewriter-Regular.ttf │ │ │ ├── KaTeX_Typewriter-Regular.woff │ │ │ └── KaTeX_Typewriter-Regular.woff2 │ │ ├── katex.min.css │ │ └── katex.min.js │ ├── lazysizes.min.js │ └── math.min.js │ ├── mask.js │ ├── module.js │ ├── options.js │ ├── themes.js │ └── timepicker.js ├── ci_interest_calculator.php ├── index.html ├── plugin.php ├── readme.md └── readme.txt /assets/fonts/inter-v12-latin-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/fonts/inter-v12-latin-500.woff2 -------------------------------------------------------------------------------- /assets/fonts/inter-v12-latin-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/fonts/inter-v12-latin-600.woff2 -------------------------------------------------------------------------------- /assets/fonts/inter-v12-latin-800.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/fonts/inter-v12-latin-800.woff2 -------------------------------------------------------------------------------- /assets/fonts/inter-v12-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/fonts/inter-v12-latin-regular.woff2 -------------------------------------------------------------------------------- /assets/images/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/apple-touch-icon.png -------------------------------------------------------------------------------- /assets/images/banner-1544x500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/banner-1544x500.png -------------------------------------------------------------------------------- /assets/images/banner-772x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/banner-772x250.png -------------------------------------------------------------------------------- /assets/images/compounding-frequency.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/compounding-frequency.png -------------------------------------------------------------------------------- /assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/favicon.ico -------------------------------------------------------------------------------- /assets/images/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/icon-128.png -------------------------------------------------------------------------------- /assets/images/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/icon-128x128.png -------------------------------------------------------------------------------- /assets/images/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/icon-16.png -------------------------------------------------------------------------------- /assets/images/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/icon-192.png -------------------------------------------------------------------------------- /assets/images/icon-196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/icon-196.png -------------------------------------------------------------------------------- /assets/images/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/icon-256x256.png -------------------------------------------------------------------------------- /assets/images/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/icon-32.png -------------------------------------------------------------------------------- /assets/images/icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/icon-48.png -------------------------------------------------------------------------------- /assets/images/icon-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/icon-96.png -------------------------------------------------------------------------------- /assets/images/icon-og.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/icon-og.png -------------------------------------------------------------------------------- /assets/images/icon-rounded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/icon-rounded.png -------------------------------------------------------------------------------- /assets/images/icon-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/icon-square.png -------------------------------------------------------------------------------- /assets/images/illustration-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/illustration-1.png -------------------------------------------------------------------------------- /assets/images/illustration-and.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/illustration-and.png -------------------------------------------------------------------------------- /assets/images/illustration-els.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/illustration-els.png -------------------------------------------------------------------------------- /assets/images/illustration-og.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/illustration-og.png -------------------------------------------------------------------------------- /assets/images/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/screenshot-1.png -------------------------------------------------------------------------------- /assets/images/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/interest-calculator/96048f551bd19e68e23e3a306dee25c98f3aa901/assets/images/screenshot-2.png -------------------------------------------------------------------------------- /assets/js/app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | window._ = document.getElementById.bind(document); 4 | window.$ = document.querySelector.bind(document); 5 | window.$$ = document.querySelectorAll.bind(document); 6 | 7 | console.log('Script is OK! ༼ つ ◕_◕ ༽つ') 8 | // IS MAIN PAGE 9 | // if(window.location.pathname === '/') document.body.classList.add('isMainPage') 10 | 11 | // DIALOG TABLE 12 | if(document.querySelector('.result-table__dialog')) { 13 | import('./dialog-table.js').then(e => { 14 | console.log("Dialog table loaded (づ ◕‿◕ )づ") 15 | }).catch(e => { 16 | console.log("Sorry, dialog tables not loaded (ಥ﹏ಥ)", e) 17 | }) 18 | } 19 | // MASK 20 | if(document.querySelector('[data-inputmask]')) { 21 | import('./mask.js').then(e => { 22 | console.log("Mask loaded (づ ◕‿◕ )づ") 23 | }).catch(e => { 24 | console.log("Sorry, masks not loaded (ಥ﹏ಥ)", e) 25 | }) 26 | } 27 | // DATEPICKER 28 | if(document.querySelector('.datepicker')) { 29 | import('./datepicker.js').then(e => { 30 | console.log("Datepicker loaded (づ ◕‿◕ )づ") 31 | }).catch(e => { 32 | console.log("Sorry, datepicker not loaded (ಥ﹏ಥ)", e) 33 | }) 34 | } 35 | // TIMEPICKER 36 | if(document.querySelector('.timepicker-input')) { 37 | import('./timepicker.js').then(e => { 38 | console.log("Timepicker loaded (づ ◕‿◕ )づ") 39 | }).catch(e => { 40 | console.log("Sorry, timepicker not loaded (ಥ﹏ಥ)", e) 41 | }) 42 | } 43 | // HOURPICKER 44 | if(document.querySelector('.hourpicker-input')) { 45 | import('./hourpicker.js').then(e => { 46 | console.log("Hourpicker loaded (づ ◕‿◕ )づ") 47 | }).catch(e => { 48 | console.log("Sorry, Hourpicker not loaded (ಥ﹏ಥ)", e) 49 | }) 50 | } 51 | // OPTIONS 52 | if(document.querySelector('.calculator-content--options')) { 53 | import('./options.js').then(e => { 54 | console.log("Options loaded (づ ◕‿◕ )づ") 55 | }).catch(e => { 56 | console.log("Sorry, options not loaded (ಥ﹏ಥ)", e) 57 | }) 58 | } 59 | // ICON DROPDOWN 60 | if(document.querySelector('.dropdown-icon')) { 61 | import('./dropdown-icon.js').then(e => { 62 | console.log("Dropdown-icon loaded (づ ◕‿◕ )づ") 63 | }).catch(e => { 64 | console.log("Sorry, dropdown-icon not loaded (ಥ﹏ಥ)", e) 65 | }) 66 | } 67 | 68 | // MODAL 69 | const MODAL_OPENERS = document.querySelectorAll('.js-modal-open') 70 | const MODAL_CLOSERS = document.querySelectorAll('.js-modal-close') 71 | const MODAL_WINDOW = document.querySelector('.modal-window') 72 | 73 | let currentShare = 0 74 | let isOpenPopupHeader = false; 75 | let focusedElement = 0; 76 | 77 | const closeAll = () => { 78 | MODAL_SEARCH.value = '' 79 | MODAL_SEARCH_LIST.classList.remove('modal-search__list--error') 80 | MODAL_SEARCH_LIST.classList.remove('modal-search__list--active') 81 | MODAL_OPENERS.forEach(MODAL_OPENER => { 82 | const MODAL_ID = MODAL_OPENER.getAttribute('data-modal') 83 | const MODAL = document.querySelector(`.modal-${MODAL_ID}`) 84 | MODAL.classList.remove(`modal-${MODAL_ID}--active`) 85 | }) 86 | } 87 | 88 | window.addEventListener('click', (e) => { 89 | let controllerTheme = document.querySelector('#popup-theme') 90 | let controllerLang = document.querySelector('#popup-lang') 91 | if(isOpenPopupHeader && !(e.composedPath().includes(controllerLang) || e.composedPath().includes(controllerTheme))) { 92 | isOpenPopupHeader = false 93 | document.querySelector('.modal-lang--active')?.classList.remove(`modal-lang--active`) 94 | document.querySelector('.modal-theme--active')?.classList.remove(`modal-theme--active`) 95 | } 96 | }) 97 | 98 | window.addEventListener('keydown', (e) => { 99 | if(e.key === 'Escape') { 100 | MODAL_WINDOW.classList.remove('modal-window--active') 101 | closeAll() 102 | } 103 | 104 | if((e.metaKey && e.key === 'k') || (e.ctrlKey && e.key === 'k')) { 105 | closeAll(); 106 | $('.modal-search__input').focus(); 107 | MODAL_WINDOW.classList.add(`modal-window--active`) 108 | $('.modal-search').classList.toggle(`modal-search--active`); 109 | } 110 | 111 | if(!MODAL_WINDOW) return; 112 | 113 | if(e.key === 'ArrowUp') { 114 | let listItem = [...MODAL_SEARCH_LIST_CONTENT.children] 115 | if(focusedElement - 1 < 0) { 116 | focusedElement = listItem.length - 1 117 | } else { 118 | focusedElement-- 119 | } 120 | listItem[focusedElement]?.focus() 121 | // listItem[focusedElement].scrollIntoView(true) 122 | } 123 | 124 | if(e.key === 'ArrowDown') { 125 | let listItem = [...MODAL_SEARCH_LIST_CONTENT.children] 126 | listItem[focusedElement]?.focus() 127 | if(focusedElement + 1 > listItem.length - 1) { 128 | focusedElement = 0 129 | } else { 130 | focusedElement++ 131 | } 132 | } 133 | }) 134 | 135 | if(MODAL_WINDOW) { 136 | MODAL_WINDOW.addEventListener('click', (e) => { 137 | if(e.target.classList.contains('modal-window')) { 138 | MODAL_WINDOW.classList.remove(`modal-window--active`) 139 | MODAL_WINDOW.classList.remove(`modal-window--active-mini`) 140 | closeAll() 141 | } 142 | }) 143 | } 144 | if(MODAL_OPENERS.length > 0) { 145 | MODAL_OPENERS.forEach(MODAL_OPENER => { 146 | MODAL_OPENER.addEventListener('click', () => { 147 | const MODAL_ID = MODAL_OPENER.getAttribute('data-modal') 148 | const MODAL = document.querySelector(`.modal-${MODAL_ID}`) 149 | 150 | closeAll(); 151 | 152 | if(MODAL_ID !== 'lang' && MODAL_ID !== 'theme') MODAL_WINDOW.classList.toggle(`modal-window--active`) 153 | else isOpenPopupHeader = true 154 | 155 | if(MODAL_ID === 'search') document.querySelector('.modal-search__input').focus() 156 | if(MODAL_ID === 'share') { 157 | console.log($('.modal-share__textarea').value) 158 | $$('.modal-share__textarea')[currentShare].focus() 159 | $$('.modal-share__textarea')[currentShare].setSelectionRange(0, $$('.modal-share__textarea')[currentShare].value.length) 160 | } 161 | 162 | MODAL.classList.toggle(`modal-${MODAL_ID}--active`) 163 | }) 164 | }) 165 | } 166 | if(MODAL_CLOSERS.length > 0) { 167 | MODAL_CLOSERS.forEach(MODAL_CLOSER => { 168 | MODAL_CLOSER.addEventListener('click', () => { 169 | const MODAL_ID = MODAL_CLOSER.getAttribute('data-modal') 170 | const MODAL = document.querySelector(`.modal-${MODAL_ID}`) 171 | 172 | closeAll(); 173 | 174 | if(MODAL_ID !== 'lang' && MODAL_ID !== 'theme') MODAL_WINDOW.classList.toggle(`modal-window--active`) 175 | else isOpenPopupHeader = true 176 | 177 | MODAL.classList.remove(`modal-${MODAL_ID}--active`) 178 | }) 179 | }) 180 | } 181 | 182 | // THEME 183 | const light = document.querySelectorAll('.light-theme'); 184 | const dark = document.querySelectorAll('.dark-theme'); 185 | const system = document.querySelectorAll('.system-theme'); 186 | 187 | light.forEach((i) => 188 | i.addEventListener('click', () => { 189 | document.documentElement.classList = ''; 190 | localStorage.setItem('theme', 'light'); 191 | setActiveThemeButton('light') 192 | if(switchTheme !== null) { 193 | if(typeof switchTheme === 'object') { 194 | switchTheme.forEach(element => element('light')) 195 | } else { 196 | switchTheme('light') 197 | } 198 | } 199 | }), 200 | ); 201 | dark.forEach((i) => 202 | i.addEventListener('click', () => { 203 | document.documentElement.classList = 'dark'; 204 | localStorage.setItem('theme', 'dark'); 205 | setActiveThemeButton('dark') 206 | if(switchTheme !== null) { 207 | if(typeof switchTheme === 'object') { 208 | switchTheme.forEach(element => element('dark')) 209 | } else { 210 | switchTheme('dark') 211 | } 212 | } 213 | }), 214 | ); 215 | system.forEach((i) => 216 | i.addEventListener('click', () => { 217 | document.documentElement.classList = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : ''; 218 | localStorage.setItem('theme', 'system'); 219 | setActiveThemeButton('system') 220 | if(switchTheme !== null) { 221 | if(typeof switchTheme === 'object') { 222 | switchTheme.forEach(element => element(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')) 223 | } else { 224 | switchTheme(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') 225 | } 226 | } 227 | }), 228 | ); 229 | 230 | // SEARCH 231 | const MODAL_SEARCH = document.querySelector('.modal-search__input') 232 | const MODAL_SEARCH_LIST = document.querySelector('.modal-search__list') 233 | const MODAL_SEARCH_LIST_CONTENT = document.querySelector('.modal-search__list-content') 234 | 235 | if(MODAL_SEARCH) MODAL_SEARCH.addEventListener('input', (e) => { 236 | focusedElement = 0 237 | if(e.target.value.length > 0) { 238 | const FILTERED_JSON = JSON_CALCULATORS.filter(CALCULATOR => { 239 | return (CALCULATOR.description.toLowerCase().match(e.target.value.toLowerCase()) !== null) || 240 | CALCULATOR.title.toLowerCase().match(e.target.value.toLowerCase()) !== null; 241 | }) 242 | if(FILTERED_JSON.length > 0) { 243 | MODAL_SEARCH_LIST.classList.remove('modal-search__list--error') 244 | MODAL_SEARCH_LIST.classList.add('modal-search__list--active') 245 | MODAL_SEARCH_LIST_CONTENT.innerHTML = '' 246 | FILTERED_JSON.forEach(CALCULATOR => { 247 | let calculatorWrapper = document.createElement('a') 248 | calculatorWrapper.classList.add('modal-search-item') 249 | calculatorWrapper.href = CALCULATOR.uri 250 | 251 | let calculatorImg = document.createElement('img') 252 | calculatorImg.classList.add('modal-search-item__img') 253 | calculatorImg.src = CALCULATOR.image 254 | 255 | let calculatorTitle = document.createElement('span') 256 | calculatorTitle.classList.add('modal-search-item__text') 257 | calculatorTitle.innerText = CALCULATOR.title 258 | 259 | calculatorWrapper.append(calculatorImg, calculatorTitle) 260 | MODAL_SEARCH_LIST_CONTENT.append(calculatorWrapper) 261 | }) 262 | } else { 263 | MODAL_SEARCH_LIST_CONTENT.innerHTML = '' 264 | MODAL_SEARCH_LIST.classList.remove('modal-search__list--active') 265 | MODAL_SEARCH_LIST.classList.add('modal-search__list--error') 266 | } 267 | } else { 268 | MODAL_SEARCH_LIST_CONTENT.innerHTML = '' 269 | MODAL_SEARCH_LIST.classList.remove('modal-search__list--active') 270 | MODAL_SEARCH_LIST.classList.remove('modal-search__list--error') 271 | } 272 | }) 273 | 274 | // LANG 275 | const LANG_SELECTS = document.querySelectorAll('.header-lang__select') 276 | 277 | if (LANG_SELECTS){ 278 | let langIsEng = true 279 | 280 | for (const LANG_SELECT of LANG_SELECTS) { 281 | if(LANG_SELECT.innerText !== 'ENG' && window.location.href.match(LANG_SELECT.href) !== null) { 282 | LANG_SELECT.classList.add('header-lang__select--active') 283 | langIsEng = false 284 | } 285 | } 286 | 287 | if(langIsEng) LANG_SELECTS[0]?.classList?.add('header-lang__select--active') 288 | } 289 | 290 | // SHARE 291 | $$('.modal-share__button').forEach((button,index) => { 292 | button.addEventListener('click', () => { 293 | $$('.modal-share__button').forEach(i => i.classList.remove('modal-share__button--active')) 294 | $$('.modal-share__content').forEach(i => i.classList.remove('modal-share__content--active')) 295 | button.classList.add('modal-share__button--active') 296 | $$('.modal-share__content')[index].classList.add('modal-share__content--active') 297 | $$('.modal-share__textarea')[index].setSelectionRange(0, $$('.modal-share__textarea')[index].value.length) 298 | currentShare = index 299 | }) 300 | }) 301 | 302 | window.copyWidget = function (button) { 303 | button.classList.add('button--copy-active') 304 | navigator.clipboard.writeText($$('.modal-share__textarea')[currentShare].value) 305 | setTimeout(() => { 306 | button.classList.remove('button--copy-active') 307 | }, 1000) 308 | } 309 | 310 | window.now = function (suffix = 0){ 311 | let date = new Date(); 312 | date.setDate(date.getDate() + suffix); 313 | return ("0" + date.getHours()).slice(-2) + ':' + ("0" + date.getMinutes()).slice(-2) + ':' + ("0" + date.getSeconds()).slice(-2) 314 | } 315 | 316 | if(window.innerWidth < 1024){ 317 | $$('.calculator-content .button--primary').forEach((button) => { 318 | button.addEventListener('click', () => { 319 | let scrollValue = _('result-container').offsetTop; 320 | window.scrollTo({ top: scrollValue, behavior: 'smooth'}); 321 | }) 322 | }); 323 | } 324 | -------------------------------------------------------------------------------- /assets/js/calculator.js: -------------------------------------------------------------------------------- 1 | function calculate(){ 2 | const principal = input.get('starting_principal').gt(0).val(); 3 | const annualAddition = +input.get('annual_addition').val(); 4 | const monthlyAddition = +input.get('monthly_addition').val(); 5 | const contributeType = input.get('contribute').raw(); 6 | const interest = +input.get('annual_interest').val(); 7 | const years = +input.get('years').val(); 8 | const tax = +input.get('tax_rate').val(); 9 | const inflation = +input.get('inflation_rate').val(); 10 | if(!input.valid()) return; 11 | const amortization = calculateAmortization(principal, annualAddition, monthlyAddition, contributeType, interest, years, tax); 12 | 13 | let annualResults = []; 14 | let chartData = [[], [], [], []]; 15 | let annualInterest = 0; 16 | let annualContribution = 0; 17 | let monthlyResultsHtml = ''; 18 | let annualResultsHtml = ''; 19 | amortization.forEach((item, index) => { 20 | monthlyResultsHtml += ` 21 | ${index + 1} 22 | ${currencyFormat(item.contribution)} 23 | ${currencyFormat(item.interestPayment)} 24 | ${currencyFormat(item.endBalance)} 25 | `; 26 | annualInterest += item.interestPayment; 27 | annualContribution += item.contribution; 28 | if((index + 1) % 12 === 0 || (index + 1) === amortization.length){ 29 | let title = 'Year #{1} End'.replace('{1}', Math.ceil((index + 1) / 12).toString()); 30 | monthlyResultsHtml += `${title}`; 31 | } 32 | if((index + 1) % 12 === 0 || (index + 1) === amortization.length){ 33 | annualResults.push({ 34 | "interestPayment": annualInterest, 35 | "contribution": annualContribution, 36 | "endBalance": item.endBalance, 37 | "totalInterest": item.totalInterest, 38 | "totalContributions": item.totalContributions, 39 | }); 40 | annualInterest = 0; 41 | annualContribution = 0; 42 | } 43 | }); 44 | 45 | let chartLegendHtml = ''; 46 | for(let i = 0; i <= years / 5; i++){ 47 | chartLegendHtml += `

${i * 5} yr

`; 48 | } 49 | if(years % 5 !== 0){ 50 | chartLegendHtml += `

${years} yr

`; 51 | } 52 | annualResults.forEach((r, index) => { 53 | annualResultsHtml += ` 54 | ${index + 1} 55 | ${currencyFormat(r.contribution)} 56 | ${currencyFormat(r.interestPayment)} 57 | ${currencyFormat(r.endBalance)} 58 | `; 59 | chartData[0].push((index + 1)); 60 | chartData[1].push(+principal.toFixed(2)); 61 | chartData[2].push(+r.totalInterest.toFixed(2)); 62 | chartData[3].push(+r.totalContributions.toFixed(2)); 63 | }); 64 | 65 | const totalInterest = amortization[amortization.length - 1].totalInterest; 66 | const totalContributions = amortization[amortization.length - 1].totalContributions; 67 | const endBalance = amortization[amortization.length - 1].endBalance; 68 | const interestWithTax = totalInterest * 1 / ((100 - tax) / 100); 69 | const taxValue = interestWithTax - totalInterest; 70 | const totalValue = interestWithTax + totalContributions + principal; 71 | const interestWithoutTaxPercent = totalInterest / totalValue * 100; 72 | const taxPercent = taxValue / totalValue * 100; 73 | const contributionsPercent = totalContributions / totalValue * 100; 74 | const principalPercent = principal / totalValue * 100; 75 | changeChartData([roundTo(principalPercent, 0), roundTo(interestWithoutTaxPercent, 0), roundTo(contributionsPercent, 0), roundTo(taxPercent, 0)], chartData); 76 | _('chart__legend').innerHTML = chartLegendHtml; 77 | output.val(annualResultsHtml).set('annual-results'); 78 | output.val(monthlyResultsHtml).set('monthly-results'); 79 | output.val('End Balance: $135,479.01').replace('$135,479.01', currencyFormat(endBalance)).set('end-balance'); 80 | output.val('Total Principal: $99,000.00').replace('$99,000.00', currencyFormat(totalContributions + principal)).set('total-contribution'); 81 | output.val('Total Interest: $39,224.74').replace('$39,224.74', currencyFormat(interestWithTax)).set('total-interest'); 82 | output.val('Total Interest after Tax: $36,479.01').replace('$36,479.01', currencyFormat(totalInterest)).set('total-interest-after-tax'); 83 | output.val('After Inflation Adjustment: $100,809.11').replace('$100,809.11', currencyFormat(backwardFlatRateInflationCalculator(endBalance, years, inflation))).set('balance-with-inflation'); 84 | } 85 | function currencyFormat(num) { 86 | return '$' + num.toFixed(2).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,') 87 | } 88 | 89 | function calculateAmortization(principal, annualAddition, monthlyAddition, contributeType, interest, years, tax){ 90 | const monthlyInterest = interest / 12; 91 | const months = years * 12; 92 | let balance = principal; 93 | let totalInterest = 0; 94 | let totalContributions = 0; 95 | let amortization = []; 96 | for(let i = 0; i < months; i++){ 97 | let contribution = i === 0 ? principal : 0; 98 | if(contributeType === 'beginning'){ 99 | balance += monthlyAddition; 100 | totalContributions += monthlyAddition; 101 | contribution += monthlyAddition; 102 | if(i % 12 === 0){ 103 | balance += annualAddition; 104 | totalContributions += annualAddition; 105 | contribution += annualAddition; 106 | } 107 | } 108 | 109 | let interestPayment = balance * monthlyInterest / 100; 110 | interestPayment = interestPayment - interestPayment * tax / 100 111 | totalInterest += interestPayment; 112 | const beginBalance = balance; 113 | balance += interestPayment; 114 | 115 | if(contributeType === 'end'){ 116 | balance += monthlyAddition; 117 | totalContributions += monthlyAddition; 118 | contribution += monthlyAddition; 119 | if((i + 1) % 12 === 0){ 120 | balance += annualAddition; 121 | totalContributions += annualAddition; 122 | contribution += annualAddition; 123 | } 124 | } 125 | amortization.push({ 126 | beginBalance, 127 | contribution, 128 | interestPayment, 129 | totalInterest, 130 | endBalance: balance, 131 | totalContributions 132 | }); 133 | } 134 | return amortization; 135 | } 136 | 137 | 138 | function backwardFlatRateInflationCalculator(futureValue, years, inflationRate) { 139 | const rate = inflationRate / 100; 140 | 141 | return futureValue / Math.pow(1 + rate, years); 142 | } 143 | -------------------------------------------------------------------------------- /assets/js/chart.js: -------------------------------------------------------------------------------- 1 | // CHART_DONUT_BIG CHART_LOAN 2 | 'use strict' 3 | 4 | let switchTheme = null; 5 | let theme = 'light'; 6 | if (localStorage.getItem('theme') === 'dark' || (localStorage.getItem('theme') === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches)) theme = 'dark'; 7 | 8 | const colors = { 9 | light: { 10 | purple: '#A78BFA', 11 | yellow: '#FBBF24', 12 | sky: '#7DD3FC', 13 | blue: '#1D4ED8', 14 | red: '#F87171', 15 | textColor: '#6B7280', 16 | yellowGradientStart: 'rgba(250, 219, 139, 0.33)', 17 | purpleGradientStart: 'rgba(104, 56, 248, 0.16)', 18 | skyGradientStart: 'rgba(56, 187, 248, 0.16)', 19 | tealGradientStart: 'rgba(56, 248, 222, 0.16)', 20 | yellowGradientStop: 'rgba(250, 219, 139, 0)', 21 | purpleGradientStop: 'rgba(104, 56, 248, 0)', 22 | skyGradientStop: 'rgba(56, 248, 222, 0.16)', 23 | gridColor: '#DBEAFE', 24 | tooltipBackground: '#fff', 25 | fractionColor: '#EDE9FE', 26 | }, 27 | dark: { 28 | purple: '#7C3AED', 29 | yellow: '#D97706', 30 | sky: '#0284C7', 31 | blue: '#101E47', 32 | red: '#F87171', 33 | textColor: '#fff', 34 | yellowGradientStart: 'rgba(146, 123, 67, 0.23)', 35 | purpleGradientStart: 'rgba(78, 55, 144, 0.11)', 36 | skyGradientStart: 'rgba(56, 187, 248, 0.16)', 37 | tealGradientStart: 'rgba(56, 248, 222, 0.16)', 38 | yellowGradientStop: 'rgba(250, 219, 139, 0)', 39 | purpleGradientStop: 'rgba(104, 56, 248, 0)', 40 | skyGradientStop: 'rgba(56, 248, 222, 0.16)', 41 | gridColor: '#162B64', 42 | tooltipBackground: '#1C3782', 43 | fractionColor: '#41467D', 44 | }, 45 | }; 46 | 47 | let data = [ 48 | { 49 | data: [18, 26, 54, 2], 50 | labels: ['18%', '26%', '54%', '2%'], 51 | backgroundColor: [colors[theme].yellow, colors[theme].purple, colors[theme].sky, colors[theme].red], 52 | borderColor: '#DDD6FE', 53 | borderWidth: 0, 54 | }, 55 | ]; 56 | 57 | let options = { 58 | rotation: 0, 59 | cutout: '37%', 60 | hover: {mode: null}, 61 | responsive: false, 62 | layout: { 63 | padding: 30, 64 | }, 65 | plugins: { 66 | tooltip: { 67 | enabled: false, 68 | }, 69 | legend: { 70 | display: false, 71 | }, 72 | }, 73 | }; 74 | 75 | const customDataLabels = { 76 | id: 'customDataLabel', 77 | afterDatasetDraw(chart, args, pluginOptions) { 78 | const { 79 | ctx, 80 | data 81 | } = chart; 82 | ctx.save(); 83 | 84 | data.datasets[0].data.forEach((datapoint, index) => { 85 | const { x, y } = chart.getDatasetMeta(0).data[index].tooltipPosition(); 86 | 87 | ctx.textAlign = 'center'; 88 | ctx.font = '14px Inter'; 89 | ctx.fillStyle = '#fff'; 90 | ctx.textBaseline = 'middle'; 91 | let toolTipText = datapoint != '0' ? datapoint + '%' : ''; 92 | ctx.fillText(toolTipText, x, y); 93 | }); 94 | }, 95 | }; 96 | 97 | let donutBig = new Chart(document.getElementById('chartDonutBig'), { 98 | type: 'doughnut', 99 | data: { 100 | datasets: data, 101 | }, 102 | options: options, 103 | plugins: [customDataLabels], 104 | }); 105 | 106 | let switchThemeDonut = function(theme) { 107 | donutBig.destroy() 108 | 109 | const customDataLabels = { 110 | id: 'customDataLabel', 111 | afterDatasetDraw(chart, args, pluginOptions) { 112 | const { 113 | ctx, 114 | data, 115 | chartArea: { top, bottom, left, right, width, height }, 116 | } = chart; 117 | ctx.save(); 118 | 119 | data.datasets[0].data.forEach((datapoint, index) => { 120 | const { x, y } = chart.getDatasetMeta(0).data[index].tooltipPosition(); 121 | 122 | ctx.textAlign = 'center'; 123 | ctx.font = '14px Inter'; 124 | ctx.fillStyle = '#fff'; 125 | ctx.textBaseline = 'middle'; 126 | let toolTipText = datapoint != '0' ? datapoint + '%' : ''; 127 | ctx.fillText(toolTipText, x, y); 128 | }); 129 | }, 130 | }; 131 | 132 | donutBig = new Chart(document.getElementById('chartDonutBig'), { 133 | type: 'doughnut', 134 | data: { 135 | datasets: data, 136 | }, 137 | options: options, 138 | plugins: [customDataLabels], 139 | }); 140 | 141 | donutBig.data.datasets[0].backgroundColor = [colors[theme].yellow, colors[theme].purple, colors[theme].sky, colors[theme].red]; 142 | donutBig.update() 143 | } 144 | 145 | // LOAN CHART 146 | 147 | let ctx = document.getElementById('chartLoan').getContext('2d'); 148 | 149 | let yellowGradient = ctx.createLinearGradient(0, 0, 0, 1024); 150 | yellowGradient.addColorStop(0, colors[theme].yellowGradientStart); 151 | yellowGradient.addColorStop(1, colors[theme].yellowGradientStop); 152 | 153 | let purpleGradient = ctx.createLinearGradient(0, 0, 0, 1024); 154 | purpleGradient.addColorStop(0, colors[theme].purpleGradientStart); 155 | purpleGradient.addColorStop(1, colors[theme].purpleGradientStop); 156 | 157 | let skyGradient = ctx.createLinearGradient(0, 0, 0, 1024); 158 | skyGradient.addColorStop(0, colors[theme].skyGradientStart); 159 | skyGradient.addColorStop(1, colors[theme].skyGradientStop); 160 | 161 | let tooltip = { 162 | enabled: false, 163 | external: function (context) { 164 | let tooltipEl = document.getElementById('chartjs-tooltip'); 165 | 166 | // Create element on first render 167 | if (!tooltipEl) { 168 | tooltipEl = document.createElement('div'); 169 | tooltipEl.id = 'chartjs-tooltip'; 170 | tooltipEl.innerHTML = '
'; 171 | document.body.appendChild(tooltipEl); 172 | } 173 | 174 | // Hide if no tooltip 175 | const tooltipModel = context.tooltip; 176 | if (tooltipModel.opacity === 0) { 177 | tooltipEl.style.opacity = 0; 178 | return; 179 | } 180 | 181 | // Set caret Position 182 | tooltipEl.classList.remove('above', 'below', 'no-transform'); 183 | if (tooltipModel.yAlign) { 184 | tooltipEl.classList.add(tooltipModel.yAlign); 185 | } else { 186 | tooltipEl.classList.add('no-transform'); 187 | } 188 | 189 | function getBody(bodyItem) { 190 | return bodyItem.lines; 191 | } 192 | 193 | if (tooltipModel.body) { 194 | const bodyLines = tooltipModel.body.map(getBody); 195 | 196 | let innerHtml = ''; 197 | 198 | let year = +(Number(tooltipModel.title) * 12).toFixed(0); 199 | let months = +(year % 12).toFixed(0); 200 | let yearText = `Year ${(year - months) / 12}`; 201 | let monthText = months === 0 ? '' : `, Month ${months}`; 202 | innerHtml += '' + yearText + monthText + ''; 203 | 204 | innerHtml += ''; 205 | bodyLines.forEach(function (body, i) { 206 | innerHtml += '' + body + ''; 207 | }); 208 | innerHtml += ''; 209 | 210 | let tableRoot = tooltipEl.querySelector('table'); 211 | tableRoot.innerHTML = innerHtml; 212 | } 213 | 214 | const position = context.chart.canvas.getBoundingClientRect(); 215 | 216 | // Display, position, and set styles for font 217 | tooltipEl.style.opacity = 1; 218 | tooltipEl.style.position = 'absolute'; 219 | tooltipEl.style.left = position.left + window.pageXOffset + tooltipModel.caretX - tooltipEl.clientWidth / 2 + 'px'; 220 | tooltipEl.style.top = position.top + window.pageYOffset + tooltipModel.caretY - tooltipEl.clientHeight / 2 + 'px'; 221 | // tooltipEl.style.font = bodyFont.string; 222 | tooltipEl.classList.add('loan-chart'); 223 | }, 224 | }; 225 | 226 | const dataCharts = { 227 | labels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 228 | datasets: [ 229 | { 230 | data: [ 231 | 25000, 232 | 25000, 233 | 25000, 234 | 25000, 235 | 25000, 236 | 25000, 237 | 25000, 238 | 25000, 239 | 25000, 240 | 25000 241 | ], 242 | type: 'line', 243 | order: 1, 244 | label: 'Initial investment', 245 | pointHoverBackgroundColor: '#FFFFFF', 246 | pointHoverBorderWidth: 2, 247 | pointHoverRadius: 6, 248 | pointHoverBorderColor: '#5045E5', 249 | stacked: true, 250 | borderColor: colors[theme].yellow, 251 | backgroundColor: yellowGradient, 252 | fill: true, 253 | }, 254 | { 255 | label: 'Interest after tax', 256 | data: [ 257 | 1486.44, 258 | 3395.01, 259 | 5745.78, 260 | 8559.75, 261 | 11858.93, 262 | 15666.35, 263 | 20006.18, 264 | 24903.69, 265 | 30385.38, 266 | 36479.01 267 | ], 268 | type: 'line', 269 | order: 1, 270 | pointHoverBackgroundColor: '#FFFFFF', 271 | pointHoverBorderWidth: 2, 272 | pointHoverRadius: 6, 273 | pointHoverBorderColor: '#5045E5', 274 | stacked: true, 275 | borderColor: colors[theme].purple, 276 | backgroundColor: purpleGradient, 277 | fill: true, 278 | }, 279 | { 280 | label: 'Contributions', 281 | data: [ 282 | 7400, 283 | 14800, 284 | 22200, 285 | 29600, 286 | 37000, 287 | 44400, 288 | 51800, 289 | 59200, 290 | 66600, 291 | 74000 292 | ], 293 | type: 'line', 294 | order: 1, 295 | pointHoverBackgroundColor: '#FFFFFF', 296 | pointHoverBorderWidth: 2, 297 | pointHoverRadius: 6, 298 | pointHoverBorderColor: '#5045E5', 299 | stack: 'combined', 300 | stacked: true, 301 | borderColor: colors[theme].sky, 302 | backgroundColor: skyGradient, 303 | fill: true, 304 | }, 305 | ], 306 | }; 307 | 308 | let chartLoan = new Chart(document.getElementById('chartLoan'), { 309 | data: dataCharts, 310 | options: { 311 | stepSize: 1, 312 | response: true, 313 | elements: { 314 | point: { 315 | radius: 0, 316 | }, 317 | }, 318 | plugins: { 319 | legend: { 320 | display: false, 321 | }, 322 | tooltip: tooltip, 323 | }, 324 | interaction: { 325 | mode: 'index', 326 | intersect: false, 327 | }, 328 | scales: { 329 | y: { 330 | grid: { 331 | tickLength: 0, 332 | color: colors[theme].gridColor, 333 | }, 334 | ticks: { 335 | display: false, 336 | stepSize: 1, 337 | }, 338 | border: { 339 | color: colors[theme].gridColor, 340 | }, 341 | }, 342 | x: { 343 | border: { 344 | color: colors[theme].gridColor, 345 | }, 346 | ticks: { 347 | display: false, 348 | color: colors[theme].gridColor, 349 | stepSize: 1, 350 | }, 351 | grid: { 352 | tickLength: 0, 353 | color: colors[theme].gridColor, 354 | }, 355 | }, 356 | }, 357 | }, 358 | }); 359 | 360 | let switchThemeLoan = function(theme) { 361 | yellowGradient.addColorStop(0, colors[theme].yellowGradientStart); 362 | yellowGradient.addColorStop(1, colors[theme].yellowGradientStop); 363 | purpleGradient.addColorStop(0, colors[theme].purpleGradientStart); 364 | purpleGradient.addColorStop(1, colors[theme].purpleGradientStop); 365 | chartLoan.data.datasets[0].backgroundColor = yellowGradient; 366 | chartLoan.data.datasets[0].borderColor = colors[theme].yellow; 367 | chartLoan.data.datasets[1].backgroundColor = purpleGradient; 368 | chartLoan.data.datasets[1].borderColor = colors[theme].purple; 369 | chartLoan.data.datasets[2].backgroundColor = skyGradient; 370 | chartLoan.options.scales.y.grid.color = colors[theme].gridColor; 371 | chartLoan.options.scales.x.grid.color = colors[theme].gridColor; 372 | chartLoan.options.scales.y.ticks.color = colors[theme].gridColor; 373 | chartLoan.options.scales.x.ticks.color = colors[theme].gridColor; 374 | chartLoan.options.scales.y.border.color = colors[theme].gridColor; 375 | chartLoan.options.scales.x.border.color = colors[theme].gridColor; 376 | chartLoan.update() 377 | } 378 | 379 | window.changeChartData = function(values, values_two) { 380 | donutBig.data.datasets[0].data = values 381 | donutBig.data.datasets[0].labels = values.map(value => `${value}%`) 382 | donutBig.update() 383 | 384 | chartLoan.data.labels = values_two[0] 385 | chartLoan.data.datasets[0].data = values_two[1] 386 | chartLoan.data.datasets[1].data = values_two[2] 387 | chartLoan.data.datasets[2].data = values_two[3] 388 | chartLoan.update() 389 | } 390 | 391 | 392 | switchTheme = [switchThemeLoan, switchThemeDonut] 393 | -------------------------------------------------------------------------------- /assets/js/datepicker.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll('.datepicker_us').forEach((datepicker) => { 2 | new AirDatepicker(datepicker, { 3 | dateFormat: 'MM/dd/yyyy', 4 | autoClose: true, 5 | onSelect: function(event){ 6 | return updateDate(event.datepicker.$el); 7 | }, 8 | // visible: true, 9 | prevHtml: 10 | '', 11 | nextHtml: 12 | '', 13 | locale: { 14 | months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], 15 | monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], 16 | days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], 17 | daysShort: ['S', 'M', 'T', 'W', 'T', 'F', 'S'], 18 | daysMin: ['S', 'M', 'T', 'W', 'T', 'F', 'S'], 19 | }, 20 | }); 21 | }); 22 | 23 | document.querySelectorAll('.datepicker').forEach((datepicker) => { 24 | new AirDatepicker(datepicker, { 25 | dateFormat: 'yyyy-MM-dd', 26 | autoClose: true, 27 | onSelect: function(event){ 28 | return updateDate(event.datepicker.$el); 29 | }, 30 | // visible: true, 31 | prevHtml: 32 | '', 33 | nextHtml: 34 | '', 35 | locale: { 36 | months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], 37 | monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], 38 | days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], 39 | daysShort: ['S', 'M', 'T', 'W', 'T', 'F', 'S'], 40 | daysMin: ['S', 'M', 'T', 'W', 'T', 'F', 'S'], 41 | }, 42 | }); 43 | }); -------------------------------------------------------------------------------- /assets/js/dialog-table.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | $$('.result-table__dialog').forEach(element => { 4 | const ELEMENT_OPEN = element.querySelector('.result-table__open'); 5 | ELEMENT_OPEN.addEventListener('click', () => { 6 | element.classList.toggle('result-table__dialog--active'); 7 | }) 8 | }) -------------------------------------------------------------------------------- /assets/js/dropdown-icon.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const setSelect = (list, index) => { 4 | list.forEach(item => { 5 | item.classList.remove('dropdown-select--selected') 6 | }) 7 | list[index].classList.add('dropdown-select--selected') 8 | } 9 | 10 | const setValue = (input, value, index) => { 11 | if(index !== undefined) { 12 | setTab(index) 13 | } 14 | input.innerHTML = value 15 | } 16 | 17 | const setTab = (index) => { 18 | document.querySelectorAll(`[data-tab]`)?.forEach(element => { 19 | if(element.getAttribute('data-tab') == index) { 20 | element.classList.add('tab--active'); 21 | } else { 22 | element.classList.remove('tab--active'); 23 | element.classList.remove('tab--active') 24 | } 25 | }) 26 | } 27 | 28 | const DROPDOWN_WRAPPER = document.querySelectorAll('.dropdown-wrapper') 29 | 30 | 31 | DROPDOWN_WRAPPER.forEach(wrapper => { 32 | // CLOSE DROPDOWN WHEN CLICK OUTSIDE 33 | window.addEventListener('click', (e) => { 34 | if(!(e.composedPath().includes(wrapper))) { 35 | wrapper.classList.remove('dropdown-wrapper--active') 36 | } 37 | }) 38 | 39 | const DROPDOWN_FIELD = wrapper.querySelector('.input-field') 40 | const DROPDOWN_VALUE = wrapper.querySelector('.input-field__text') 41 | const DROPDOWN_SELECTS = wrapper.querySelectorAll('.dropdown-select') 42 | // DEFAULT TAB 43 | setTab(0) 44 | 45 | // OPEN DROPDOWN WHEN CLICK ON INPUT 46 | DROPDOWN_FIELD.addEventListener('click', () => { 47 | // CLOSE OTHERS DROPDOWN 48 | if(!wrapper.classList.contains('dropdown-wrapper--active')) { 49 | DROPDOWN_WRAPPER.forEach(wrapper => {wrapper.classList.remove('dropdown-wrapper--active')}) 50 | } 51 | // OPEN THIS DROPDOWN 52 | wrapper.classList.toggle('dropdown-wrapper--active') 53 | }) 54 | 55 | // SET EVENT LISTENERS FOR SELECT 56 | DROPDOWN_SELECTS.forEach((select, index) => { 57 | const SELECT_VALUE = select.innerHTML 58 | select.addEventListener('click', () => { 59 | wrapper.classList.remove('dropdown-wrapper--active') 60 | 61 | // SET SELECT FOR DROPDOWN 62 | setSelect(DROPDOWN_SELECTS, index) 63 | 64 | // SET VALUE FOR DROPDOWN 65 | if(select.classList.contains('dropdown-select--tab')) { 66 | // IF DROPDOWN IS DROPDOWN TAB : example due-date-calculator dropdown "Calculate based on" 67 | setValue(DROPDOWN_VALUE,SELECT_VALUE, index) 68 | } else { 69 | setValue(DROPDOWN_VALUE,SELECT_VALUE) 70 | } 71 | }) 72 | }) 73 | }) -------------------------------------------------------------------------------- /assets/js/fractions.js: -------------------------------------------------------------------------------- 1 | const Fractions = { 2 | // helpers 3 | isCorrectMixed(whole, num, denom){ 4 | return !(!whole && !num && !denom || !num && denom || num && !denom); 5 | }, 6 | buildFrac(whole, num, denom){ 7 | whole = Number(whole); 8 | num = Number(num); 9 | denom = Number(denom); 10 | return num && denom ? 11 | math.fraction( 12 | (math.sign(whole) || 1) * (math.sign(num) || 1) * (math.sign(denom) || 1) * 13 | (math.abs(whole) * math.abs(denom) + math.abs(num)), math.abs(denom) 14 | ) : 15 | math.fraction(whole, 1) 16 | ; 17 | }, 18 | getFracPart(frac){ 19 | return frac.d != 1 ? { 20 | num: frac.n % frac.d, denom: frac.d, 21 | } : ''; 22 | }, 23 | getWholePart(frac){ 24 | const sign = frac.s == -1 ? '-' : ''; 25 | const wholePart = (frac.n - frac.n % frac.d) / frac.d; 26 | if(wholePart == 0 && this.getFracPart(frac)) return sign; 27 | return sign + wholePart; 28 | }, 29 | getChartData(frac){ 30 | const fracPart = this.getFracPart(frac); 31 | const wholePart = this.getWholePart(frac); 32 | return fracPart ? [fracPart.num, fracPart.denom-fracPart.num] : (wholePart == 0 ? [0,1]:[1,0]); 33 | }, 34 | outputFrac(frac, prefix){ 35 | const isWhole = frac.d == 1; 36 | const sign = frac.s == -1 ? '-' : ''; 37 | _(prefix+'Whole').classList[!isWhole && !sign ? 'add':'remove']('hidden'); 38 | _(prefix+'Whole').innerText = sign + (isWhole ? frac.n : ''); 39 | _(prefix+'Frac').classList[isWhole?'add':'remove']('hidden'); 40 | _(prefix+'Num').innerText = frac.n; 41 | _(prefix+'Denom').innerText = frac.d; 42 | }, 43 | outputMixed(frac, prefix){ 44 | const fracPart = this.getFracPart(frac); 45 | const wholePart = this.getWholePart(frac); 46 | _(prefix+'Whole').classList[!wholePart?'add':'remove']('hidden'); 47 | _(prefix+'Whole').innerText = wholePart; 48 | _(prefix+'Frac').classList[!fracPart?'add':'remove']('hidden'); 49 | _(prefix+'Num').innerText = fracPart.num; 50 | _(prefix+'Denom').innerText = fracPart.denom; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /assets/js/functions.js: -------------------------------------------------------------------------------- 1 | window.log = console.log; 2 | 3 | window.setValue = function (inputId, value, index){ 4 | _(inputId).value = value; 5 | _(inputId).index = index; 6 | } 7 | 8 | window.switcher = function(button, id, action) { 9 | $('#' + id).value = button.value; 10 | $('#' + id).onchange && $('#' + id).onchange(button.value); 11 | $('#' + id + '-a')?.classList.remove("button-switcher--active"); 12 | $('#' + id + '-b')?.classList.remove("button-switcher--active"); 13 | $('#' + id + '-c')?.classList.remove("button-switcher--active"); 14 | $('#' + id + '-d')?.classList.remove("button-switcher--active"); 15 | $('#' + id + '-e')?.classList.remove("button-switcher--active"); 16 | button.classList.add("button-switcher--active"); 17 | toggleRelatedInputs(button, id, action); 18 | } 19 | 20 | window.toggleRelatedInputs = function(element, id, action){ 21 | element = element instanceof Event ? element.target : element 22 | id = id ?? element.id; 23 | let value = element.value; 24 | if(element.type == 'select-one') value = element.selectedIndex; 25 | $$('.' + id).forEach(element => { 26 | if(action === "disabled") { 27 | element.classList.remove("disabled"); 28 | } else if (action === "finding") { 29 | element.classList.remove("disabled"); 30 | if(element.querySelector('.input-field__input').value === "???") { 31 | element.querySelector('.input-field__input').value = "" 32 | } 33 | } else { 34 | element.classList.add("related-item-hidden"); 35 | } 36 | }); 37 | $$('.related-to-' + id + '-' + value)?.forEach(element => { 38 | if(action === "disabled") { 39 | element.classList.add("disabled"); 40 | } else if (action === "finding") { 41 | element.querySelector('.input-field__input').value = "???" 42 | element.classList.add("disabled"); 43 | } else { 44 | element.classList.remove("related-item-hidden"); 45 | } 46 | }); 47 | } 48 | 49 | window.switch_data_tab = function(owner, index, value){ 50 | $$('[data-tab]')?.forEach(element => { 51 | if(element.getAttribute('data-tab') == index) { 52 | element.classList.add('tab--active'); 53 | owner.classList.add('tab--active'); 54 | } else { 55 | element.classList.remove('tab--active'); 56 | } 57 | }); 58 | } 59 | 60 | window.isMetricSystem = function() { 61 | return $('.system-switcher').classList.contains('system-switcher--active'); 62 | } 63 | 64 | window.setSystem = function(system) { 65 | const add = (system == "metric" ? ".imperial-system-item" : ".metric-system-item"); 66 | $$(".system-item-hidden").forEach(element => { 67 | element.classList.remove("system-item-hidden"); 68 | }); 69 | $$(add).forEach(element => { 70 | element.classList.add("system-item-hidden"); 71 | }); 72 | localStorage.setItem("system", system); 73 | } 74 | 75 | window.toggleSystem = function(button) { 76 | button.classList.toggle('system-switcher--active'); 77 | if(button.classList.contains('system-switcher--active')) { 78 | return setSystem('metric'); 79 | } 80 | return setSystem('imperial'); 81 | } 82 | 83 | window.isHidden = function(element) { 84 | const styles = window.getComputedStyle(element); 85 | return styles.display === 'none' || styles.visibility === 'hidden'; 86 | } 87 | 88 | window.output = { 89 | value: null, 90 | val: function(value){ 91 | this.value = value; 92 | return this; 93 | }, 94 | replace: function(search, replacement) { 95 | this.value = this.value.replace(search, replacement); 96 | return this; 97 | }, 98 | set: function(elementId){ 99 | _(elementId).innerHTML = this.value; 100 | return this; 101 | } 102 | }; 103 | 104 | // check erroneous result of expression (or if it's not string - check itself) 105 | // usefull in cases when erroneous values of input fields are unknown 106 | window.calc = (expression,scope,resultType) => { 107 | let result = expression; 108 | scope = scope || {}; 109 | if(typeof expression == 'string'){ 110 | result = math.evaluate(expression,scope); // can throw error as well 111 | } 112 | let valid = true; 113 | switch(resultType){ 114 | case 'positive': valid = result > 0; 115 | } 116 | if(isNaN(result)||result.im||!valid) { 117 | const args = Object.keys(scope).map(arg=>arg+'='+scope[arg]); 118 | throw new Error( 119 | `result of expression ${expression} is ${result}.` 120 | + (resultType ? `
Should be ${resultType}.`:'') 121 | + (args.length != 0 ? `
Args: ${args.join(', ')}.`:'') 122 | ); 123 | } 124 | return result; 125 | }; 126 | 127 | // allows to show accurate results with fixed precision/length (instead of using BigNumber) 128 | window.format = number => math.format(number,{precision:7}) 129 | 130 | window.input = { 131 | box: $('#error-box'), 132 | list: $('#error-list'), 133 | value: null, 134 | elementId: null, 135 | shown: false, 136 | processed: false, 137 | silent: false, 138 | reset: function(){ 139 | this.shown = false; 140 | this.box.classList.remove('calculator-result--error-active'); 141 | $$('.input-field--error')?.forEach(el => el.classList.remove('input-field--error')) 142 | $$('.calculator-result:not(.calculator-result--error)').forEach(el => el.classList.remove('calculator-result--hidden')) 143 | }, 144 | error: function (inputId, message = `Incorrect value for "${inputId}"`, last = false) { 145 | if(this.silent) return; 146 | if(this.processed) this.reset(); 147 | if(!Array.isArray(inputId)) inputId = [inputId]; 148 | for(const inputIdItem of inputId) _(inputIdItem).parentNode.classList.add('input-field--error'); 149 | if(!this.shown){ 150 | this.processed = false; 151 | this.shown = true; 152 | this.list.innerHTML = ''; 153 | this.box.classList.add('calculator-result--error-active'); 154 | $$('.calculator-result:not(.calculator-result--error)').forEach(el => el.classList.add('calculator-result--hidden')) 155 | } 156 | const element = document.createElement('p'); 157 | element.classList.add('calculator-error__item'); 158 | element.innerHTML = message; 159 | this.list.append(element); 160 | if(last) this.processed = true; 161 | }, 162 | // sometimes it's complicated to check input values 163 | // so we're checking `calc` result for an erroneous result, show exception and return 164 | // e.g.: try{calc(...);}catch(e){input.exception(field{s},e);return;} 165 | exception(inputId, message){ 166 | if(typeof message != 'string'){ 167 | let error = ''; 168 | if(message instanceof Error){ 169 | error = message.toString(); 170 | } 171 | // default message 172 | if(Array.isArray(inputId) && inputId.length == 0){ 173 | message = `${error}`; 174 | } else { 175 | message = `Value${Array.isArray(inputId)?'s':` "${this.get(inputId).value}"`} of "${inputId}" ${Array.isArray(inputId)?'are':'is'} invalid.
${error}`; 176 | } 177 | } 178 | return this.error(inputId, message, true); 179 | }, 180 | valid: function(){ 181 | if(!this.shown || this.processed) this.reset(); 182 | this.processed = true; 183 | this.silent = false; 184 | return !this.shown; 185 | }, 186 | get: function(elementId){ 187 | this.elementId = elementId; 188 | let element = _(elementId); 189 | this.silent = false; 190 | if(element == null){ 191 | this.value = null; 192 | } else { 193 | this.value = element.value; 194 | for (; element && element !== document; element = element.parentNode ) { 195 | if(element.classList.contains('related-item-hidden')) this.silent = true; 196 | } 197 | } 198 | return this; 199 | }, 200 | index: function(){ 201 | this.value = _(this.elementId).selectedIndex; 202 | return this; 203 | }, 204 | checked: function(elementId){ 205 | this.value = _(this.elementId).checked; 206 | return this; 207 | }, 208 | split: function(separator){ 209 | this.value = this.value.split(separator); 210 | return this; 211 | }, 212 | replace: function(pattern, replacement){ 213 | this.value = this.value.replace(pattern, replacement); 214 | return this; 215 | }, 216 | default: function(value){ 217 | if(!this.value) this.value = value; 218 | return this; 219 | }, 220 | optional: function(value){ 221 | if(!this.value) this.silent = true; 222 | return this; 223 | }, 224 | gt: function(compare = 0, errorText = `The ${this.elementId} must be greater than ${compare}.`){ 225 | if (this.value instanceof Date) { 226 | compare = compare instanceof Date ? compare : new Date(_(compare).value); 227 | if (this.value.getTime() <= compare.getTime()) this.error(this.elementId, errorText); 228 | } else { 229 | compare = isNaN(compare) ? Number(_(compare).value) : compare; 230 | if(this.value === '' || isNaN(Number(this.value))) 231 | this.error(this.elementId, `The ${this.elementId} must be a number.`); 232 | else 233 | if (Number(this.value) <= compare) this.error(this.elementId, errorText); 234 | } 235 | return this; 236 | }, 237 | gte: function(compare = 0, errorText = `The ${this.elementId} must be greater than or equal to ${compare}.`){ 238 | if (this.value instanceof Date) { 239 | compare = compare instanceof Date ? compare : new Date(_(compare).value); 240 | if (this.value.getTime() < compare.getTime()) this.error(this.elementId, errorText); 241 | } else { 242 | compare = isNaN(compare) ? Number(_(compare).value) : compare; 243 | if(this.value === '' || isNaN(Number(this.value))) 244 | this.error(this.elementId, `The ${this.elementId} must be a number.`); 245 | else 246 | if (Number(this.value) < compare) this.error(this.elementId, errorText); 247 | } 248 | return this; 249 | }, 250 | lt: function(compare = 0, errorText = `The ${this.elementId} must be less than ${compare}.`){ 251 | if (this.value instanceof Date) { 252 | compare = compare instanceof Date ? compare : new Date(_(compare).value); 253 | if (this.value.getTime() >= compare.getTime()) this.error(this.elementId, errorText); 254 | } else { 255 | compare = isNaN(compare) ? Number(_(compare).value) : compare; 256 | if(this.value === '' || isNaN(Number(this.value))) 257 | this.error(this.elementId, `The ${this.elementId} must be a number.`); 258 | else 259 | if (Number(this.value) >= compare) this.error(this.elementId, errorText); 260 | } 261 | return this; 262 | }, 263 | lte: function(compare = 0, errorText = `The ${this.elementId} must be less than or equal to ${compare}.`){ 264 | if (this.value instanceof Date) { 265 | compare = compare instanceof Date ? compare : new Date(_(compare).value); 266 | if (this.value.getTime() > compare.getTime()) this.error(this.elementId, errorText); 267 | } else { 268 | compare = isNaN(compare) ? Number(_(compare).value) : compare; 269 | if(this.value === '' || isNaN(Number(this.value))) 270 | this.error(this.elementId, `The ${this.elementId} must be a number.`); 271 | else 272 | if (Number(this.value) > compare) this.error(this.elementId, errorText); 273 | } 274 | return this; 275 | }, 276 | integer: function(errorText = `The ${this.elementId} must be integer number (-3, -2, -1, 0, 1, 2, 3, ...).`){ 277 | if (!this.value.match(/^-?(0|[1-9]\d*)$/)) this.error(this.elementId, errorText); 278 | return this; 279 | }, 280 | _naturalRegexp: /^([1-9]\d*)$/, 281 | natural: function(errorText = `The ${this.elementId} must be a natural number (1, 2, 3, ...).`){ 282 | if (!this.value.match(this._naturalRegexp)) this.error(this.elementId, errorText); 283 | return this; 284 | }, 285 | natural_numbers: function(errorText = `The ${this.elementId} must be a set of natural numbers (1, 2, 3, ...).`){ 286 | this.split(/[ ,]+/); 287 | if (!this.value.every(value=>value.match(this._naturalRegexp))) this.error(this.elementId, errorText); 288 | return this; 289 | }, 290 | _mixedRegexp: /^(0|-?[1-9]\d*|-?[1-9]\d*\/[1-9]\d*|-?[1-9]\d*\s[1-9]\d*\/[1-9]\d*)$/, 291 | mixed: function(errorText = `The ${this.elementId} must be an integer/fraction/mixed number (1, 2/3, 4 5/6, ...).`){ 292 | if (!this.value.match(this._mixedRegexp)) this.error(this.elementId, errorText); 293 | return this; 294 | }, 295 | mixed_numbers: function(errorText = `The ${this.elementId} must be a set of integer/fraction/mixed numbers (1, 2/3, 4 5/6, ...).`){ 296 | this.split(/,\s*/); 297 | if (!this.value.every(value=>value.match(this._mixedRegexp))) this.error(this.elementId, errorText); 298 | return this; 299 | }, 300 | number: function(errorText = `The "${this.elementId}" must be a number.`){ 301 | if(this.value === '' || isNaN(Number(this.value))) this.error(this.elementId, errorText); 302 | return this; 303 | }, 304 | probability: function(errorText = `The "${this.elementId}" must be a number between 0 and 1.`){ 305 | if(this.value === '' || isNaN(Number(this.value)) || Number(this.value) < 0 || Number(this.value) > 1) 306 | this.error(this.elementId, errorText); 307 | return this; 308 | }, 309 | percentage: function(errorText = `The "${this.elementId}" must be a number between 0 and 100.`){ 310 | if(this.value === '' || isNaN(Number(this.value)) || Number(this.value) < 0 || Number(this.value) > 100) 311 | this.error(this.elementId, errorText); 312 | return this; 313 | }, 314 | numbers: function(errorText = `The ${this.elementId} must be a set of numbers.`){ 315 | if (this.value.filter(value => isNaN(Number(value))).length) this.error(this.elementId, errorText); 316 | return this; 317 | }, 318 | whole: function(errorText = `The ${this.elementId} must be a whole number.`){ 319 | if (!this.value.match(/^(0|[1-9]\d*)$/)) this.error(this.elementId, errorText); 320 | return this; 321 | }, 322 | positive: function(errorText = `The ${this.elementId} must be greater than 0.`){ 323 | this.gt(0, errorText); 324 | return this; 325 | }, 326 | nonZero: function(errorText = `The ${this.elementId} must be non-zero.`){ 327 | if(this.value === '' || isNaN(Number(this.value))) 328 | this.error(this.elementId, `The ${this.elementId} must be a number.`); 329 | else 330 | if(Number(this.value) == 0) this.error(this.elementId, errorText); 331 | return this; 332 | }, 333 | nonNegative: function(errorText = `The ${this.elementId} must be greater than or equal to 0.`){ 334 | this.gte(0, errorText); 335 | return this; 336 | }, 337 | negative: function(errorText = `The ${this.elementId} must be less than 0.`){ 338 | this.lt(0, errorText); 339 | return this; 340 | }, 341 | scientific: function(errorText = `The ${this.elementId} must be in scientific notation.`){ 342 | if (!this.value.match(/^(-|\+)?((0|[1-9]\d*)(\.\d*)?|(\.\d+))((e|\s?(\*|x)\s?10\^)(-|\+)?(0|[1-9]\d*))?$/i)) this.error(this.elementId, errorText); 343 | return this; 344 | }, 345 | periodic: function(errorText = `The ${this.elementId} must be regular or periodic number.`){ 346 | if (this.value === '' || isNaN(Number(this.value)) && !this.value.match(/\.\d*\(\d+\)$/)) this.error(this.elementId, errorText); 347 | return this; 348 | }, 349 | time: function(format = 'hh:mm:ss', errorText = `The ${this.elementId} must be in format ${format}.`){ 350 | let regex = new RegExp("^" + format.replace(/[\:\-\/\\]/g, '\\$&').replace(/h|m|s/g, '\\d') + "$"); 351 | if (!this.value.match(regex) || isNaN(this.value = new Date(`1900-01-01 ${this.value}`))) this.error(this.elementId, errorText); 352 | return this; 353 | }, 354 | date: function(format = 'yyyy-mm-dd', errorText = `The ${this.elementId} is required field.`){ 355 | let regex = new RegExp("^" + format.replace(/[\:\-\/\\]/g, '\\$&').replace(/y|m|d|h|s/g, '\\d') + "$"); 356 | if (!this.value.match(regex) || isNaN(this.value = new Date(this.value))) this.error(this.elementId, errorText); 357 | this.value = convertTZ(this.value, "UTC"); 358 | return this; 359 | }, 360 | bool: function(){ 361 | return !!this.value; 362 | }, 363 | val: function(){ 364 | if(this.value === '' || this.value === null) return null; 365 | return Number(this.value); 366 | }, 367 | vals: function(){ 368 | return this.value.map(value => Number(value)); 369 | }, 370 | raw: function(){ 371 | return this.value; 372 | }, 373 | group(name,ids){ 374 | const result = ids.split('|') 375 | .reduce((result,ids)=>{ 376 | const obj = ids.split('.').every(id=>_(name+id.match(/^[_a-zA-Z]+/)[0]).value) ? 377 | ids.split('.').reduce((obj,id)=>{ 378 | let type = 'number'; 379 | let getter = 'val'; 380 | switch((id.match(/[^_a-zA-Z]+$/)||[])[0]){ 381 | case '+': type = 'positive'; break; 382 | case '!0': type = 'nonZero'; break; 383 | case '#': getter = 'raw'; break; 384 | default: /* custom modificator */; break; 385 | } 386 | id = id.match(/^[_a-zA-Z]+/)[0]; 387 | return {...obj, ...{ 388 | [id]: input.get(name+id)[type]()[getter]() 389 | }}; 390 | }, {}) : null; 391 | if(!result && !obj) return null; 392 | return {...result,...obj}; 393 | }, null); 394 | return result; 395 | }, 396 | } 397 | 398 | window.sysnumconv = function(element){ 399 | const id = element.dataset.target; 400 | const metric = _(id); 401 | const imperial = _(id + "_imperial"); 402 | if(window.isMetricSystem()){ 403 | let value = metric.value; 404 | imperial.value = eval(imperial.dataset.formula).toFixed(2); 405 | } else { 406 | let value = imperial.value; 407 | metric.value = eval(metric.dataset.formula).toFixed(2); 408 | } 409 | }; 410 | 411 | window.updateHeight = function(element){ 412 | var id = element.dataset.target; 413 | var cm = _(id); 414 | var feet = _(id + "_ft"); 415 | var inches = _(id + "_in"); 416 | if(window.isMetricSystem()){ 417 | feet.value = Math.floor(cm.value * 0.393701 / 12); 418 | inches.value = (cm.value * 0.393701 % 12).toFixed(2); 419 | } else { 420 | cm.value = ((feet.value * 30.48) + (inches.value * 2.54)).toFixed(2); 421 | } 422 | }; 423 | 424 | window.setDate = function (id, suffix) { 425 | var date = new Date(); 426 | var date_m = _(id); 427 | var date_us = _(id + "_us"); 428 | date.setDate(date.getDate() + suffix); 429 | date_us.value = ("0" + (date.getMonth() + 1)).slice(-2) + '/' + ("0" + date.getDate()).slice(-2) + '/' + date.getFullYear(); 430 | date_m.value = date.getFullYear() + '-' + ("0" + (date.getMonth() + 1)).slice(-2) + '-' + ("0" + date.getDate()).slice(-2); 431 | } 432 | 433 | window.updateDate = function(element){ 434 | var id = element.dataset.target; 435 | var date_m = _(id); 436 | var date_us = _(id + "_us"); 437 | if(window.isMetricSystem()){ 438 | let date = new Date(Date.parse(date_m.value)); 439 | date_us.value = ("0" + (date.getMonth() + 1)).slice(-2) + '/' + ("0" + date.getDate()).slice(-2) + '/' + date.getFullYear(); 440 | } else { 441 | let date = new Date(Date.parse(date_us.value)); 442 | date_m.value = date.getFullYear() + '-' + ("0" + (date.getMonth() + 1)).slice(-2) + '-' + ("0" + date.getDate()).slice(-2); 443 | } 444 | }; 445 | 446 | window.switchTab = function (event, index) { 447 | if(index !== undefined && index !== null) { 448 | if(event.target.classList.contains('tab-item')) { 449 | $$(`.${event.target.classList[0]}`).forEach((tab, i) => { 450 | if(index === i) { 451 | tab.classList.add('tab-item--active') 452 | } else { 453 | tab.classList.remove('tab-item--active') 454 | } 455 | }) 456 | } 457 | } 458 | $$(`[data-tab]`).forEach(element => { 459 | if(element.dataset.tab == event.selectedIndex || element.dataset.tab == index) { 460 | element.classList.add('tab--active') 461 | } else { 462 | element.classList.remove('tab--active') 463 | } 464 | }) 465 | } 466 | 467 | window.setMaxLength = function(element, maxLength) { 468 | if(getComputedStyle(element).getPropertyValue('--fontStep') !== '') { 469 | element.style = `--maxWidth: ${Number(getComputedStyle(element).getPropertyValue('--fontStep').replace(/px/gi, '')) * (element.value.length === 0 ? 1 : element.value.length > maxLength ? maxLength : element.value.length) }px` 470 | } 471 | if(element.value.length > maxLength) { 472 | element.value = element.value.substring(0, maxLength); 473 | } 474 | } 475 | 476 | window.relatedToggle = function(element, related, action) { 477 | if(action === "disabled") { 478 | if(element.target.checked) { 479 | $$(`.related-to-${related}`)?.forEach(el => el.classList.remove('disabled')) 480 | } else { 481 | $$(`.related-to-${related}`)?.forEach(el => el.classList.add('disabled')) 482 | } 483 | } else { 484 | if(element.target.checked) { 485 | $$(`.related-to-${related}-a`)?.forEach(el => el.classList.add('related-item-hidden')) 486 | $$(`.related-to-${related}-b`)?.forEach(el => el.classList.remove('related-item-hidden')) 487 | } else { 488 | $$(`.related-to-${related}-a`)?.forEach(el => el.classList.remove('related-item-hidden')) 489 | $$(`.related-to-${related}-b`)?.forEach(el => el.classList.add('related-item-hidden')) 490 | } 491 | } 492 | } 493 | 494 | window.roundTo = function (num, decimals = 5) { 495 | if (typeof num !== 'number' || !isFinite(num)) return num; 496 | if (num.toString().includes('e')) { 497 | const splitted = num.toString().split('e'); 498 | return roundTo(+splitted[0], decimals) + `e${splitted[1]}`; 499 | }; 500 | return +(Math.round(num + `e+${decimals}`) + `e-${decimals}`); 501 | } 502 | 503 | window.delay = ms => new Promise(resolve => setTimeout(resolve, ms)); 504 | window.generateRandomDigit = () => Math.floor(Math.random() * 10); 505 | window.convertTZ = (date, tzString) => new Date((typeof date === "string" ? new Date(date) : date).toLocaleString("en-US", {timeZone: tzString})); 506 | window.animateElement = async (element, isText) => { 507 | const originalText = element.innerHTML; 508 | let digitIndices = []; 509 | let originalDigits = []; 510 | 511 | [...originalText].forEach((char, index) => { 512 | if (!isNaN(parseInt(char, 10)) && char !== ' ') { 513 | digitIndices.push(index); 514 | originalDigits.push(char); 515 | } 516 | }); 517 | 518 | for (let i = 0; i < 10; i++) { 519 | let textArray = [...originalText]; 520 | digitIndices.forEach(index => { 521 | textArray[index] = generateRandomDigit(); 522 | }); 523 | element.innerHTML = textArray.join(''); 524 | await delay(30); 525 | } 526 | 527 | digitIndices.forEach((index, digitIndex) => { 528 | let textArray = [...originalText]; 529 | textArray[index] = originalDigits[digitIndex]; 530 | element.innerHTML = textArray.join(''); 531 | }); 532 | }; 533 | window.animateElements = function(){ 534 | $$('.animate').forEach(async (element) => animateElement(element)); 535 | $$('.animate-text').forEach(async (element) => animateElement(element, true)); 536 | } 537 | window.plural = function (number, versions, options = { showNumber: true, localize: true, locale: null }) { 538 | const words = { 539 | zero: "", 540 | one: "", 541 | two: "", 542 | few: "", 543 | many: "", 544 | other: "" 545 | }; 546 | const parts = versions.split(':'); 547 | if (parts.length === 1) { 548 | Object.keys(words).forEach(key => words[key] = versions); 549 | } else { 550 | let i = 0; 551 | for (let key in words) { 552 | words[key] = parts[i] || parts[0]; 553 | i++; 554 | } 555 | } 556 | const rule = new Intl.PluralRules(options.locale ?? window.locale).select(Math.floor(number)); 557 | return `${options.showNumber ? `${options.localize ? number.toLocaleString(options.locale ?? window.locale) : number} ` : ''}${words[rule]}`; 558 | }; 559 | 560 | window.numberToWords = function(num) { 561 | if (num === 0) return 'zero'; 562 | const belowTwenty = [ 563 | '', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 564 | 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 565 | 'sixteen', 'seventeen', 'eighteen', 'nineteen']; 566 | 567 | const tens = ['', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety']; 568 | 569 | const hundreds = [ 570 | '', 'one hundred', 'two hundred', 'three hundred', 'four hundred', 'five hundred', 571 | 'six hundred', 'seven hundred', 'eight hundred', 'nine hundred']; 572 | 573 | const integerScales = [ 574 | 'thousands:thousand:thousands:thousands:thousands:thousands', 'millions:million:millions:millions:millions:millions', 'billions:billion:billions:billions:billions:billions', 'trillions:trillion:trillions:trillions:trillions:trillions', 'quadrillions:quadrillion:quadrillions:quadrillions:quadrillions:quadrillions', 575 | 'quintillions:quintillion:quintillions:quintillions:quintillions:quintillions', 'sextillions:sextillion:sextillions:sextillions:sextillions:sextillions', 'septillions:septillion:septillions:septillions:septillions:septillions', 'octillions:octillion:octillions:octillions:octillions:octillions', 'nonillions:nonillion:nonillions:nonillions:nonillions:nonillions', 576 | 'decillions:decillion:decillions:decillions:decillions:decillions', 'undecillions:undecillion:undecillions:undecillions:undecillions:undecillions', 'duodecillions:duodecillion:duodecillions:duodecillions:duodecillions:duodecillions', 'tredecillions:tredecillion:tredecillions:tredecillions:tredecillions:tredecillions', 'quattuordecillions:quattuordecillion:quattuordecillions:quattuordecillions:quattuordecillions:quattuordecillions', 577 | 'quindecillions:quindecillion:quindecillions:quindecillions:quindecillions:quindecillions', 'sexdecillions:sexdecillion:sexdecillions:sexdecillions:sexdecillions:sexdecillions', 'septendecillions:septendecillion:septendecillions:septendecillions:septendecillions:septendecillions', 'octodecillions:octodecillion:octodecillions:octodecillions:octodecillions:octodecillions', 578 | 'novemdecillions:novemdecillion:novemdecillions:novemdecillions:novemdecillions:novemdecillions', 'vigintillions:vigintillion:vigintillions:vigintillions:vigintillions:vigintillions']; 579 | 580 | const decimalScales = [ 581 | 'tenths:tenth:tenths:tenths:tenths:tenths', 'hundredths:hundredth:hundredths:hundredths:hundredths:hundredths', 'thousandths:thousandth:thousandths:thousandths:thousandths:thousandths', 'ten-thousandths:ten-thousandth:ten-thousandths:ten-thousandths:ten-thousandths:ten-thousandths', 'hundred-thousandths:hundred-thousandth:hundred-thousandths:hundred-thousandths:hundred-thousandths:hundred-thousandths', 'millionths:millionth:millionths:millionths:millionths:millionths', 582 | 'ten-millionths:ten-millionth:ten-millionths:ten-millionths:ten-millionths:ten-millionths', 'hundred-millionths:hundred-millionth:hundred-millionths:hundred-millionths:hundred-millionths:hundred-millionths', 'billionths:billionth:billionths:billionths:billionths:billionths', 'ten-billionths:ten-billionth:ten-billionths:ten-billionths:ten-billionths:ten-billionths', 'hundred-billionths:hundred-billionth:hundred-billionths:hundred-billionths:hundred-billionths:hundred-billionths', 583 | 'trillionths:trillionth:trillionths:trillionths:trillionths:trillionths', 'ten-trillionths:ten-trillionth:ten-trillionths:ten-trillionths:ten-trillionths:ten-trillionths', 'hundred-trillionths:hundred-trillionth:hundred-trillionths:hundred-trillionths:hundred-trillionths:hundred-trillionths', 'quadrillionths:quadrillionth:quadrillionths:quadrillionths:quadrillionths:quadrillionths', 'ten-quadrillionths:ten-quadrillionth:ten-quadrillionths:ten-quadrillionths:ten-quadrillionths:ten-quadrillionths', 584 | 'hundred-quadrillionths:hundred-quadrillionth:hundred-quadrillionths:hundred-quadrillionths:hundred-quadrillionths:hundred-quadrillionths', 'quintillionths:quintillionth:quintillionths:quintillionths:quintillionths:quintillionths', 'ten-quintillionths:ten-quintillionth:ten-quintillionths:ten-quintillionths:ten-quintillionths:ten-quintillionths', 'hundred-quintillionths:hundred-quintillionth:hundred-quintillionths:hundred-quintillionths:hundred-quintillionths:hundred-quintillionths', 585 | 'sextillionths:sextillionth:sextillionths:sextillionths:sextillionths:sextillionths', 'septillionths:septillionth:septillionths:septillionths:septillionths:septillionths', 'octillionths:octillionth:octillionths:octillionths:octillionths:octillionths', 'nonillionths:nonillionth:nonillionths:nonillionths:nonillionths:nonillionths', 'decillionths:decillionth:decillionths:decillionths:decillionths:decillionths', 586 | 'undecillionths:undecillionth:undecillionths:undecillionths:undecillionths:undecillionths', 'duodecillionths:duodecillionth:duodecillionths:duodecillionths:duodecillionths:duodecillionths', 'tredecillionths:tredecillionth:tredecillionths:tredecillionths:tredecillionths:tredecillionths', 'quattuordecillionths:quattuordecillionth:quattuordecillionths:quattuordecillionths:quattuordecillionths:quattuordecillionths', 'quindecillionths:quindecillionth:quindecillionths:quindecillionths:quindecillionths:quindecillionths', 587 | 'sexdecillionths:sexdecillionth:sexdecillionths:sexdecillionths:sexdecillionths:sexdecillionths', 'septendecillionths:septendecillionth:septendecillionths:septendecillionths:septendecillionths:septendecillionths', 'octodecillionths:octodecillionth:octodecillionths:octodecillionths:octodecillionths:octodecillionths', 'novemdecillionths:novemdecillionth:novemdecillionths:novemdecillionths:novemdecillionths:novemdecillionths', 'vigintillionths:vigintillionth:vigintillionths:vigintillionths:vigintillionths:vigintillionths']; 588 | 589 | let chunks = []; 590 | let currentNum = BigInt(Math.floor(num)); 591 | 592 | while (currentNum > 0n) { 593 | chunks.unshift(currentNum % 1000n); 594 | currentNum = currentNum / 1000n; 595 | } 596 | 597 | if (chunks.length === 0) chunks.push(0n); // Handle numbers less than 1 598 | 599 | let integerWords = chunks.map((chunk, index) => { 600 | chunk = Number(chunk); 601 | if (chunk === 0) return ''; 602 | const hundred = Math.floor(chunk / 100); 603 | const tenUnit = chunk % 100; 604 | const ten = Math.floor(tenUnit / 10); 605 | const unit = tenUnit % 10; 606 | 607 | let chunkText = ''; 608 | 609 | if (hundred > 0) chunkText += hundreds[hundred] + ' '; 610 | if (tenUnit >= 20) { 611 | chunkText += tens[ten] + ' '; 612 | if (unit > 0) chunkText += belowTwenty[unit]; 613 | } else if (tenUnit > 0) { 614 | chunkText += belowTwenty[tenUnit]; 615 | } 616 | 617 | if (chunk > 0 && index < chunks.length - 1) { 618 | let scale = integerScales[chunks.length - index - 2]; 619 | return chunkText.trim() + ' ' + plural(chunk, scale, {showNumber: false}); 620 | } 621 | return chunkText.trim(); 622 | }).join(' ').trim(); 623 | 624 | let decimalPart = (num - Math.floor(num)).toFixed(num.toString().split('.')?.[1]?.length).toString().slice(2); 625 | let decimalLength = decimalPart > 0 ? decimalPart.toString().replace('-', '').length : 0; 626 | 627 | return (integerWords.length > 0 ? integerWords : 'zero') + (decimalLength > 0 ? ' point ' + numberToWords(decimalPart) + ' ' + plural(decimalPart, decimalScales[decimalLength-1], {showNumber: false}) : ''); 628 | } 629 | 630 | window.toSentenceCase = function(str){ 631 | const firstLetter = str.substr(0, 1); 632 | return firstLetter.toUpperCase() + str.substr(1).toLowerCase(); 633 | } 634 | 635 | window.toTitleCase = function(str) { 636 | return str.toLowerCase().split(' ').map(function(word) { 637 | return (word.charAt(0).toUpperCase() + word.slice(1)); 638 | }).join(' '); 639 | } 640 | 641 | window.setCookie = function(key, value){ 642 | const date = new Date(); 643 | date.setTime(date.getTime() + (10 * 365 * 24 * 60 * 60 * 1000)); 644 | document.cookie = `${key}=${value};expires=${date.toUTCString()};path=/`; 645 | } 646 | 647 | window.getCookie = function(key) { 648 | let nameEQ = key + "="; 649 | let ca = document.cookie.split(';'); 650 | for (let i = 0; i < ca.length; i++) { 651 | let c = ca[i]; 652 | while (c.charAt(0) === ' ') c = c.substring(1); 653 | if (c.indexOf(nameEQ) === 0) { 654 | return decodeURIComponent(c.substring(nameEQ.length, c.length)); 655 | } 656 | } 657 | return null; 658 | } -------------------------------------------------------------------------------- /assets/js/hourpicker.js: -------------------------------------------------------------------------------- 1 | const INPUTS = $$('.hourpicker-input'); 2 | let timepickerFormat = true 3 | let currentInput = { 4 | input: null, 5 | hour: null, 6 | minute: null, 7 | } 8 | 9 | function createTimepicker() { 10 | const TIMEPICKER = document.createElement('div'); 11 | const {top, left} = currentInput.input.getBoundingClientRect() 12 | const {offsetHeight} = currentInput.input 13 | const VALUE = currentInput.input.value.split(':') 14 | 15 | TIMEPICKER.classList.add('timepicker'); 16 | TIMEPICKER.classList.add('timepicker--hourpicker'); 17 | TIMEPICKER.innerHTML = ` 18 |
19 |
20 |
21 |

Hour

22 | 23 |
24 |

:

25 |
26 |

Minute

27 | 28 |
29 |
30 |
31 | 34 | 37 |
38 |
39 | 40 | 49 | ` 50 | document.body.appendChild(TIMEPICKER); 51 | TIMEPICKER.style.top = top + window.scrollY + 9 + offsetHeight + 'px'; 52 | TIMEPICKER.style.left = left - 13 + 'px'; 53 | TIMEPICKER.classList.add('timepicker--active'); 54 | 55 | const TIMEPICKER_DONE = document.getElementById('timepicker_done') 56 | const TIMEPICKER_CANCEL = document.getElementById('timepicker_cancel') 57 | const TIMEPICKER_HOUR = document.getElementById('timepicker_hour') 58 | const TIMEPICKER_MINUTE = document.getElementById('timepicker_minute') 59 | const TIMEPICKER_FORMAT_SWITCHER = document.getElementById('timepicker_format_switcher') 60 | const TIMEPICKER_FORMAT = document.getElementById('timepicker_format') 61 | const SWITCH_TO_AM = document.getElementById('switch_to_am') 62 | const SWITCH_TO_PM = document.getElementById('switch_to_pm') 63 | 64 | SWITCH_TO_AM.addEventListener('click', () => { 65 | SWITCH_TO_PM.classList.remove('button-switcher--active') 66 | SWITCH_TO_AM.classList.add('button-switcher--active') 67 | }) 68 | 69 | SWITCH_TO_PM.addEventListener('click', () => { 70 | SWITCH_TO_AM.classList.remove('button-switcher--active') 71 | SWITCH_TO_PM.classList.add('button-switcher--active') 72 | }) 73 | 74 | TIMEPICKER_HOUR.addEventListener('input', (event) => { 75 | limitLengthHandler(event.target, 2) 76 | currentInput.hour = event.target.value 77 | if(event.target.value.length === 2) TIMEPICKER_MINUTE.focus() 78 | }) 79 | TIMEPICKER_MINUTE.addEventListener('input', (event) => { 80 | limitLengthHandler(event.target, 2) 81 | currentInput.minute = event.target.value 82 | }) 83 | 84 | TIMEPICKER_FORMAT_SWITCHER.addEventListener('input', (event) => { 85 | if(event.target.checked) { 86 | TIMEPICKER_FORMAT.style.display = 'none' 87 | timepickerFormat = false 88 | } 89 | else { 90 | TIMEPICKER_FORMAT.style.display = 'flex' 91 | timepickerFormat = true 92 | } 93 | }) 94 | 95 | TIMEPICKER_DONE.addEventListener('click', onDoneTimepicker) 96 | window.addEventListener('keydown', onDoneTimepickerEnter) 97 | 98 | TIMEPICKER_CANCEL.addEventListener('click', () => { 99 | onCloseTimepicker(); 100 | }) 101 | } 102 | 103 | function limitLengthHandler(input, limit) { 104 | if(input.value.length > limit) { 105 | input.value = input.value.slice(0, limit); 106 | } 107 | return input.value; 108 | } 109 | 110 | function onDoneTimepickerEnter (event) { 111 | if(event.key === 'Enter') { 112 | onDoneTimepicker(); 113 | } 114 | } 115 | 116 | function onDoneTimepicker () { 117 | let hours = currentInput.hour < 10 ? '0' + currentInput.hour : currentInput.hour 118 | let minutes = currentInput.minute < 10 ? '0' + currentInput.minute : currentInput.minute 119 | 120 | currentInput.input.value = `${hours > 0 ? hours : '00'}:${minutes > 0 ? minutes : '00'}` 121 | onCloseTimepicker(); 122 | window.removeEventListener('keydown', onDoneTimepickerEnter) 123 | window.removeEventListener('click', onCloseTimepickerOutside) 124 | } 125 | 126 | function onCloseTimepicker() { 127 | currentInput.input = null 128 | document.body.removeChild($('.timepicker')); 129 | window.removeEventListener('click', onCloseTimepickerOutside) 130 | } 131 | 132 | function onCloseTimepickerOutside(event) { 133 | if($('.timepicker') && !(event.composedPath().includes($('.timepicker')) || event.composedPath().includes(currentInput.input))) { 134 | onCloseTimepicker(); 135 | } 136 | } 137 | 138 | window.timepicker = (event) => { 139 | if($('.timepicker')) { 140 | onCloseTimepicker(); 141 | } else { 142 | currentInput.input = event.target 143 | createTimepicker() 144 | window.addEventListener('click', onCloseTimepickerOutside) 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /assets/js/lib/chartjs/helpers.d.ts: -------------------------------------------------------------------------------- 1 | import { Color } from '@kurkle/color'; 2 | import { P as Point$1, f as ChartArea, e as ChartEvent, C as Chart, au as ChartMeta, b as PointElement, cn as TRBL, co as TRBLCorners, aZ as FontSpec, b9 as PointStyle, ck as Color$1, cp as RoundedRect } from './chunks/helpers.core.js'; 3 | export { cg as EasingFunction, cE as MergeOptions, cL as _capitalize, cI as _deprecated, cB as _elementsEqual, cP as _isClickEvent, cD as _merger, cH as _mergerIf, cJ as _splitKey, cz as callback, cC as clone, cM as defined, cA as each, cv as finiteOrDefault, ct as isArray, cq as isFinite, cN as isFunction, cs as isNullOrUndef, cu as isObject, cF as merge, cG as mergeIf, n as noop, cK as resolveObjectKey, cO as setsEqual, cy as toDimension, cx as toPercentage, cr as uid, cw as valueOrDefault } from './chunks/helpers.core.js'; 4 | 5 | declare function isPatternOrGradient(value: unknown): value is CanvasPattern | CanvasGradient; 6 | declare function color(value: CanvasGradient): CanvasGradient; 7 | declare function color(value: CanvasPattern): CanvasPattern; 8 | declare function color(value: string | { 9 | r: number; 10 | g: number; 11 | b: number; 12 | a: number; 13 | } | [number, number, number] | [number, number, number, number]): Color; 14 | declare function getHoverColor(value: CanvasGradient): CanvasGradient; 15 | declare function getHoverColor(value: CanvasPattern): CanvasPattern; 16 | declare function getHoverColor(value: string): string; 17 | 18 | /** 19 | * Note: typedefs are auto-exported, so use a made-up `canvas` namespace where 20 | * necessary to avoid duplicates with `export * from './helpers`; see 21 | * https://github.com/microsoft/TypeScript/issues/46011 22 | */ 23 | type Point = Point$1; 24 | 25 | /** 26 | * Binary search 27 | * @param table - the table search. must be sorted! 28 | * @param value - value to find 29 | * @param cmp 30 | * @private 31 | */ 32 | declare function _lookup(table: number[], value: number, cmp?: (value: number) => boolean): { 33 | lo: number; 34 | hi: number; 35 | }; 36 | declare function _lookup(table: T[], value: number, cmp: (value: number) => boolean): { 37 | lo: number; 38 | hi: number; 39 | }; 40 | /** 41 | * Binary search 42 | * @param table - the table search. must be sorted! 43 | * @param key - property name for the value in each entry 44 | * @param value - value to find 45 | * @param last - lookup last index 46 | * @private 47 | */ 48 | declare const _lookupByKey: (table: Record[], key: string, value: number, last?: boolean) => { 49 | lo: number; 50 | hi: number; 51 | }; 52 | /** 53 | * Reverse binary search 54 | * @param table - the table search. must be sorted! 55 | * @param key - property name for the value in each entry 56 | * @param value - value to find 57 | * @private 58 | */ 59 | declare const _rlookupByKey: (table: Record[], key: string, value: number) => { 60 | lo: number; 61 | hi: number; 62 | }; 63 | /** 64 | * Return subset of `values` between `min` and `max` inclusive. 65 | * Values are assumed to be in sorted order. 66 | * @param values - sorted array of values 67 | * @param min - min value 68 | * @param max - max value 69 | */ 70 | declare function _filterBetween(values: number[], min: number, max: number): number[]; 71 | interface ArrayListener { 72 | _onDataPush?(...item: T[]): void; 73 | _onDataPop?(): void; 74 | _onDataShift?(): void; 75 | _onDataSplice?(index: number, deleteCount: number, ...items: T[]): void; 76 | _onDataUnshift?(...item: T[]): void; 77 | } 78 | /** 79 | * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice', 80 | * 'unshift') and notify the listener AFTER the array has been altered. Listeners are 81 | * called on the '_onData*' callbacks (e.g. _onDataPush, etc.) with same arguments. 82 | */ 83 | declare function listenArrayEvents(array: T[], listener: ArrayListener): void; 84 | /** 85 | * Removes the given array event listener and cleanup extra attached properties (such as 86 | * the _chartjs stub and overridden methods) if array doesn't have any more listeners. 87 | */ 88 | declare function unlistenArrayEvents(array: T[], listener: ArrayListener): void; 89 | /** 90 | * @param items 91 | */ 92 | declare function _arrayUnique(items: T[]): T[]; 93 | 94 | interface SplinePoint { 95 | x: number; 96 | y: number; 97 | skip?: boolean; 98 | cp1x?: number; 99 | cp1y?: number; 100 | cp2x?: number; 101 | cp2y?: number; 102 | } 103 | declare function splineCurve(firstPoint: SplinePoint, middlePoint: SplinePoint, afterPoint: SplinePoint, t: number): { 104 | previous: SplinePoint; 105 | next: SplinePoint; 106 | }; 107 | /** 108 | * This function calculates Bézier control points in a similar way than |splineCurve|, 109 | * but preserves monotonicity of the provided data and ensures no local extremums are added 110 | * between the dataset discrete points due to the interpolation. 111 | * See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation 112 | */ 113 | declare function splineCurveMonotone(points: SplinePoint[], indexAxis?: 'x' | 'y'): void; 114 | /** 115 | * @private 116 | */ 117 | declare function _updateBezierControlPoints(points: SplinePoint[], options: any, area: ChartArea, loop: boolean, indexAxis: 'x' | 'y'): void; 118 | 119 | /** 120 | * Note: typedefs are auto-exported, so use a made-up `dom` namespace where 121 | * necessary to avoid duplicates with `export * from './helpers`; see 122 | * https://github.com/microsoft/TypeScript/issues/46011 123 | * @typedef { import("../core/core.controller").default } dom.Chart 124 | * @typedef { import('../../types').ChartEvent } ChartEvent 125 | */ 126 | /** 127 | * @private 128 | */ 129 | declare function _isDomSupported(): boolean; 130 | /** 131 | * @private 132 | */ 133 | declare function _getParentNode(domNode: HTMLCanvasElement): HTMLCanvasElement; 134 | declare function getStyle(el: HTMLElement, property: string): string; 135 | /** 136 | * Gets an event's x, y coordinates, relative to the chart area 137 | * @param event 138 | * @param chart 139 | * @returns x and y coordinates of the event 140 | */ 141 | declare function getRelativePosition(event: Event | ChartEvent | TouchEvent | MouseEvent, chart: Chart): { 142 | x: number; 143 | y: number; 144 | }; 145 | declare function getMaximumSize(canvas: HTMLCanvasElement, bbWidth?: number, bbHeight?: number, aspectRatio?: number): { 146 | width: number; 147 | height: number; 148 | }; 149 | /** 150 | * @param chart 151 | * @param forceRatio 152 | * @param forceStyle 153 | * @returns True if the canvas context size or transformation has changed. 154 | */ 155 | declare function retinaScale(chart: Chart, forceRatio: number, forceStyle?: boolean): boolean | void; 156 | /** 157 | * Detects support for options object argument in addEventListener. 158 | * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support 159 | * @private 160 | */ 161 | declare const supportsEventListenerOptions: boolean; 162 | /** 163 | * The "used" size is the final value of a dimension property after all calculations have 164 | * been performed. This method uses the computed style of `element` but returns undefined 165 | * if the computed style is not expressed in pixels. That can happen in some cases where 166 | * `element` has a size relative to its parent and this last one is not yet displayed, 167 | * for example because of `display: none` on a parent node. 168 | * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value 169 | * @returns Size in pixels or undefined if unknown. 170 | */ 171 | declare function readUsedSize(element: HTMLElement, property: 'width' | 'height'): number | undefined; 172 | 173 | declare function fontString(pixelSize: number, fontStyle: string, fontFamily: string): string; 174 | /** 175 | * Request animation polyfill 176 | */ 177 | declare const requestAnimFrame: (((callback: FrameRequestCallback) => number) & typeof requestAnimationFrame) | ((callback: any) => any); 178 | /** 179 | * Throttles calling `fn` once per animation frame 180 | * Latest arguments are used on the actual call 181 | */ 182 | declare function throttled>(fn: (...args: TArgs) => void, thisArg: any): (...args: TArgs) => void; 183 | /** 184 | * Debounces calling `fn` for `delay` ms 185 | */ 186 | declare function debounce>(fn: (...args: TArgs) => void, delay: number): (...args: TArgs) => number; 187 | /** 188 | * Converts 'start' to 'left', 'end' to 'right' and others to 'center' 189 | * @private 190 | */ 191 | declare const _toLeftRightCenter: (align: 'start' | 'end' | 'center') => "center" | "right" | "left"; 192 | /** 193 | * Returns `start`, `end` or `(start + end) / 2` depending on `align`. Defaults to `center` 194 | * @private 195 | */ 196 | declare const _alignStartEnd: (align: 'start' | 'end' | 'center', start: number, end: number) => number; 197 | /** 198 | * Returns `left`, `right` or `(left + right) / 2` depending on `align`. Defaults to `left` 199 | * @private 200 | */ 201 | declare const _textX: (align: 'left' | 'right' | 'center', left: number, right: number, rtl: boolean) => number; 202 | /** 203 | * Return start and count of visible points. 204 | * @private 205 | */ 206 | declare function _getStartAndCountOfVisiblePoints(meta: ChartMeta<'line' | 'scatter'>, points: PointElement[], animationsDisabled: boolean): { 207 | start: number; 208 | count: number; 209 | }; 210 | /** 211 | * Checks if the scale ranges have changed. 212 | * @param {object} meta - dataset meta. 213 | * @returns {boolean} 214 | * @private 215 | */ 216 | declare function _scaleRangesChanged(meta: any): boolean; 217 | 218 | /** 219 | * @private 220 | */ 221 | declare function _pointInLine(p1: Point$1, p2: Point$1, t: number, mode?: any): { 222 | x: number; 223 | y: number; 224 | }; 225 | /** 226 | * @private 227 | */ 228 | declare function _steppedInterpolation(p1: Point$1, p2: Point$1, t: number, mode: 'middle' | 'after' | unknown): { 229 | x: number; 230 | y: number; 231 | }; 232 | /** 233 | * @private 234 | */ 235 | declare function _bezierInterpolation(p1: SplinePoint, p2: SplinePoint, t: number, mode?: any): { 236 | x: number; 237 | y: number; 238 | }; 239 | 240 | declare function formatNumber(num: number, locale: string, options?: Intl.NumberFormatOptions): string; 241 | 242 | /** 243 | * @alias Chart.helpers.options 244 | * @namespace 245 | */ 246 | /** 247 | * Converts the given line height `value` in pixels for a specific font `size`. 248 | * @param value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em'). 249 | * @param size - The font size (in pixels) used to resolve relative `value`. 250 | * @returns The effective line height in pixels (size * 1.2 if value is invalid). 251 | * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height 252 | * @since 2.7.0 253 | */ 254 | declare function toLineHeight(value: number | string, size: number): number; 255 | /** 256 | * @param value 257 | * @param props 258 | */ 259 | declare function _readValueToProps(value: number | Record, props: K[]): Record; 260 | declare function _readValueToProps(value: number | Record, props: Record): Record; 261 | /** 262 | * Converts the given value into a TRBL object. 263 | * @param value - If a number, set the value to all TRBL component, 264 | * else, if an object, use defined properties and sets undefined ones to 0. 265 | * x / y are shorthands for same value for left/right and top/bottom. 266 | * @returns The padding values (top, right, bottom, left) 267 | * @since 3.0.0 268 | */ 269 | declare function toTRBL(value: number | TRBL | Point): Record<"top" | "right" | "bottom" | "left", number>; 270 | /** 271 | * Converts the given value into a TRBL corners object (similar with css border-radius). 272 | * @param value - If a number, set the value to all TRBL corner components, 273 | * else, if an object, use defined properties and sets undefined ones to 0. 274 | * @returns The TRBL corner values (topLeft, topRight, bottomLeft, bottomRight) 275 | * @since 3.0.0 276 | */ 277 | declare function toTRBLCorners(value: number | TRBLCorners): Record<"topLeft" | "topRight" | "bottomLeft" | "bottomRight", number>; 278 | /** 279 | * Converts the given value into a padding object with pre-computed width/height. 280 | * @param value - If a number, set the value to all TRBL component, 281 | * else, if an object, use defined properties and sets undefined ones to 0. 282 | * x / y are shorthands for same value for left/right and top/bottom. 283 | * @returns The padding values (top, right, bottom, left, width, height) 284 | * @since 2.7.0 285 | */ 286 | declare function toPadding(value?: number | TRBL): ChartArea; 287 | interface CanvasFontSpec extends FontSpec { 288 | string: string; 289 | } 290 | /** 291 | * Parses font options and returns the font object. 292 | * @param options - A object that contains font options to be parsed. 293 | * @param fallback - A object that contains fallback font options. 294 | * @return The font object. 295 | * @private 296 | */ 297 | declare function toFont(options: Partial, fallback?: Partial): { 298 | family: string; 299 | lineHeight: number; 300 | size: number; 301 | style: "normal" | "italic" | "oblique" | "initial" | "inherit"; 302 | weight: string; 303 | string: string; 304 | }; 305 | /** 306 | * Evaluates the given `inputs` sequentially and returns the first defined value. 307 | * @param inputs - An array of values, falling back to the last value. 308 | * @param context - If defined and the current value is a function, the value 309 | * is called with `context` as first argument and the result becomes the new input. 310 | * @param index - If defined and the current value is an array, the value 311 | * at `index` become the new input. 312 | * @param info - object to return information about resolution in 313 | * @param info.cacheable - Will be set to `false` if option is not cacheable. 314 | * @since 2.7.0 315 | */ 316 | declare function resolve(inputs: Array, context?: object, index?: number, info?: { 317 | cacheable: boolean; 318 | }): unknown; 319 | /** 320 | * @param minmax 321 | * @param grace 322 | * @param beginAtZero 323 | * @private 324 | */ 325 | declare function _addGrace(minmax: { 326 | min: number; 327 | max: number; 328 | }, grace: number | string, beginAtZero: boolean): { 329 | min: number; 330 | max: number; 331 | }; 332 | /** 333 | * Create a context inheriting parentContext 334 | * @param parentContext 335 | * @param context 336 | * @returns 337 | */ 338 | declare function createContext

(parentContext: P, context: T): P extends null ? T : P & T; 339 | 340 | /** 341 | * @alias Chart.helpers.math 342 | * @namespace 343 | */ 344 | declare const PI: number; 345 | declare const TAU: number; 346 | declare const PITAU: number; 347 | declare const INFINITY: number; 348 | declare const RAD_PER_DEG: number; 349 | declare const HALF_PI: number; 350 | declare const QUARTER_PI: number; 351 | declare const TWO_THIRDS_PI: number; 352 | declare const log10: (x: number) => number; 353 | declare const sign: (x: number) => number; 354 | declare function almostEquals(x: number, y: number, epsilon: number): boolean; 355 | /** 356 | * Implementation of the nice number algorithm used in determining where axis labels will go 357 | */ 358 | declare function niceNum(range: number): number; 359 | /** 360 | * Returns an array of factors sorted from 1 to sqrt(value) 361 | * @private 362 | */ 363 | declare function _factorize(value: number): number[]; 364 | declare function isNumber(n: unknown): n is number; 365 | declare function almostWhole(x: number, epsilon: number): boolean; 366 | /** 367 | * @private 368 | */ 369 | declare function _setMinAndMaxByKey(array: Record[], target: { 370 | min: number; 371 | max: number; 372 | }, property: string): void; 373 | declare function toRadians(degrees: number): number; 374 | declare function toDegrees(radians: number): number; 375 | /** 376 | * Returns the number of decimal places 377 | * i.e. the number of digits after the decimal point, of the value of this Number. 378 | * @param x - A number. 379 | * @returns The number of decimal places. 380 | * @private 381 | */ 382 | declare function _decimalPlaces(x: number): number; 383 | declare function getAngleFromPoint(centrePoint: Point$1, anglePoint: Point$1): { 384 | angle: number; 385 | distance: number; 386 | }; 387 | declare function distanceBetweenPoints(pt1: Point$1, pt2: Point$1): number; 388 | /** 389 | * Shortest distance between angles, in either direction. 390 | * @private 391 | */ 392 | declare function _angleDiff(a: number, b: number): number; 393 | /** 394 | * Normalize angle to be between 0 and 2*PI 395 | * @private 396 | */ 397 | declare function _normalizeAngle(a: number): number; 398 | /** 399 | * @private 400 | */ 401 | declare function _angleBetween(angle: number, start: number, end: number, sameAngleIsFullCircle?: boolean): boolean; 402 | /** 403 | * Limit `value` between `min` and `max` 404 | * @param value 405 | * @param min 406 | * @param max 407 | * @private 408 | */ 409 | declare function _limitValue(value: number, min: number, max: number): number; 410 | /** 411 | * @param {number} value 412 | * @private 413 | */ 414 | declare function _int16Range(value: number): number; 415 | /** 416 | * @param value 417 | * @param start 418 | * @param end 419 | * @param [epsilon] 420 | * @private 421 | */ 422 | declare function _isBetween(value: number, start: number, end: number, epsilon?: number): boolean; 423 | 424 | interface RTLAdapter { 425 | x(x: number): number; 426 | setWidth(w: number): void; 427 | textAlign(align: 'center' | 'left' | 'right'): 'center' | 'left' | 'right'; 428 | xPlus(x: number, value: number): number; 429 | leftForLtr(x: number, itemWidth: number): number; 430 | } 431 | declare function getRtlAdapter(rtl: boolean, rectX: number, width: number): RTLAdapter; 432 | declare function overrideTextDirection(ctx: CanvasRenderingContext2D, direction: 'ltr' | 'rtl'): void; 433 | declare function restoreTextDirection(ctx: CanvasRenderingContext2D, original?: [string, string]): void; 434 | 435 | declare function clearCanvas(canvas: HTMLCanvasElement, ctx?: CanvasRenderingContext2D): void; 436 | 437 | declare function clipArea(ctx: CanvasRenderingContext2D, area: ChartArea): void; 438 | 439 | declare function unclipArea(ctx: CanvasRenderingContext2D): void; 440 | 441 | interface DrawPointOptions { 442 | pointStyle: PointStyle; 443 | rotation?: number; 444 | radius: number; 445 | borderWidth: number; 446 | } 447 | 448 | declare function drawPoint(ctx: CanvasRenderingContext2D, options: DrawPointOptions, x: number, y: number): void; 449 | 450 | declare function drawPointLegend(ctx: CanvasRenderingContext2D, options: DrawPointOptions, x: number, y: number, w: number): void; 451 | 452 | /** 453 | * Converts the given font object into a CSS font string. 454 | * @param font a font object 455 | * @return The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font 456 | */ 457 | declare function toFontString(font: { size: number; family: string; style?: string; weight?: string }): string | null; 458 | 459 | interface RenderTextOpts { 460 | /** 461 | * The fill color of the text. If unset, the existing 462 | * fillStyle property of the canvas is unchanged. 463 | */ 464 | color?: Color$1; 465 | 466 | /** 467 | * The width of the strikethrough / underline 468 | * @default 2 469 | */ 470 | decorationWidth?: number; 471 | 472 | /** 473 | * The max width of the text in pixels 474 | */ 475 | maxWidth?: number; 476 | 477 | /** 478 | * A rotation to be applied to the canvas 479 | * This is applied after the translation is applied 480 | */ 481 | rotation?: number; 482 | 483 | /** 484 | * Apply a strikethrough effect to the text 485 | */ 486 | strikethrough?: boolean; 487 | 488 | /** 489 | * The color of the text stroke. If unset, the existing 490 | * strokeStyle property of the context is unchanged 491 | */ 492 | strokeColor?: Color$1; 493 | 494 | /** 495 | * The text stroke width. If unset, the existing 496 | * lineWidth property of the context is unchanged 497 | */ 498 | strokeWidth?: number; 499 | 500 | /** 501 | * The text alignment to use. If unset, the existing 502 | * textAlign property of the context is unchanged 503 | */ 504 | textAlign: CanvasTextAlign; 505 | 506 | /** 507 | * The text baseline to use. If unset, the existing 508 | * textBaseline property of the context is unchanged 509 | */ 510 | textBaseline: CanvasTextBaseline; 511 | 512 | /** 513 | * If specified, a translation to apply to the context 514 | */ 515 | translation?: [number, number]; 516 | 517 | /** 518 | * Underline the text 519 | */ 520 | underline?: boolean; 521 | } 522 | 523 | declare function renderText( 524 | ctx: CanvasRenderingContext2D, 525 | text: string | string[], 526 | x: number, 527 | y: number, 528 | font: CanvasFontSpec, 529 | opts?: RenderTextOpts 530 | ): void; 531 | 532 | declare function addRoundedRectPath(ctx: CanvasRenderingContext2D, rect: RoundedRect): void; 533 | 534 | export { ArrayListener, CanvasFontSpec, DrawPointOptions, HALF_PI, INFINITY, PI, PITAU, QUARTER_PI, RAD_PER_DEG, RTLAdapter, RenderTextOpts, SplinePoint, TAU, TWO_THIRDS_PI, _addGrace, _alignStartEnd, _angleBetween, _angleDiff, _arrayUnique, _bezierInterpolation, _decimalPlaces, _factorize, _filterBetween, _getParentNode, _getStartAndCountOfVisiblePoints, _int16Range, _isBetween, _isDomSupported, _limitValue, _lookup, _lookupByKey, _normalizeAngle, _pointInLine, _readValueToProps, _rlookupByKey, _scaleRangesChanged, _setMinAndMaxByKey, _steppedInterpolation, _textX, _toLeftRightCenter, _updateBezierControlPoints, addRoundedRectPath, almostEquals, almostWhole, clearCanvas, clipArea, color, createContext, debounce, distanceBetweenPoints, drawPoint, drawPointLegend, fontString, formatNumber, getAngleFromPoint, getHoverColor, getMaximumSize, getRelativePosition, getRtlAdapter, getStyle, isNumber, isPatternOrGradient, listenArrayEvents, log10, niceNum, overrideTextDirection, readUsedSize, renderText, requestAnimFrame, resolve, restoreTextDirection, retinaScale, sign, splineCurve, splineCurveMonotone, supportsEventListenerOptions, throttled, toDegrees, toFont, toFontString, toLineHeight, toPadding, toRadians, toTRBL, toTRBLCorners, unclipArea, unlistenArrayEvents }; 535 | -------------------------------------------------------------------------------- /assets/js/lib/chartjs/helpers.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Chart.js v4.0.1 3 | * https://www.chartjs.org 4 | * (c) 2022 Chart.js Contributors 5 | * Released under the MIT License 6 | */ 7 | export { H as HALF_PI, b2 as INFINITY, P as PI, b1 as PITAU, b4 as QUARTER_PI, b3 as RAD_PER_DEG, T as TAU, b5 as TWO_THIRDS_PI, R as _addGrace, X as _alignPixel, a2 as _alignStartEnd, p as _angleBetween, b6 as _angleDiff, _ as _arrayUnique, a8 as _attachContext, as as _bezierCurveTo, ap as _bezierInterpolation, ax as _boundSegment, an as _boundSegments, a5 as _capitalize, am as _computeSegments, a9 as _createResolver, aK as _decimalPlaces, aV as _deprecated, aa as _descriptors, ah as _elementsEqual, N as _factorize, aO as _filterBetween, I as _getParentNode, q as _getStartAndCountOfVisiblePoints, W as _int16Range, aj as _isBetween, ai as _isClickEvent, M as _isDomSupported, C as _isPointInArea, S as _limitValue, aN as _longestText, aP as _lookup, B as _lookupByKey, V as _measureText, aT as _merger, aU as _mergerIf, ay as _normalizeAngle, y as _parseObjectDataRadialScale, aq as _pointInLine, ak as _readValueToProps, A as _rlookupByKey, w as _scaleRangesChanged, aG as _setMinAndMaxByKey, aW as _splitKey, ao as _steppedInterpolation, ar as _steppedLineTo, aB as _textX, a1 as _toLeftRightCenter, al as _updateBezierControlPoints, au as addRoundedRectPath, aJ as almostEquals, aI as almostWhole, Q as callback, af as clearCanvas, Y as clipArea, aS as clone, c as color, j as createContext, ad as debounce, h as defined, aE as distanceBetweenPoints, at as drawPoint, aD as drawPointLegend, F as each, e as easingEffects, O as finiteOrDefault, a$ as fontString, o as formatNumber, D as getAngleFromPoint, aR as getHoverColor, G as getMaximumSize, z as getRelativePosition, az as getRtlAdapter, a_ as getStyle, b as isArray, g as isFinite, a7 as isFunction, k as isNullOrUndef, x as isNumber, i as isObject, aQ as isPatternOrGradient, l as listenArrayEvents, aM as log10, a4 as merge, ab as mergeIf, aH as niceNum, aF as noop, aA as overrideTextDirection, J as readUsedSize, Z as renderText, r as requestAnimFrame, a as resolve, f as resolveObjectKey, aC as restoreTextDirection, ae as retinaScale, ag as setsEqual, s as sign, aY as splineCurve, aZ as splineCurveMonotone, K as supportsEventListenerOptions, L as throttled, U as toDegrees, n as toDimension, a0 as toFont, aX as toFontString, b0 as toLineHeight, E as toPadding, m as toPercentage, t as toRadians, av as toTRBL, aw as toTRBLCorners, ac as uid, $ as unclipArea, u as unlistenArrayEvents, v as valueOrDefault } from './chunks/helpers.segment.js'; 8 | //# sourceMappingURL=helpers.js.map 9 | -------------------------------------------------------------------------------- /assets/js/lib/chartjs/helpers.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"helpers.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"} -------------------------------------------------------------------------------- /assets/js/lib/katex/auto-render.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={771:function(t){t.exports=e}},r={};function n(e){var i=r[e];if(void 0!==i)return i.exports;var a=r[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,{a:t}),t},n.d=function(e,t){for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var i={};return function(){n.d(i,{default:function(){return s}});var e=n(771),t=n.n(e),r=function(e,t,r){for(var n=r,i=0,a=e.length;n0&&(i.push({type:"text",data:e.slice(0,n)}),e=e.slice(n));var l=t.findIndex((function(t){return e.startsWith(t.left)}));if(-1===(n=r(t[l].right,e,t[l].left.length)))break;var d=e.slice(0,n+t[l].right.length),s=a.test(d)?d:e.slice(t[l].left.length,n);i.push({type:"math",data:s,rawData:d,display:t[l].display}),e=e.slice(n+t[l].right.length)}return""!==e&&i.push({type:"text",data:e}),i},l=function(e,r){var n=o(e,r.delimiters);if(1===n.length&&"text"===n[0].type)return null;for(var i=document.createDocumentFragment(),a=0;a.newline{display:block}.katex .base{position:relative;white-space:nowrap;width:-webkit-min-content;width:-moz-min-content;width:min-content}.katex .base,.katex .strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .hbox{width:100%}.katex .hbox,.katex .thinbox{display:inline-flex;flex-direction:row}.katex .thinbox{max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline,.katex .hline,.katex .mfrac .frac-line,.katex .overline .overline-line,.katex .rule,.katex .underline .underline-line{min-height:1px}.katex .mspace{display:inline-block}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.inner,.katex .llap>.inner,.katex .rlap>.inner{position:absolute}.katex .clap>.fix,.katex .llap>.fix,.katex .rlap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .clap>.inner,.katex .rlap>.inner{left:0}.katex .clap>.inner>span{margin-left:-50%;margin-right:50%}.katex .rule{border:0 solid;display:inline-block;position:relative}.katex .hline,.katex .overline .overline-line,.katex .underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.root{margin-left:.27777778em;margin-right:-.55555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .sizing.reset-size2.size1{font-size:.83333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .sizing.reset-size2.size3{font-size:1.16666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .sizing.reset-size2.size4{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .sizing.reset-size2.size6{font-size:1.66666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .sizing.reset-size2.size10{font-size:3.45666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .sizing.reset-size2.size11{font-size:4.14666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .sizing.reset-size3.size1{font-size:.71428571em}.katex .fontsize-ensurer.reset-size3.size2,.katex .sizing.reset-size3.size2{font-size:.85714286em}.katex .fontsize-ensurer.reset-size3.size3,.katex .sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .sizing.reset-size3.size4{font-size:1.14285714em}.katex .fontsize-ensurer.reset-size3.size5,.katex .sizing.reset-size3.size5{font-size:1.28571429em}.katex .fontsize-ensurer.reset-size3.size6,.katex .sizing.reset-size3.size6{font-size:1.42857143em}.katex .fontsize-ensurer.reset-size3.size7,.katex .sizing.reset-size3.size7{font-size:1.71428571em}.katex .fontsize-ensurer.reset-size3.size8,.katex .sizing.reset-size3.size8{font-size:2.05714286em}.katex .fontsize-ensurer.reset-size3.size9,.katex .sizing.reset-size3.size9{font-size:2.46857143em}.katex .fontsize-ensurer.reset-size3.size10,.katex .sizing.reset-size3.size10{font-size:2.96285714em}.katex .fontsize-ensurer.reset-size3.size11,.katex .sizing.reset-size3.size11{font-size:3.55428571em}.katex .fontsize-ensurer.reset-size4.size1,.katex .sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .sizing.reset-size5.size1{font-size:.55555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .sizing.reset-size5.size2{font-size:.66666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .sizing.reset-size5.size3{font-size:.77777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .sizing.reset-size5.size4{font-size:.88888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .sizing.reset-size5.size6{font-size:1.11111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .sizing.reset-size5.size7{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .sizing.reset-size5.size10{font-size:2.30444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .sizing.reset-size5.size11{font-size:2.76444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .sizing.reset-size7.size1{font-size:.41666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .sizing.reset-size7.size3{font-size:.58333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .sizing.reset-size7.size4{font-size:.66666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .sizing.reset-size7.size6{font-size:.83333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .sizing.reset-size7.size10{font-size:1.72833333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .sizing.reset-size7.size11{font-size:2.07333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .sizing.reset-size8.size1{font-size:.34722222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .sizing.reset-size8.size2{font-size:.41666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .sizing.reset-size8.size3{font-size:.48611111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .sizing.reset-size8.size4{font-size:.55555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .sizing.reset-size8.size6{font-size:.69444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .sizing.reset-size8.size7{font-size:.83333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .sizing.reset-size8.size10{font-size:1.44027778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .sizing.reset-size8.size11{font-size:1.72777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .sizing.reset-size9.size1{font-size:.28935185em}.katex .fontsize-ensurer.reset-size9.size2,.katex .sizing.reset-size9.size2{font-size:.34722222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .sizing.reset-size9.size3{font-size:.40509259em}.katex .fontsize-ensurer.reset-size9.size4,.katex .sizing.reset-size9.size4{font-size:.46296296em}.katex .fontsize-ensurer.reset-size9.size5,.katex .sizing.reset-size9.size5{font-size:.52083333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .sizing.reset-size9.size6{font-size:.5787037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .sizing.reset-size9.size7{font-size:.69444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .sizing.reset-size9.size8{font-size:.83333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .sizing.reset-size9.size10{font-size:1.20023148em}.katex .fontsize-ensurer.reset-size9.size11,.katex .sizing.reset-size9.size11{font-size:1.43981481em}.katex .fontsize-ensurer.reset-size10.size1,.katex .sizing.reset-size10.size1{font-size:.24108004em}.katex .fontsize-ensurer.reset-size10.size2,.katex .sizing.reset-size10.size2{font-size:.28929605em}.katex .fontsize-ensurer.reset-size10.size3,.katex .sizing.reset-size10.size3{font-size:.33751205em}.katex .fontsize-ensurer.reset-size10.size4,.katex .sizing.reset-size10.size4{font-size:.38572806em}.katex .fontsize-ensurer.reset-size10.size5,.katex .sizing.reset-size10.size5{font-size:.43394407em}.katex .fontsize-ensurer.reset-size10.size6,.katex .sizing.reset-size10.size6{font-size:.48216008em}.katex .fontsize-ensurer.reset-size10.size7,.katex .sizing.reset-size10.size7{font-size:.57859209em}.katex .fontsize-ensurer.reset-size10.size8,.katex .sizing.reset-size10.size8{font-size:.69431051em}.katex .fontsize-ensurer.reset-size10.size9,.katex .sizing.reset-size10.size9{font-size:.83317261em}.katex .fontsize-ensurer.reset-size10.size10,.katex .sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .sizing.reset-size10.size11{font-size:1.19961427em}.katex .fontsize-ensurer.reset-size11.size1,.katex .sizing.reset-size11.size1{font-size:.20096463em}.katex .fontsize-ensurer.reset-size11.size2,.katex .sizing.reset-size11.size2{font-size:.24115756em}.katex .fontsize-ensurer.reset-size11.size3,.katex .sizing.reset-size11.size3{font-size:.28135048em}.katex .fontsize-ensurer.reset-size11.size4,.katex .sizing.reset-size11.size4{font-size:.32154341em}.katex .fontsize-ensurer.reset-size11.size5,.katex .sizing.reset-size11.size5{font-size:.36173633em}.katex .fontsize-ensurer.reset-size11.size6,.katex .sizing.reset-size11.size6{font-size:.40192926em}.katex .fontsize-ensurer.reset-size11.size7,.katex .sizing.reset-size11.size7{font-size:.48231511em}.katex .fontsize-ensurer.reset-size11.size8,.katex .sizing.reset-size11.size8{font-size:.57877814em}.katex .fontsize-ensurer.reset-size11.size9,.katex .sizing.reset-size11.size9{font-size:.69453376em}.katex .fontsize-ensurer.reset-size11.size10,.katex .sizing.reset-size11.size10{font-size:.83360129em}.katex .fontsize-ensurer.reset-size11.size11,.katex .sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .accent .accent-body{position:relative}.katex .accent .accent-body:not(.accent-full){width:0}.katex .overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .stretchy:after,.katex .stretchy:before{content:""}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .eqn-num:before{content:"(" counter(katexEqnNo) ")";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:"(" counter(mmlEqnNo) ")";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo} 2 | -------------------------------------------------------------------------------- /assets/js/lib/lazysizes.min.js: -------------------------------------------------------------------------------- 1 | /*! lazysizes - v5.3.2 */ 2 | 3 | !function(e){var t=function(u,D,f){"use strict";var k,H;if(function(){var e;var t={lazyClass:"lazyload",loadedClass:"lazyloaded",loadingClass:"lazyloading",preloadClass:"lazypreload",errorClass:"lazyerror",autosizesClass:"lazyautosizes",fastLoadedClass:"ls-is-cached",iframeLoadMode:0,srcAttr:"data-src",srcsetAttr:"data-srcset",sizesAttr:"data-sizes",minSize:40,customMedia:{},init:true,expFactor:1.5,hFac:.8,loadMode:2,loadHidden:true,ricTimeout:0,throttleDelay:125};H=u.lazySizesConfig||u.lazysizesConfig||{};for(e in t){if(!(e in H)){H[e]=t[e]}}}(),!D||!D.getElementsByClassName){return{init:function(){},cfg:H,noSupport:true}}var O=D.documentElement,i=u.HTMLPictureElement,P="addEventListener",$="getAttribute",q=u[P].bind(u),I=u.setTimeout,U=u.requestAnimationFrame||I,o=u.requestIdleCallback,j=/^picture$/i,r=["load","error","lazyincluded","_lazyloaded"],a={},G=Array.prototype.forEach,J=function(e,t){if(!a[t]){a[t]=new RegExp("(\\s|^)"+t+"(\\s|$)")}return a[t].test(e[$]("class")||"")&&a[t]},K=function(e,t){if(!J(e,t)){e.setAttribute("class",(e[$]("class")||"").trim()+" "+t)}},Q=function(e,t){var a;if(a=J(e,t)){e.setAttribute("class",(e[$]("class")||"").replace(a," "))}},V=function(t,a,e){var i=e?P:"removeEventListener";if(e){V(t,a)}r.forEach(function(e){t[i](e,a)})},X=function(e,t,a,i,r){var n=D.createEvent("Event");if(!a){a={}}a.instance=k;n.initEvent(t,!i,!r);n.detail=a;e.dispatchEvent(n);return n},Y=function(e,t){var a;if(!i&&(a=u.picturefill||H.pf)){if(t&&t.src&&!e[$]("srcset")){e.setAttribute("srcset",t.src)}a({reevaluate:true,elements:[e]})}else if(t&&t.src){e.src=t.src}},Z=function(e,t){return(getComputedStyle(e,null)||{})[t]},s=function(e,t,a){a=a||e.offsetWidth;while(a49?function(){o(t,{timeout:n});if(n!==H.ricTimeout){n=H.ricTimeout}}:te(function(){I(t)},true);return function(e){var t;if(e=e===true){n=33}if(a){return}a=true;t=r-(f.now()-i);if(t<0){t=0}if(e||t<9){s()}else{I(s,t)}}},ie=function(e){var t,a;var i=99;var r=function(){t=null;e()};var n=function(){var e=f.now()-a;if(e0;if(r&&Z(i,"overflow")!="visible"){a=i.getBoundingClientRect();r=C>a.left&&pa.top-1&&g500&&O.clientWidth>500?500:370:H.expand;k._defEx=u;f=u*H.expFactor;c=H.hFac;A=null;if(w2&&h>2&&!D.hidden){w=f;N=0}else if(h>1&&N>1&&M<6){w=u}else{w=_}}if(l!==n){y=innerWidth+n*c;z=innerHeight+n;s=n*-1;l=n}a=d[t].getBoundingClientRect();if((b=a.bottom)>=s&&(g=a.top)<=z&&(C=a.right)>=s*c&&(p=a.left)<=y&&(b||C||p||g)&&(H.loadHidden||x(d[t]))&&(m&&M<3&&!o&&(h<3||N<4)||W(d[t],n))){R(d[t]);r=true;if(M>9){break}}else if(!r&&m&&!i&&M<4&&N<4&&h>2&&(v[0]||H.preloadAfterLoad)&&(v[0]||!o&&(b||C||p||g||d[t][$](H.sizesAttr)!="auto"))){i=v[0]||d[t]}}if(i&&!r){R(i)}}};var a=ae(t);var S=function(e){var t=e.target;if(t._lazyCache){delete t._lazyCache;return}L(e);K(t,H.loadedClass);Q(t,H.loadingClass);V(t,B);X(t,"lazyloaded")};var i=te(S);var B=function(e){i({target:e.target})};var T=function(e,t){var a=e.getAttribute("data-load-mode")||H.iframeLoadMode;if(a==0){e.contentWindow.location.replace(t)}else if(a==1){e.src=t}};var F=function(e){var t;var a=e[$](H.srcsetAttr);if(t=H.customMedia[e[$]("data-media")||e[$]("media")]){e.setAttribute("media",t)}if(a){e.setAttribute("srcset",a)}};var s=te(function(t,e,a,i,r){var n,s,o,l,u,f;if(!(u=X(t,"lazybeforeunveil",e)).defaultPrevented){if(i){if(a){K(t,H.autosizesClass)}else{t.setAttribute("sizes",i)}}s=t[$](H.srcsetAttr);n=t[$](H.srcAttr);if(r){o=t.parentNode;l=o&&j.test(o.nodeName||"")}f=e.firesLoad||"src"in t&&(s||n||l);u={target:t};K(t,H.loadingClass);if(f){clearTimeout(c);c=I(L,2500);V(t,B,true)}if(l){G.call(o.getElementsByTagName("source"),F)}if(s){t.setAttribute("srcset",s)}else if(n&&!l){if(d.test(t.nodeName)){T(t,n)}else{t.src=n}}if(r&&(s||l)){Y(t,{src:n})}}if(t._lazyRace){delete t._lazyRace}Q(t,H.lazyClass);ee(function(){var e=t.complete&&t.naturalWidth>1;if(!f||e){if(e){K(t,H.fastLoadedClass)}S(u);t._lazyCache=true;I(function(){if("_lazyCache"in t){delete t._lazyCache}},9)}if(t.loading=="lazy"){M--}},true)});var R=function(e){if(e._lazyRace){return}var t;var a=n.test(e.nodeName);var i=a&&(e[$](H.sizesAttr)||e[$]("sizes"));var r=i=="auto";if((r||!m)&&a&&(e[$]("src")||e.srcset)&&!e.complete&&!J(e,H.errorClass)&&J(e,H.lazyClass)){return}t=X(e,"lazyunveilread").detail;if(r){re.updateElem(e,true,e.offsetWidth)}e._lazyRace=true;M++;s(e,t,r,i,a)};var r=ie(function(){H.loadMode=3;a()});var o=function(){if(H.loadMode==3){H.loadMode=2}r()};var l=function(){if(m){return}if(f.now()-e<999){I(l,999);return}m=true;H.loadMode=3;a();q("scroll",o,true)};return{_:function(){e=f.now();k.elements=D.getElementsByClassName(H.lazyClass);v=D.getElementsByClassName(H.lazyClass+" "+H.preloadClass);q("scroll",a,true);q("resize",a,true);q("pageshow",function(e){if(e.persisted){var t=D.querySelectorAll("."+H.loadingClass);if(t.length&&t.forEach){U(function(){t.forEach(function(e){if(e.complete){R(e)}})})}}});if(u.MutationObserver){new MutationObserver(a).observe(O,{childList:true,subtree:true,attributes:true})}else{O[P]("DOMNodeInserted",a,true);O[P]("DOMAttrModified",a,true);setInterval(a,999)}q("hashchange",a,true);["focus","mouseover","click","load","transitionend","animationend"].forEach(function(e){D[P](e,a,true)});if(/d$|^c/.test(D.readyState)){l()}else{q("load",l);D[P]("DOMContentLoaded",a);I(l,2e4)}if(k.elements.length){t();ee._lsFlush()}else{a()}},checkElems:a,unveil:R,_aLSL:o}}(),re=function(){var a;var n=te(function(e,t,a,i){var r,n,s;e._lazysizesWidth=i;i+="px";e.setAttribute("sizes",i);if(j.test(t.nodeName||"")){r=t.getElementsByTagName("source");for(n=0,s=r.length;n { 4 | const OPTION_HEAD = option.querySelector('.calculator-content-head') 5 | 6 | OPTION_HEAD.addEventListener('click', (event) => { 7 | 8 | if(!event.composedPath().includes($('.js-add-button'))) { 9 | option.classList.toggle('calculator-content--active') 10 | } 11 | }) 12 | }) -------------------------------------------------------------------------------- /assets/js/themes.js: -------------------------------------------------------------------------------- 1 | const setActiveThemeButton = (theme) => { 2 | document.querySelectorAll('.header-popup__button').forEach(button => { 3 | if(button.classList.contains(`${theme}-theme`)) button.classList.add('header-popup__button--active') 4 | else button.classList.remove('header-popup__button--active') 5 | }) 6 | } 7 | 8 | switch (localStorage.getItem('theme')) { 9 | case 'light': 10 | document.documentElement.classList = ''; 11 | console.log('Theme: Light'); 12 | localStorage.setItem('theme', 'light'); 13 | window.addEventListener('DOMContentLoaded', () => { 14 | setActiveThemeButton('light') 15 | }) 16 | break; 17 | case 'dark': 18 | document.documentElement.classList = 'dark'; 19 | console.log('Theme: Dark'); 20 | localStorage.setItem('theme', 'dark'); 21 | window.addEventListener('DOMContentLoaded', () => { 22 | setActiveThemeButton('dark') 23 | }) 24 | break; 25 | case 'system': 26 | default: 27 | document.documentElement.classList = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : ''; 28 | console.log('Theme: System'); 29 | localStorage.setItem('theme', 'system'); 30 | window.addEventListener('DOMContentLoaded', () => { 31 | setActiveThemeButton('system') 32 | }) 33 | } -------------------------------------------------------------------------------- /assets/js/timepicker.js: -------------------------------------------------------------------------------- 1 | const INPUTS = $$('.timepicker-input'); 2 | let timepickerFormat = true 3 | let currentInput = { 4 | input: null, 5 | hour: null, 6 | minute: null, 7 | second: null, 8 | } 9 | 10 | function createTimepicker() { 11 | const TIMEPICKER = document.createElement('div'); 12 | const {top, left} = currentInput.input.getBoundingClientRect() 13 | const {offsetHeight} = currentInput.input 14 | const VALUE = currentInput.input.value.split(':') 15 | 16 | TIMEPICKER.classList.add('timepicker'); 17 | TIMEPICKER.innerHTML = ` 18 |

19 |
20 |
21 |

Hour

22 | 23 |
24 |

:

25 |
26 |

Minute

27 | 28 |
29 |

:

30 |
31 |

Second

32 | 33 |
34 |
35 |
36 | 39 | 42 |
43 |
44 | 45 | 54 | ` 55 | document.body.appendChild(TIMEPICKER); 56 | TIMEPICKER.style.top = top + window.scrollY + 9 + offsetHeight + 'px'; 57 | TIMEPICKER.style.left = left - 13 + 'px'; 58 | TIMEPICKER.classList.add('timepicker--active'); 59 | 60 | const TIMEPICKER_DONE = document.getElementById('timepicker_done') 61 | const TIMEPICKER_CANCEL = document.getElementById('timepicker_cancel') 62 | const TIMEPICKER_HOUR = document.getElementById('timepicker_hour') 63 | const TIMEPICKER_MINUTE = document.getElementById('timepicker_minute') 64 | const TIMEPICKER_SECOND = document.getElementById('timepicker_second') 65 | const TIMEPICKER_FORMAT_SWITCHER = document.getElementById('timepicker_format_switcher') 66 | const TIMEPICKER_FORMAT = document.getElementById('timepicker_format') 67 | const SWITCH_TO_AM = document.getElementById('switch_to_am') 68 | const SWITCH_TO_PM = document.getElementById('switch_to_pm') 69 | 70 | SWITCH_TO_AM.addEventListener('click', () => { 71 | SWITCH_TO_PM.classList.remove('button-switcher--active') 72 | SWITCH_TO_AM.classList.add('button-switcher--active') 73 | }) 74 | 75 | SWITCH_TO_PM.addEventListener('click', () => { 76 | SWITCH_TO_AM.classList.remove('button-switcher--active') 77 | SWITCH_TO_PM.classList.add('button-switcher--active') 78 | }) 79 | 80 | TIMEPICKER_HOUR.addEventListener('input', (event) => { 81 | limitLengthHandler(event.target, 2) 82 | currentInput.hour = event.target.value 83 | if(event.target.value.length === 2) TIMEPICKER_MINUTE.focus() 84 | }) 85 | TIMEPICKER_MINUTE.addEventListener('input', (event) => { 86 | limitLengthHandler(event.target, 2) 87 | currentInput.minute = event.target.value 88 | if(event.target.value.length === 2) TIMEPICKER_SECOND.focus() 89 | }) 90 | TIMEPICKER_SECOND.addEventListener('input', (event) => { 91 | currentInput.second = event.target.value 92 | limitLengthHandler(event.target, 2) 93 | }) 94 | 95 | TIMEPICKER_FORMAT_SWITCHER.addEventListener('input', (event) => { 96 | if(event.target.checked) { 97 | TIMEPICKER_FORMAT.style.display = 'none' 98 | timepickerFormat = false 99 | } 100 | else { 101 | TIMEPICKER_FORMAT.style.display = 'flex' 102 | timepickerFormat = true 103 | } 104 | }) 105 | 106 | TIMEPICKER_DONE.addEventListener('click', onDoneTimepicker) 107 | window.addEventListener('keydown', onDoneTimepickerEnter) 108 | 109 | TIMEPICKER_CANCEL.addEventListener('click', () => { 110 | onCloseTimepicker(); 111 | }) 112 | } 113 | 114 | function limitLengthHandler(input, limit) { 115 | if(input.value.length > limit) { 116 | input.value = input.value.slice(0, limit); 117 | } 118 | return input.value; 119 | } 120 | 121 | function onDoneTimepickerEnter (event) { 122 | if(event.key === 'Enter') { 123 | onDoneTimepicker(); 124 | } 125 | } 126 | 127 | function onDoneTimepicker () { 128 | let hours = currentInput.hour < 10 ? '0' + currentInput.hour : currentInput.hour 129 | let minutes = currentInput.minute < 10 ? '0' + currentInput.minute : currentInput.minute 130 | let seconds = currentInput.second < 10 ? '0' + currentInput.second : currentInput.second 131 | 132 | currentInput.input.value = `${hours > 0 ? hours : '00'}:${minutes > 0 ? minutes : '00'}:${seconds > 0 ? seconds : '00'}` 133 | onCloseTimepicker(); 134 | window.removeEventListener('keydown', onDoneTimepickerEnter) 135 | window.removeEventListener('click', onCloseTimepickerOutside) 136 | } 137 | 138 | function onCloseTimepicker() { 139 | currentInput.input = null 140 | document.body.removeChild($('.timepicker')); 141 | window.removeEventListener('click', onCloseTimepickerOutside) 142 | } 143 | 144 | function onCloseTimepickerOutside(event) { 145 | if($('.timepicker') && !(event.composedPath().includes($('.timepicker')) || event.composedPath().includes(currentInput.input))) { 146 | onCloseTimepicker(); 147 | } 148 | } 149 | 150 | window.timepicker = (event) => { 151 | if($('.timepicker')) { 152 | onCloseTimepicker(); 153 | } else { 154 | currentInput.input = event.target 155 | createTimepicker() 156 | window.addEventListener('click', onCloseTimepickerOutside) 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /ci_interest_calculator.php: -------------------------------------------------------------------------------- 1 | Interest Calculator
'; 20 | } 21 | 22 | 23 | add_shortcode( 'ci_interest_calculator', 'display_calcio_ci_interest_calculator' ); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Interest Calculator 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |
28 |
29 |
30 |
31 |
39 |
40 |
55 |
56 | 63 | 69 |
70 |
71 |
86 |
87 |
102 |
103 | 104 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 |
121 |
122 |
123 |
124 |
125 |
126 | 127 | 131 | 132 |

There was an error with your calculation.

133 |
134 |
135 |
140 |
141 |

Interest

142 |
143 |
144 |

End Balance: $135,479.01

145 |

After Inflation Adjustment: $100,809.11

146 |

Total Principal: $99,000.00

147 |

Total Interest: $39,224.74

148 |

Total Interest after Tax: $36,479.01

149 |
150 |
151 | 152 |
153 |
154 |
155 |

Initial investment

156 |
157 |
158 |
159 |

Interest after tax

160 |
161 |
162 |
163 |

Contributions

164 |
165 |
166 |
167 |

Tax

168 |
169 |
170 |
171 |
172 |
173 | 174 |
175 |

0 yr

176 |

5 yr

177 |

10 yr

178 |
179 |
180 |
181 | 187 |
188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 |
#DEPOSITINTERESTENDING BALANCE
1$32,400.00$1,486.44$33,886.44
2$7,400.00$1,908.58$43,195.01
3$7,400.00$2,350.77$52,945.78
4$7,400.00$2,813.97$63,159.75
5$7,400.00$3,299.17$73,858.93
6$7,400.00$3,807.43$85,066.35
7$7,400.00$4,339.82$96,806.18
8$7,400.00$4,897.51$109,103.69
9$7,400.00$5,481.69$121,985.38
10$7,400.00$6,093.62$135,479.01
249 |
250 |
251 |
252 | 258 |
259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 |
#DEPOSITINTERESTENDING BALANCE
1$30,200.00$117.03$30,317.03
2$200.00$118.25$30,635.28
3$200.00$119.49$30,954.77
4$200.00$120.72$31,275.49
5$200.00$121.97$31,597.46
6$200.00$123.22$31,920.67
7$200.00$124.47$32,245.14
8$200.00$125.72$32,570.87
9$200.00$126.99$32,897.85
10$200.00$128.25$33,226.11
11$200.00$129.53$33,555.63
12$200.00$130.80$33,886.44
Year 1 End
13$5,200.00$151.46$39,237.90
14$200.00$152.82$39,590.72
15$200.00$154.19$39,944.91
16$200.00$155.56$40,300.47
17$200.00$156.94$40,657.41
18$200.00$158.32$41,015.73
19$200.00$159.71$41,375.44
20$200.00$161.10$41,736.55
21$200.00$162.50$42,099.05
22$200.00$163.91$42,462.96
23$200.00$165.32$42,828.28
24$200.00$166.73$43,195.01
Year 2 End
25$5,200.00$187.53$48,582.54
26$200.00$189.03$48,971.57
27$200.00$190.54$49,362.11
28$200.00$192.05$49,754.17
29$200.00$193.57$50,147.74
30$200.00$195.10$50,542.84
31$200.00$196.63$50,939.47
32$200.00$198.17$51,337.63
33$200.00$199.71$51,737.34
34$200.00$201.26$52,138.60
35$200.00$202.81$52,541.41
36$200.00$204.37$52,945.78
Year 3 End
37$5,200.00$225.31$58,371.10
38$200.00$226.96$58,798.06
39$200.00$228.62$59,226.68
40$200.00$230.28$59,656.96
41$200.00$231.95$60,088.90
42$200.00$233.62$60,522.52
43$200.00$235.30$60,957.82
44$200.00$236.99$61,394.81
45$200.00$238.68$61,833.49
46$200.00$240.38$62,273.87
47$200.00$242.09$62,715.95
48$200.00$243.80$63,159.75
Year 4 End
49$5,200.00$264.89$68,624.65
50$200.00$266.70$69,091.34
51$200.00$268.50$69,559.85
52$200.00$270.32$70,030.17
53$200.00$272.14$70,502.31
54$200.00$273.97$70,976.28
55$200.00$275.81$71,452.09
56$200.00$277.65$71,929.74
57$200.00$279.50$72,409.24
58$200.00$281.36$72,890.60
59$200.00$283.23$73,373.83
60$200.00$285.10$73,858.93
Year 5 End
61$5,200.00$306.35$79,365.28
62$200.00$308.32$79,873.60
63$200.00$310.29$80,383.88
64$200.00$312.26$80,896.14
65$200.00$314.25$81,410.39
66$200.00$316.24$81,926.63
67$200.00$318.24$82,444.87
68$200.00$320.25$82,965.12
69$200.00$322.26$83,487.39
70$200.00$324.29$84,011.67
71$200.00$326.32$84,537.99
72$200.00$328.36$85,066.35
Year 6 End
73$5,200.00$349.78$90,616.14
74$200.00$351.91$91,168.05
75$200.00$354.05$91,722.10
76$200.00$356.20$92,278.30
77$200.00$358.35$92,836.65
78$200.00$360.52$93,397.17
79$200.00$362.69$93,959.86
80$200.00$364.87$94,524.73
81$200.00$367.06$95,091.79
82$200.00$369.26$95,661.04
83$200.00$371.46$96,232.50
84$200.00$373.68$96,806.18
Year 7 End
85$5,200.00$395.27$102,401.45
86$200.00$397.58$102,999.03
87$200.00$399.90$103,598.93
88$200.00$402.22$104,201.15
89$200.00$404.55$104,805.71
90$200.00$406.90$105,412.60
91$200.00$409.25$106,021.85
92$200.00$411.61$106,633.46
93$200.00$413.98$107,247.44
94$200.00$416.36$107,863.80
95$200.00$418.75$108,482.55
96$200.00$421.14$109,103.69
Year 8 End
97$5,200.00$442.93$114,746.62
98$200.00$445.42$115,392.04
99$200.00$447.92$116,039.96
100$200.00$450.43$116,690.39
101$200.00$452.95$117,343.34
102$200.00$455.48$117,998.82
103$200.00$458.02$118,656.84
104$200.00$460.57$119,317.41
105$200.00$463.13$119,980.54
106$200.00$465.70$120,646.24
107$200.00$468.28$121,314.52
108$200.00$470.87$121,985.38
Year 9 End
109$5,200.00$492.84$127,678.23
110$200.00$495.53$128,373.76
111$200.00$498.22$129,071.98
112$200.00$500.93$129,772.91
113$200.00$503.65$130,476.55
114$200.00$506.37$131,182.92
115$200.00$509.11$131,892.03
116$200.00$511.86$132,603.89
117$200.00$514.62$133,318.50
118$200.00$517.38$134,035.89
119$200.00$520.16$134,756.05
120$200.00$522.95$135,479.01
Year 10 End
870 |
871 |
872 |
873 |
874 |
875 |
876 | 877 | 878 | 879 | 880 | 881 | -------------------------------------------------------------------------------- /plugin.php: -------------------------------------------------------------------------------- 1 | Interest Calculator
'; 20 | } 21 | 22 | add_shortcode( 'ci_interest_calculator', 'display_ci_interest_calculator' ); -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Interest Calculator Widget for WordPress 2 | 3 | With this free interest calculator you can compute accumulation schedules, final balances, and accrued interest. 4 | 5 | ![Interest Calculator Input Form](/assets/images/screenshot-1.png "Interest Calculator Input Form") 6 | 7 | ## Installation 8 | 9 | 1. [Download](https://github.com/pub-calculator-io/interest-calculator/archive/refs/heads/master.zip) the ZIP file of this repository. 10 | 2. Upload the /interest-calculator-master/ folder to the /wp-content/plugins/ directory. 11 | 3. Activate the [Interest Calculator](https://www.calculator.io/interest-calculator/ "Interest Calculator Homepage") plugin through the "Plugins" menu in WordPress. 12 | 13 | ## Usage 14 | * Add the shortcode `[ci_interest_calculator]` to your page, post or sidebar. 15 | * Or add the following code: `` to your template where you would like the Interest Calculator to appear. 16 | 17 | ## Libraries in Use 18 | 1. https://mathjs.org/ 19 | 2. https://katex.org/ 20 | 3. https://github.com/aFarkas/lazysizes 21 | 4. https://github.com/RobinHerbots/Inputmask 22 | 5. https://air-datepicker.com/ 23 | 6. https://www.chartjs.org/ 24 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === CI Interest calculator === 2 | Contributors: calculatorio 3 | Tags: interest calculator, calculate interest, interest rate calculator, simple interest calculator, compound interest calculator, interest formula, interest on savings, loan interest calculator, mortgage interest calculator, credit card interest calculator 4 | Requires at least: 5.0 5 | Tested up to: 6.4.0 6 | Stable tag: 1.0.0 7 | License: GPLv2 or later 8 | License URI: https://www.gnu.org/licenses/gpl-2.0.html 9 | 10 | With this free interest calculator you can compute accumulation schedules, final balances, and accrued interest. 11 | 12 | [https://www.calculator.io/interest-calculator/](https://www.calculator.io/interest-calculator/) 13 | 14 | == Usage == 15 | 16 | Add the Interest Calculator shortcode to your page, post or sidebar: 17 | 18 | `[ci_interest_calculator]` 19 | 20 | Add the following code to your template where you would like the Interest Calculator to appear: 21 | 22 | `` 23 | 24 | == Screenshots == 25 | 26 | 1. The Interest Calculator Input Form. 27 | 28 | == Installation == 29 | 30 | 1. Upload the Interest Calculator /ci_interest_calculator/ folder to the /wp-content/plugins/ directory. 31 | 2. Activate the Interest Calculator plugin through the "Plugins" menu in WordPress. 32 | 33 | == Changelog == 34 | 35 | = 1.0.0 = 36 | * Initial release of Interest Calculator 37 | --------------------------------------------------------------------------------