PageSourceSearch

https://fitfingers.github.io/portfolio.js

js fitfingers.github.io collected 2026-10-03 09:11:51 UTC 10,326 bytes, 307 lines download raw bytes

1// VARIABLE DECLARATION
2const LANDING_PAGE = document.getElementById("landing-page");
3const MOBILE_MENU_TOGGLES = document.getElementsByClassName("mobile-menu-toggle");
4const MOBILE_MENU_OVERLAY = document.getElementById("mobile-menu-overlay");
5const MOBILE_MENU_CLOSE = document.getElementById("mobile-menu-close");
6const NAVBAR_LINKS = document.getElementById("navbar-links");
7const DROP_LINK = document.getElementsByClassName("drop-down-link");
8const DROP_LINK_CONTENT = document.getElementsByClassName("drop-link-content");
9const MAIN_TABS = document.getElementsByClassName("project-tab");
10const TAB_CONTENTS = document.getElementsByClassName("tab-contents");
11const FEE_BREAKDOWN_GRID = document.getElementById("fee-breakdown-grid");
12const FEE_DETAILS = document.getElementsByClassName("fee-details");
13const CURRENCY_BUTTONS = document.getElementsByClassName("currency-select");
14const OUT_CURRENCY = document.getElementById("output-currency-select");
15const OUTPUT_CONT = document.getElementById("receive-money-output-box");
16const OUTPUT_VALUE = document.getElementById("output-value");
17const RECEIVE_TEXT = document.getElementById("receive-text");
18const INSUFF_ERROR = document.getElementById("insufficient-amount-error");
19const INSUFF_TEXT = document.getElementById("insufficient-amount-error-text");
20const ARRIVAL_TIME = document.getElementById("arrival-time");
21const CLICKABLE_OBJECTS = document.querySelectorAll("[tabindex]");
22const CASH_REGISTER = document.getElementById("cash-register");
23
24const BASE_CHARGE = 0.8;
25const MONTH = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
26const EXCHANGE_RATES = {
27  EUR: 1,
28  GBP: 0.90032,
29  USD: 1.14541,
30  INR: 80.3551
31};
32
33let isFeeDetailVisible = false;
34let isMenuVisible = false;
35let menuType = "";
36let transferSpeed = 0.005;
37let exRate = 0;
38let tomorrow = new Date();
39
40
41
42// FUNCTIONS
43
44function menuTypeQuery() {
45  menuType = window.innerWidth >= 741 ? "laptop" : "mobile";
46  adjustMenuFormat();
47  setToggleType();
48  closeDropLinks();
49}
50
51function toggleMobileMenu() {
52  isMenuVisible = !isMenuVisible;
53  adjustMenuFormat();
54}
55
56function adjustMenuFormat() {
57  if (menuType === "mobile") {
58    if (isMenuVisible === false) {
59      NAVBAR_LINKS.style.display = "none";
60      LANDING_PAGE.style.opacity = "1";
61      MOBILE_MENU_OVERLAY.style.display = "none";
62      MOBILE_MENU_OVERLAY.style.opacity = "0";
63      MOBILE_MENU_CLOSE.style.display = "none";
64    } else {
65      NAVBAR_LINKS.style.display = "inline-block";
66      LANDING_PAGE.style.opacity = "0.4";
67      MOBILE_MENU_OVERLAY.style.display = "block";
68      MOBILE_MENU_OVERLAY.style.opacity = "0.5";
69      MOBILE_MENU_CLOSE.style.display = "block";
70    }
71  } else {
72    NAVBAR_LINKS.style.display = "inline-block";
73  }
74}
75
76function openDropLink() {
77  closeDropLinks();
78  this.children.item(1).style.display = "block";
79  if (menuType === "mobile") {
80    this.style.color = "var(--logocol)";
81  }
82}
83
84function closeDropLinks() {
85  [...DROP_LINK_CONTENT].map(content => content.style.display = "none");
86  [...DROP_LINK].map(link => link.style.color =
87                     menuType === "mobile" ? "var(--darkcol)" : "white");
88}
89
90function openTab() {
91  [...MAIN_TABS].map(tab => tab.classList.remove("selected-tab"));
92  [...TAB_CONTENTS].map(content => content.style.display = "none");
93  document.getElementById(`${this.id}-content`).style.display = "block";
94  this.classList.add("selected-tab");
95};
96
97function calculateFee(val) {
98  return (BASE_CHARGE + (val - BASE_CHARGE) * transferSpeed).toFixed(2);
99}
100
101function calculateConvertValue(val) {
102  return (val - calculateFee(val)).toFixed(2);
103}
104
105function getSendCurrency() {
106  return document.getElementById("input-currency-select").value; 
107}
108
109function getReceiveCurrency() {
110  return document.getElementById("output-currency-select").value;
111}
112
113// Maybe a better method would be, on the server-side, to update an EX_RATE object every x minutes and to then resend it to the client-side in bacthes every few minutes, rather than the client changing currencies and waiting a second or two between each change. This would mean the change is almost instant (as the data is stored on their computer) and improves UX and decrease the risk of the API being slow/overloaded/broken.
114// function getExRate(value, callback) {
115//   REQ.onload = () => {
116//     if (REQ.readyState === 4 && REQ.status === 200) {
117//       callback(value, REQ.response);
118//     }
119//   }
120//   REQ.open("GET", `https://free.currencyconverterapi.com/api/v6/convert?q=${getSendCurrency()}_${getReceiveCurrency()}&compact=ultra&apiKey=4bee894984d26fd53193`, true);
121//   REQ.send();
122// }
123
124// function setExRate(value, result) {
125//   exRate = JSON.parse(result)[`${getSendCurrency()}_${getReceiveCurrency()}`].toFixed(5);
126//   renderNewValues(value);
127// }
128
129// This asynchronous function makes the workflow much easier to work with/read than the previous XHR/callback method.
130async function setExchangeRate() {
131  const CURRENCIES = `${getSendCurrency()}_${getReceiveCurrency()}`;
132  await fetch(`https://free.currencyconverterapi.com/api/v6/convert?q=${CURRENCIES}&compact=ultra&apiKey=4bee894984d26fd53193`)
133  .then((response) => response.json())
134  .then((json) => exRate = json[CURRENCIES].toFixed(5));
135  runCalculator();
136}
137
138function calculateReceiveValue(val) {
139  return (calculateConvertValue(val) * exRate).toFixed(2);
140}
141
142// Random savings of 4.7% for demo
143function calculateSavings(val) {
144  return (calculateConvertValue(val) * 0.047).toFixed(2);
145}
146
147// Update the DOM with newly-calculated values
148function renderNewValues(input) {
149  const SEND = getSendCurrency();
150  
151  if (isThereAnError(input) || isInputInvalid(input)) { return };
152
153  document.getElementById("fee-value").innerHTML =
154    `-${calculateFee(input)} ${SEND}`;
155
156  document.getElementById("amount-to-convert").innerHTML =
157    `${calculateConvertValue(input)} ${SEND}`;
158
159  document.getElementById("exchange-rate").innerHTML =
160    exRate;
161
162  document.getElementById("total-savings").innerHTML =
163    `${calculateSavings(input)} ${SEND}`;
164
165  OUTPUT_VALUE.value = calculateReceiveValue(input);
166}
167
168function isThereAnError(val) {
169  if (val <= 0.8) {
170    OUTPUT_VALUE.value = "0.00";
171    INSUFF_ERROR.style.display = "block";
172    INSUFF_TEXT.innerHTML = `Please enter an amount more than 0.01 ${getReceiveCurrency()}`;
173    OUTPUT_CONT.classList.add("cash-value-box-error");
174    OUT_CURRENCY.style.height = "70%";
175    OUT_CURRENCY.style.borderRadius = "0 3px 0 0";
176    RECEIVE_TEXT.style.color = "#C73B3B";
177    return true;
178  } else {
179    INSUFF_ERROR.style.display = "none";
180    RECEIVE_TEXT.style.color = "var(--shapecol)";
181    OUTPUT_CONT.classList.remove("cash-value-box-error");
182    OUT_CURRENCY.style.height = "100%";
183    OUT_CURRENCY.style.borderRadius = "0 3px 3px 0";
184    return false;
185  }
186}
187
188function isInputInvalid(input) {
189  if (input.match(/(?=\D)(?!\.)/) || input.match(/\.(.{3}|.*\.)/)) {
190    document.getElementById("output-value").value = "0.00";
191    return true;
192  }
193}
194
195function resetInput(val) {
196  document.getElementById("input-value").placeholder = "1,000";
197  runCalculator();
198}
199
200function runCalculator() {
201  const INPUT_VALUE = document.getElementById("input-value").value || "1000";
202  renderNewValues(INPUT_VALUE);
203  // getExRate(INPUT_VALUE, setExRate);
204}
205
206function toggleFeeBreakdown() {
207  isFeeDetailVisible = !isFeeDetailVisible;
208  showFees();
209}
210
211function showFees() {
212  if (isFeeDetailVisible === true) {
213    FEE_BREAKDOWN_GRID.style.gridTemplateRows = "30px 30px 30px";
214    [...FEE_DETAILS].map(cell => cell.style.visibility = "visible");
215  } else {
216    FEE_BREAKDOWN_GRID.style.gridTemplateRows = "30px 0px 30px";
217    [...FEE_DETAILS].map(cell => cell.style.visibility = "hidden");
218  }
219}
220
221function setArrivalTime() {
222  if (this.value === "fast-easy-transfer") {
223    transferSpeed = 0.005;
224    ARRIVAL_TIME.innerHTML = "in seconds!";
225  } else {
226    transferSpeed = 0.003;
227    tomorrow.setDate(tomorrow.getDate() + 1);
228    ARRIVAL_TIME.innerHTML = `by ${tomorrow.getDate()} ${MONTH[tomorrow.getMonth()]}`;
229  }
230  runCalculator();
231}
232
233function removeMouseFocus() {
234  [...CLICKABLE_OBJECTS].map(obj => obj.classList.remove("removeMouseFocus"));
235  this.classList.add("removeMouseFocus");
236}
237
238// Determine the correct event and assign event listener to drop links
239function setToggleType() {
240  [...DROP_LINK].map(droplink => determineToggleType(droplink));
241}
242
243// Determine whether drop links open on click(mobile) or hover(laptop)
244function determineToggleType(droplink) {
245  if (menuType === "mobile") {
246    droplink.addEventListener("click", openDropLink);
247    droplink.removeEventListener("mouseover", openDropLink);
248    droplink.removeEventListener("mouseout", closeDropLinks);
249  } else {
250    droplink.addEventListener("mouseover", openDropLink);
251    droplink.addEventListener("mouseout", closeDropLinks);
252  }
253}
254
255// function resizeiFrame(iframe) {
256//   document.getElementById(iframe).contentWindow.postMessage("i need ur height pls", "*");
257// }
258
259// function iFrameHeightPipe(event) {
260//   if (event.origin !== "https://fitfingers.github.io") {
261//     return;
262//   }
263//   document.getElementById(event.data[0]).height = parseInt(event.data[1] * 0.48);
264// }
265
266function hidePhoto() {
267  if (window.innerWidth >= 926) {
268    document.getElementById("my-story-tab-content").style.display = "none";
269  }
270}
271
272
273
274
275// EVENT LISTENERS
276
277window.addEventListener("resize", menuTypeQuery);
278window.addEventListener("resize", hidePhoto);
279// window.addEventListener("resize", () => resizeiFrame("cash-register"));
280// window.addEventListener("message", iFrameHeightPipe);
281
282[...CURRENCY_BUTTONS].map(button => button.addEventListener("change", setExchangeRate));
283
284document.getElementById("transfer-speed").addEventListener("change", setArrivalTime);
285
286document.getElementById("show-fee-button").addEventListener("click", toggleFeeBreakdown);
287
288[...MAIN_TABS].map(tab => tab.addEventListener("click", openTab));
289
290[...CLICKABLE_OBJECTS].map(obj => obj.addEventListener("click", removeMouseFocus));
291
292[...MOBILE_MENU_TOGGLES].map(function(toggle) {
293  toggle.addEventListener("click", toggleMobileMenu);
294  toggle.addEventListener("click", closeDropLinks);
295});
296
297
298
299
300// WINDOW.ONLOAD/PAGE SETUP
301window.onload = function() {
302  // getExRate(setExRate);
303  setExchangeRate();
304  resetInput();
305  menuTypeQuery();
306  // resizeiFrame("cash-register");
307}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.