├── 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-1280x640.png │ ├── banner-1544x500.png │ ├── banner-772x250.png │ ├── favicon.ico │ ├── icon-128.png │ ├── icon-128x128.png │ ├── icon-16.png │ ├── icon-192.png │ ├── icon-196.png │ ├── icon-256.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-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_subnet_calculator.php ├── index.html ├── plugin.php ├── readme.md └── readme.txt /assets/fonts/inter-v12-latin-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/fonts/inter-v12-latin-500.woff2 -------------------------------------------------------------------------------- /assets/fonts/inter-v12-latin-600.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/fonts/inter-v12-latin-600.woff2 -------------------------------------------------------------------------------- /assets/fonts/inter-v12-latin-800.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/fonts/inter-v12-latin-800.woff2 -------------------------------------------------------------------------------- /assets/fonts/inter-v12-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/fonts/inter-v12-latin-regular.woff2 -------------------------------------------------------------------------------- /assets/images/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/apple-touch-icon.png -------------------------------------------------------------------------------- /assets/images/banner-1280x640.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/banner-1280x640.png -------------------------------------------------------------------------------- /assets/images/banner-1544x500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/banner-1544x500.png -------------------------------------------------------------------------------- /assets/images/banner-772x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/banner-772x250.png -------------------------------------------------------------------------------- /assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/favicon.ico -------------------------------------------------------------------------------- /assets/images/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/icon-128.png -------------------------------------------------------------------------------- /assets/images/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/icon-128x128.png -------------------------------------------------------------------------------- /assets/images/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/icon-16.png -------------------------------------------------------------------------------- /assets/images/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/icon-192.png -------------------------------------------------------------------------------- /assets/images/icon-196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/icon-196.png -------------------------------------------------------------------------------- /assets/images/icon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/icon-256.png -------------------------------------------------------------------------------- /assets/images/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/icon-256x256.png -------------------------------------------------------------------------------- /assets/images/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/icon-32.png -------------------------------------------------------------------------------- /assets/images/icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/icon-48.png -------------------------------------------------------------------------------- /assets/images/icon-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/icon-96.png -------------------------------------------------------------------------------- /assets/images/icon-og.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/icon-og.png -------------------------------------------------------------------------------- /assets/images/icon-rounded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/icon-rounded.png -------------------------------------------------------------------------------- /assets/images/icon-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/icon-square.png -------------------------------------------------------------------------------- /assets/images/illustration-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/illustration-1.png -------------------------------------------------------------------------------- /assets/images/illustration-els.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/illustration-els.png -------------------------------------------------------------------------------- /assets/images/illustration-og.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/illustration-og.png -------------------------------------------------------------------------------- /assets/images/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/assets/images/screenshot-1.png -------------------------------------------------------------------------------- /assets/images/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pub-calculator-io/subnet-calculator/56182de3ea1d49e7446b2b4cbe27f6dfa9c59f79/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 ipv = $('[data-tab].tab--active').dataset.tab == '0' ? 'v4' : 'v6'; 3 | switch(ipv){ 4 | case 'v4':{ 5 | // 1. init & validate 6 | const subnet = input.get('subnet').raw().split(' ')[1].slice(1); 7 | const ipv4_address = input.get('ipv4_address').raw(); 8 | const segments = ipv4_address.split('.'); 9 | const ipv4_address_valid = 10 | segments.length == 4 && segments.every(segment => { 11 | return Number(segment) <= 255 && RegExp(/^(0|[1-9]\d*)$/).test(segment); 12 | }); 13 | if(!ipv4_address_valid){ 14 | input.error('ipv4_address', 'Provide a valid IPv4 address: a.b.c.d, where 0 <= a|b|c|d <= 255.'); 15 | } 16 | if(!input.valid()) return; 17 | 18 | // 2. calculate 19 | const ipAddress = ipv4_address; 20 | const mask = 0xffffffff << (32 - subnet); 21 | const applyMask = (address, mask, mode = 'AND', format = 10) => { 22 | const segments = address.split('.'); 23 | return segments.map((segment, index) => { 24 | const segmentMask = mask >> (8 * (3 - index)) & 0xff; 25 | const result = Number(mode == 'AND' ? segmentMask & segment : segmentMask | segment) 26 | .toString(format); 27 | return (format == 2 ? '0'.repeat(8-result.length) : '') + result; 28 | }).join('.'); 29 | } 30 | const networkAddress = applyMask(ipAddress, mask); 31 | const hexId = ipAddress.split('.').map(segment => { 32 | segment = Number(segment).toString(16); 33 | return '0'.repeat(2-segment.length) + segment; 34 | }).join(''); 35 | const binId = '0'.repeat(32-Number('0x'+hexId).toString(2).length)+Number('0x'+hexId).toString(2); 36 | const integerId = Number('0x'+hexId); 37 | const broadcastAddress = applyMask(networkAddress, ~mask, 'OR'); 38 | const shortAddress = `${ipAddress} /${subnet}`; 39 | const arpaAddress = `${ipAddress.split('.').reverse().join('.')}.in-addr.arpa`; 40 | const totalHosts = math.pow(2, 32 - subnet); 41 | const usableHosts = subnet <= 31 ? math.pow(2, 32 - subnet) - 2 : 0; 42 | const mappedAddress = `::ffff:${hexId.slice(0,4)}.${hexId.slice(4)}`; 43 | const _6to4prefix = `2002:${hexId.slice(0,4)}.${hexId.slice(4)}::/48`; 44 | const subnetMask = applyMask('255.255.255.255',mask); 45 | const ipClass = subnet >= 24 ? 'C' : (subnet >= 16 ? 'B' : (subnet >= 8 ? 'A' : 'Any')); 46 | const wildcardMask = applyMask('255.255.255.255',~mask); 47 | const cidrNotation = `/${subnet}`; 48 | const usableHostRange = subnet <= 30 ? 49 | applyMask(networkAddress, 1, 'OR') + ' - ' + applyMask(networkAddress, ~(mask | 1), 'OR') : 'NA'; 50 | const binaryMask = applyMask('255.255.255.255',mask,'AND',2); 51 | 52 | // 3. output 53 | _('ipv4_ip_address').innerHTML = ipAddress; 54 | _('ipv4_short_address').innerHTML = shortAddress; 55 | _('ipv4_type').innerHTML = 'Public'; 56 | _('ipv4_hex_id').innerHTML = '0x' + hexId; 57 | _('ipv4_integer_id').innerHTML = integerId; 58 | _('ipv4_net_address').innerHTML = networkAddress; 59 | _('ipv4_broadcast_address').innerHTML = broadcastAddress; 60 | _('ipv4_arpa_address').innerHTML = arpaAddress; 61 | _('ipv4_total_hosts').innerHTML = totalHosts; 62 | _('ipv4_mapped_address').innerHTML = mappedAddress; 63 | _('ipv4_usable_hosts').innerHTML = usableHosts; 64 | _('ipv4_6to4_prefix').innerHTML = _6to4prefix; 65 | _('ipv4_subnet_mask').innerHTML = subnetMask; 66 | _('ipv4_ip_class').innerHTML = ipClass; 67 | _('ipv4_wildcard_mask').innerHTML = wildcardMask; 68 | _('ipv4_cidr_notation').innerHTML = cidrNotation; 69 | _('ipv4_usable_host_range').innerHTML = usableHostRange; 70 | _('ipv4_binary_id').innerHTML = binId; 71 | _('ipv4_binary_mask').innerHTML = binaryMask; 72 | }break; 73 | 74 | case 'v6':{ 75 | // 1. init & validate 76 | const prefix_length = input.get('prefix_length').raw().slice(1); 77 | const ipv6_address = input.get('ipv6_address').raw(); 78 | const segments = ipv6_address.replace('::',':').split(':'); 79 | const ipv6_address_valid = 80 | ( 81 | (ipv6_address.match(/::/g)||[]).length == 1 ? segments.length >= 2 && segments.length <= 8 : segments.length == 8 82 | ) && 83 | segments.every(segment => { 84 | return segment == '' || RegExp(/^[0-9a-fA-F]{1,4}$/).test(segment) 85 | }); 86 | if(!ipv6_address_valid){ 87 | input.error('ipv6_address', 'Provide a valid IPv6 address: a:b:c:d:e:f:g:h or shortened format using :: for omitting zero-segments (e.g. a::h), where 0 <= a|b|c|d|e|f|g|h <= ffff.'); 88 | } 89 | if(!input.valid()) return; 90 | 91 | // 2. calculate 92 | const ipAddress = `${ipv6_address}/${prefix_length}`; 93 | const fullIpAddress = ipv6_address 94 | .replace('::', 95 | (ipv6_address[0] == ':' ? '0:' : ':') + 96 | Array(8 - ipv6_address.replace('::',':').split(':').length).fill('0').join(':') + 97 | (ipv6_address[ipv6_address.length-1] == ':' ? ':0' : ':') 98 | ) 99 | .split(':').map(segment => '0'.repeat(4 - segment.length) + segment).join(':') 100 | ; // aaaa:bbbb:cccc:dddd:eeee:ffff:gggg:hhhh 101 | const mask = '1'.repeat(prefix_length) + '0'.repeat(128-prefix_length); 102 | const applyMask = (address, mask, mode = 'AND') => { 103 | return address.split(':') 104 | .map((segment, index) => { 105 | const segmentMask = Number('0b'+mask.slice(16*index, 16*(index+1))); 106 | const result = (mode == 'AND' ? Number('0x'+segment) & segmentMask : Number('0x'+segment) | segmentMask).toString(16); 107 | return '0'.repeat(4-result.length)+result; 108 | }).join(':'); 109 | } 110 | const networkAddress = applyMask(fullIpAddress, mask).split(':') 111 | .slice(0, math.ceil(prefix_length / 16)).join(':'); 112 | const network = networkAddress + (networkAddress.split(':').length < 8 ? '::' : ''); 113 | const total = (power) => math.pow(2,math.bignumber(power)).toFixed(); 114 | const totalAddresses = total(128-prefix_length); 115 | const totalNetworks = prefix_length < 64 ? total(64-prefix_length) : ''; 116 | const ipRange = applyMask(fullIpAddress, mask) + ' - ' + 117 | applyMask(fullIpAddress, '0'.repeat(prefix_length) + '1'.repeat(128-prefix_length), 'OR'); 118 | 119 | // 3. output 120 | _('ipv6_ip_address').innerHTML = ipAddress; 121 | _('ipv6_full_ip_address').innerHTML = fullIpAddress; 122 | _('ipv6_total_addresses').innerHTML = totalAddresses; 123 | _('ipv6_total_networks').innerHTML = totalNetworks; 124 | _('ipv6_network').innerHTML = network; 125 | _('ipv6_ip_range').innerHTML = ipRange; 126 | }break; 127 | } 128 | } 129 | 130 | _('network_class').onchange = networkClass => { 131 | const subnetsCount = { 132 | any: 32, a: 25, b: 17, c: 9 133 | }[networkClass]; 134 | 135 | const subnets = Array(subnetsCount).fill('').map((item, index)=>{ 136 | const value = 0xffffffff << index; 137 | const label = `${value >> 24 & 0xff}.${value >> 16 & 0xff}.${value >> 8 & 0xff}.${value & 0xff} /${32 - index}`; 138 | return ``; 139 | }).join(""); 140 | 141 | _('subnet').innerHTML = subnets; 142 | _('subnet').parentNode.parentNode.querySelector('div.input-hints span').innerText = "Subnets count: " + subnetsCount; 143 | } 144 | -------------------------------------------------------------------------------- /assets/js/chart.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | let theme = 'light'; 4 | if (localStorage.getItem('theme') === 'dark' || (localStorage.getItem('theme') === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches)) theme = 'dark'; 5 | 6 | Chart.defaults.borderColor = '#000'; 7 | 8 | const colors = { 9 | light: { 10 | purple: '#A78BFA', 11 | yellow: '#FBBF24', 12 | sky: '#7DD3FC', 13 | blue: '#1D4ED8', 14 | textColor: '#6B7280', 15 | yellowGradientStart: 'rgba(250, 219, 139, 0.33)', 16 | purpleGradientStart: 'rgba(104, 56, 248, 0.16)', 17 | skyGradientStart: 'rgba(56, 187, 248, 0.16)', 18 | tealGradientStart: 'rgba(56, 248, 222, 0.16)', 19 | yellowGradientStop: 'rgba(250, 219, 139, 0)', 20 | purpleGradientStop: 'rgba(104, 56, 248, 0)', 21 | gridColor: '#DBEAFE', 22 | tooltipBackground: '#fff', 23 | fractionColor: '#EDE9FE', 24 | }, 25 | dark: { 26 | purple: '#7C3AED', 27 | yellow: '#D97706', 28 | sky: '#0284C7', 29 | blue: '#101E47', 30 | textColor: '#fff', 31 | yellowGradientStart: 'rgba(146, 123, 67, 0.23)', 32 | purpleGradientStart: 'rgba(78, 55, 144, 0.11)', 33 | skyGradientStart: 'rgba(56, 187, 248, 0.16)', 34 | tealGradientStart: 'rgba(56, 248, 222, 0.16)', 35 | yellowGradientStop: 'rgba(250, 219, 139, 0)', 36 | purpleGradientStop: 'rgba(104, 56, 248, 0)', 37 | gridColor: '#162B64', 38 | tooltipBackground: '#1C3782', 39 | fractionColor: '#41467D', 40 | }, 41 | }; 42 | 43 | class Custom extends LineController { 44 | draw() { 45 | super.draw(arguments); 46 | 47 | const ctx = this.chart.ctx; 48 | let _stroke = ctx.stroke; 49 | 50 | ctx.stroke = function () { 51 | ctx.save(); 52 | ctx.shadowColor = 'black'; 53 | ctx.shadowBlur = 20; 54 | ctx.shadowOffsetX = 0; 55 | ctx.shadowOffsetY = 20; 56 | _stroke.apply(this, arguments); 57 | ctx.restore(); 58 | }; 59 | } 60 | } 61 | 62 | Custom.id = 'shadowLine'; 63 | Custom.defaults = LineController.defaults; 64 | 65 | Chart.register(Custom); 66 | 67 | let switchTheme = [] 68 | 69 | if (document.getElementById('chartC150')) { 70 | let ctx = document.getElementById('chartC150').getContext('2d'); 71 | 72 | let purpleGradient = ctx.createLinearGradient(0, 0, 2048, 0); 73 | purpleGradient.addColorStop(0, 'rgba(152, 96, 250, 0)'); 74 | purpleGradient.addColorStop(1, 'rgba(152, 96, 250, .8)'); 75 | 76 | const dataCharts = { 77 | labels: [0, 1, 2, 3, 4, 5, 6], 78 | datasets: [ 79 | { 80 | label: 'D1', 81 | data: [{ x: 4, y: 40 }], 82 | backgroundColor: '#7C3AED', 83 | borderWidth: 0, 84 | radius: 6, 85 | hoverRadius: 6, 86 | type: 'scatter', 87 | stacked: true, 88 | order: 0, 89 | }, 90 | { 91 | label: 'D2', 92 | data: [0, 10, 20, 40, 60, 80, 100], 93 | borderColor: colors[theme].purple, 94 | type: 'line', 95 | fill: '-1', 96 | order: 1, 97 | pointHoverRadius: 0, 98 | }, 99 | { 100 | label: 'D3', 101 | data: [0, 5, 5, 15, 25, 35, 45], 102 | borderColor: colors[theme].purple, 103 | backgroundColor: purpleGradient, 104 | type: 'line', 105 | fill: '-1', 106 | order: 1, 107 | pointHoverRadius: 0, 108 | }, 109 | ], 110 | }; 111 | 112 | let chart = new Chart(document.getElementById('chartC150'), { 113 | data: dataCharts, 114 | options: { 115 | stepSize: 1, 116 | response: true, 117 | elements: { 118 | point: { 119 | radius: 0, 120 | }, 121 | }, 122 | plugins: { 123 | legend: { 124 | display: false, 125 | }, 126 | tooltip: false, 127 | }, 128 | interaction: { 129 | mode: 'index', 130 | intersect: false, 131 | }, 132 | scales: { 133 | y: { 134 | max: 100, 135 | grid: { 136 | tickLength: 0, 137 | color: colors[theme].gridColor, 138 | }, 139 | ticks: { 140 | display: false, 141 | stepSize: 25, 142 | }, 143 | border: { 144 | color: colors[theme].gridColor, 145 | }, 146 | }, 147 | x: { 148 | stacked: false, 149 | border: { 150 | color: colors[theme].gridColor, 151 | }, 152 | ticks: { 153 | display: false, 154 | color: colors[theme].gridColor, 155 | stepSize: 1, 156 | }, 157 | grid: { 158 | tickLength: 0, 159 | color: colors[theme].gridColor, 160 | }, 161 | }, 162 | }, 163 | }, 164 | }); 165 | 166 | let switchThemeChartC150 = function(theme) { 167 | let y = chart.config.options.scales.y 168 | let x = chart.config.options.scales.x 169 | let data = chart.config.data 170 | y.grid.color = colors[theme].gridColor; 171 | y.border.color = colors[theme].gridColor; 172 | x.border.color = colors[theme].gridColor; 173 | x.grid.color = colors[theme].gridColor; 174 | x.ticks.color = colors[theme].gridColor; 175 | data.datasets[1].borderColor = colors[theme].purple; 176 | data.datasets[2].borderColor = colors[theme].purple; 177 | chart.update() 178 | } 179 | 180 | switchTheme.push(switchThemeChartC150) 181 | 182 | } -------------------------------------------------------------------------------- /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_subnet_calculator.php: -------------------------------------------------------------------------------- 1 | Subnet Calculator
'; 20 | } 21 | 22 | 23 | add_shortcode( 'ci_subnet_calculator', 'display_calcio_ci_subnet_calculator' ); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Subnet Calculator 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 |
35 | 42 | 48 | 54 | 60 |
61 |
62 |
170 |
171 |
179 |
180 |
181 |
182 |
575 |
576 |
587 |
588 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 |
604 |
605 |
606 |
607 |
608 |
609 | 610 | 614 | 615 |

There was an error with your calculation.

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 |
IP SUBNET
IP Address94.204.187.191Short94.204.187.191 /30
IP TypePublicInteger ID1590475711
Network Address94.204.187.188Hex ID0x5eccbbbf
Broadcast Address94.204.187.191in-addr.arpa191.187.204.94.in-addr.arpa
Total Number of Hosts4IPv4 Mapped Address::ffff:5ecc.bbbf
Number of Usable Hosts26to4 Prefix2002:5ecc.bbbf::/48
Subnet Mask255.255.255.252IP ClassC
Wildcard Mask0.0.0.3CIDR Notation/30
Usable Host IP Range94.204.187.189 - 94.204.187.190
Binary ID01011110110011001011101110111111
Binary Subnet Mask11111111.11111111.11111111.11111100
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 |
IP SUBNET
IP Address2001:db8:85a3::8a2e:370:7334/64
Full IP Address2001:0db8:85a3:0000:0000:8a2e:0370:7334
Total IP Addresses18446744073709551616
Total /64 Networks
Network2001:0db8:85a3:0000::
IP Range2001:0db8:85a3:0000:0000:0000:0000:0000 - 2001:0db8:85a3:0000:ffff:ffff:ffff:ffff
726 |
727 |
728 |
729 |
730 |
731 | 732 | 733 | 734 | 735 | 736 | -------------------------------------------------------------------------------- /plugin.php: -------------------------------------------------------------------------------- 1 | Subnet Calculator
'; 20 | } 21 | 22 | add_shortcode( 'ci_subnet_calculator', 'display_ci_subnet_calculator' ); -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Subnet Calculator Widget for WordPress 2 | 3 | This IP subnet calculator makes subnetting easy, providing full subnet information like IP ranges, subnet masks and more from just basic network information. 4 | 5 | ![Subnet Calculator Input Form](/assets/images/screenshot-1.png "Subnet Calculator Input Form") 6 | 7 | ## Installation 8 | 9 | 1. [Download](https://github.com/pub-calculator-io/subnet-calculator/archive/refs/heads/master.zip) the ZIP file of this repository. 10 | 2. Upload the /subnet-calculator-master/ folder to the /wp-content/plugins/ directory. 11 | 3. Activate the [Subnet Calculator](https://www.calculator.io/subnet-calculator/ "Subnet Calculator Homepage") plugin through the "Plugins" menu in WordPress. 12 | 13 | ## Usage 14 | * Add the shortcode `[ci_subnet_calculator]` to your page, post or sidebar. 15 | * Or add the following code: `` to your template where you would like the Subnet 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 Subnet calculator === 2 | Contributors: calculatorio 3 | Tags: ipv4 subnetting, ipv6 subnets, network addresses, usable host ranges, subnet mask, ip class, tcp/ip, subnetting, classless inter-domain routing (cidr), network mask 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 | This IP subnet calculator makes subnetting easy, providing full subnet information like IP ranges, subnet masks and more from just basic network information. 11 | 12 | [https://www.calculator.io/subnet-calculator/](https://www.calculator.io/subnet-calculator/) 13 | 14 | == Usage == 15 | 16 | Add the Subnet Calculator shortcode to your page, post or sidebar: 17 | 18 | `[ci_subnet_calculator]` 19 | 20 | Add the following code to your template where you would like the Subnet Calculator to appear: 21 | 22 | `` 23 | 24 | == Screenshots == 25 | 26 | 1. The Subnet Calculator Input Form. 27 | 28 | == Installation == 29 | 30 | 1. Upload the Subnet Calculator /ci_subnet_calculator/ folder to the /wp-content/plugins/ directory. 31 | 2. Activate the Subnet Calculator plugin through the "Plugins" menu in WordPress. 32 | 33 | == Changelog == 34 | 35 | = 1.0.0 = 36 | * Initial release of Subnet Calculator 37 | --------------------------------------------------------------------------------