├── 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 |
Hour
22 | 23 |:
25 |Minute
27 | 28 |(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 Hour : Minute : Second There was an error with your calculation. {
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 | Subnet Calculator';
20 | }
21 |
22 |
23 | add_shortcode( 'ci_subnet_calculator', 'display_calcio_ci_subnet_calculator' );
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
621 |
622 |
689 |
623 |
625 |
626 |
627 | IP SUBNET
624 |
628 |
633 | IP Address
629 | 94.204.187.191
630 | Short
631 | 94.204.187.191 /30
632 |
634 |
639 | IP Type
635 | Public
636 | Integer ID
637 | 1590475711
638 |
640 |
645 | Network Address
641 | 94.204.187.188
642 | Hex ID
643 | 0x5eccbbbf
644 |
646 |
651 | Broadcast Address
647 | 94.204.187.191
648 | in-addr.arpa
649 | 191.187.204.94.in-addr.arpa
650 |
652 |
657 | Total Number of Hosts
653 | 4
654 | IPv4 Mapped Address
655 | ::ffff:5ecc.bbbf
656 |
658 |
663 | Number of Usable Hosts
659 | 2
660 | 6to4 Prefix
661 | 2002:5ecc.bbbf::/48
662 |
664 |
669 | Subnet Mask
665 | 255.255.255.252
666 | IP Class
667 | C
668 |
670 |
675 | Wildcard Mask
671 | 0.0.0.3
672 | CIDR Notation
673 | /30
674 |
676 |
679 | Usable Host IP Range
677 | 94.204.187.189 - 94.204.187.190
678 |
680 |
683 | Binary ID
681 | 01011110110011001011101110111111
682 |
684 |
687 |
688 | Binary Subnet Mask
685 | 11111111.11111111.11111111.11111100
686 |
694 |
695 |
726 |
696 |
698 |
699 |
700 | IP SUBNET
697 |
701 |
704 | IP Address
702 | 2001:db8:85a3::8a2e:370:7334/64
703 |
705 |
708 | Full IP Address
706 | 2001:0db8:85a3:0000:0000:8a2e:0370:7334
707 |
709 |
712 | Total IP Addresses
710 | 18446744073709551616
711 |
713 |
716 | Total /64 Networks
714 |
715 |
717 |
720 | Network
718 | 2001:0db8:85a3:0000::
719 |
721 |
724 |
725 | IP Range
722 | 2001:0db8:85a3:0000:0000:0000:0000:0000 - 2001:0db8:85a3:0000:ffff:ffff:ffff:ffff
723 | 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 | 
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 |
--------------------------------------------------------------------------------