Refactor js

This commit is contained in:
Schmelczer András 2019-12-14 14:55:29 +01:00
commit 5de596c38a
28 changed files with 859 additions and 0 deletions

12
.idea/CompiledCV.iml generated Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

9
.idea/dictionaries/andras.xml generated Normal file
View file

@ -0,0 +1,9 @@
<component name="ProjectDictionaryState">
<dictionary name="andras">
<words>
<w>andrás</w>
<w>forex</w>
<w>schmelczer</w>
</words>
</dictionary>
</component>

6
.idea/misc.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>

8
.idea/modules.xml generated Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/CompiledCV.iml" filepath="$PROJECT_DIR$/.idea/CompiledCV.iml" />
</modules>
</component>
</project>

60
.idea/workspace.xml generated Normal file
View file

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="8edc47ab-1265-4111-9771-536b24cc9310" name="Default Changelist" comment="" />
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="JavaScript File" />
</list>
</option>
</component>
<component name="ProjectId" id="1UhDrRKewMzhTrQJ4npWjp729uJ" />
<component name="PropertiesComponent">
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="prettierjs.PrettierConfiguration.Package" value="/usr/local/lib/node_modules/prettier" />
</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$" />
</key>
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="8edc47ab-1265-4111-9771-536b24cc9310" name="Default Changelist" comment="" />
<created>1575800131097</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1575800131097</updated>
<workItem from="1575800132288" duration="1444000" />
<workItem from="1576164066512" duration="3000" />
<workItem from="1576250286627" duration="21557000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="1" />
</component>
</project>

3
README.md Normal file
View file

@ -0,0 +1,3 @@
# Timeline
An easily configurable portfolio.

116
content-en.json Normal file
View file

@ -0,0 +1,116 @@
{
"config": {
"showMore": "Show details",
"showLess": "Show less"
},
"header": {
"name": "András Schmelczer",
"picture": "/static/me.jpg",
"about": [
"I have always been fascinated by the engineering feats that surround us. When I realized that someday I might be able to contribute to these achievements, I knew that is what I need to aim for. As I am finishing my fifth semester at the Budapest University of Technology and Economics, I feel I am getting closer to it every day.",
"You can see some of the more interesting projects I have worked on below."
]
},
"timeline": [
{
"date": "2019 Autumn",
"title": "Predicting foreign exchange rates",
"picture": "/static/forex.gif",
"description": "From the animation we can see that my algorithm does a somewhat acceptable job at predicting (blue graph) the EUR/USD rates (green graph).",
"more": [
"In a nutshell, the algorithm (written with Python - NumPy, SciPy, Flask), extrapolates in the frequency domain. The steps are the following: smoothing the input values, differentiating, applying a short-time Fourier-transformation with overlapped (and Hanning-windowed) windows, extrapolating and then applying the inverse of these transformations to the extrapolated values.",
"Of course, there is still plenty of room for improvement, but even with this simple algorithm a mostly profitable trading strategy is viable. In my free time I may put more work into it."
]
},
{
"date": "2019 November",
"title": "My Notes",
"picture": "/static/my-notes.png",
"description": "A minimalist note organizer and editor powered by Markwon.",
"more": [
{ "type": "a", "href": "https://github.com/schmelczerandras/my-notes", "text": "MyNotes on GitHub" },
"A basic android app for creating and filtering notes written in markdown.",
"It was my homework for BME's Android and web development course. It was also my first experience with Android development."
]
},
{
"date": "2018 October - November",
"title": "Simulating the cooling system of a nuclear facility",
"picture": "/static/process-simulator.jpg",
"description": "Dynamically calculating the temperatures and flow velocities in a fluid based cooling system based on a simple model.",
"more": [
"A simulated system can contain reactors (heaters / coolers), pumps, heat exchangers, drains sources, and of course, pipes.",
"The algorithm takes advantages of graphs and matrices to get to a next time frame.",
"Python is used for the backend along with Flask and NumPy. A REST API facilitates the communication between the layers. For drawing the frontend HTML5 canvas is utilized."
]
},
{
"date": "2018 October - November",
"title": "Graph editing application",
"picture": "/static/process-simulator-input.jpg",
"description": "An intuitive editor to create and edit input files for the nuclear facility simulator.",
"more": [
"Nodes can be moved with drag&drop gestures. Editing the parameters of elements can be done on the right panel.",
"The UI is built with JavaFX. The output can be exported as JSON or directly uploaded to the simulation backend."
]
},
{
"date": "2018 July-August",
"title": "City simulation",
"picture": "/static/simulation.jpg",
"description": "Simulating a city where car crashes are more frequent than usual.",
"more": [
"Through a REST API the state of the traffic lights can be changed. The drivers follow the instructions of the traffic lights, so if a mistake is made, there will be collisions. There is also support for displaying tweets on a HUD.",
"This was created for a Cybersecurity challenge. With the help of this program tha contestants could instantly see the effect of their work.",
"The most interesting aspect of this project was building it in a server-client architecture. The decisions of the agents is calculated server-side. The real challenge was broadcasting these decisions in a fault-tolerant way using minimal bandwidth.",
"The program is made with Unity using C# as the scripting language. The models and animations were also made by me using Blender."
]
},
{
"date": "2018 June",
"title": "Photo color grader",
"picture": "/static/color.jpg",
"description": "An innovative (at least I thought so) color grader web application.",
"more": [
"The most noteworthy feature of this application is the color selector UI. This program is only intended as a proof-of-concept, I wanted to experiment with some ideas and this was the outcome. ",
"You can select some colors and then apply transformations to the other colors as a function of their distance to the selected color.",
"By clicking on a colored circle you can change its settings. New circles can be created by clicking in the large circle (and they can also be moved by drag&drop).",
{ "type": "a", "href": "color", "text": "schmelczer.dev/color" }
]
},
{
"date": "2017 autumn",
"title": "Platform game",
"picture": "/static/platform.png",
"description": "A 3D game written in C with the help of SDL 1.2 (I haven't heard of GPU programming at the time).",
"more": [
"The maps are randomly generated and fully destroyable. The player is getting chased by flying enemies. Overall, I find it a really enjoyable game.",
"I did this as a homework for my Basics of Programming course."
]
},
{
"date": "2016 summer",
"title": "Photos",
"picture": "/static/photos.jpg",
"description": "A simple web page where you can view my photos.",
"link": "schmelczer.dev/photos"
},
{
"date": "2016 spring",
"title": "Lights synchronised to music",
"picture": "/static/led.jpg",
"description": "A full stack application with a built-in music player which music controls the color of some RGB LED strips.",
"more": [
"This was my first non-trivial project which got finished. Obviously, it is rather far from perfect, but I am still proud that I was able to build it on my own.",
"The backend logic is written in Python the FFT is provided by NumPy. A quite simple frontend for accessing the music player and changing the settings also got built using vanilla web development technologies.",
"Below is a video showing the system in work.",
{ "type": "video", "src": "static/led720.mp4" }
]
}
],
"footer": {
"email": "andras.schmelczer@schdesign.hu",
"cv": "/static/andras_schmelczer_cv.pdf"
}
}

93
content-hu.json Normal file
View file

@ -0,0 +1,93 @@
{
"header": {
"name": "Schmelczer András",
"picture": "/static/me.jpg",
"about": [
"Mind a szoftverfejlesztés, mind pedig a design fontos számomra. Élvezem a problémák megoldását. Motivál, hogy hasznos és érdekes projektekben vegyek részt. Szeretek tanulni.",
"2017-ben kezdtem tanulmányaimat a Budapesti Műszaki és Gazdaságtudományi Egyetem mérnökinformatikus szakán. Azóta is minden félévemet kiváló eredménnyel zártam. Tavaly csatlakoztam a Simonyi Károly Szakkollégium schdesign köréhez. Korábban a Pécsi Tudományegyetem Gyakorló Gimnáziumába jártam, mialatt angol komplex C1-es nyelvvizsgát is szereztem.",
"Az alábbiakban összeszedtem pár izgalmasabb projektemet. A képekből természetesen csak a megoldások megjelenítés részét lehet látni. Emellett azonban igyekeztem a háttérben zajló folyamatokról is írni, hiszen az igazi kihívások általában ott rejlenek."
]
},
"timeline": [
{
"date": "2018 október - november",
"title": "Atomreaktor hűtőrendszerének szimulációja",
"picture": "/static/process-simulator.jpg",
"description": "Egy csőrendszerben lévő víz hőmérsékletének és áramlásának dinamikus számítása.",
"more": [
"A reaktorok (vízmelegítők), szivattyúk, hőcserélők adataiból és a csőrendszer felépítéséből kiszámolja az alkalmazás, hogy melyik időpillanatban hol, mennyi víz folyik, és az milyen meleg.",
"Ezt egy érdekes gráfelméleti algoritmussal, illetve egy mátrix ügyes manipulálásával éri el.",
"A szimuláció backendje python Flask-ben lett írva. Ezzel kommunikálni egy REST API-n keresztül lehet. A megjelenítés HTML5 canvas segítségével történik."
]
},
{
"date": "2018 október - november",
"title": "Gráf szerkesztő alkalmazás",
"picture": "/static/process-simulator-input.jpg",
"description": "A fentebb látható szoftverhez tartozó csőrendszert lehet vele létrehozni.",
"more": [
"A grafikus és felhasználóbarát szerkesztőprogram a végeredményt megfelelő JSON formátumba alakítja, amit a szimulátor már könnyedén fel tud dolgozni.",
"Szerkeszteni klikkeléssel, illetve drag & droppal lehetséges. Az alkalmazásban továbbá lehet a vízmelegítők, szivattyúk stb. paramétereit is beállítani.",
"Java-ban lett írva, a megjelenítést a JavaFX biztosítja."
]
},
{
"date": "2018 július - augusztus",
"title": "Közlekedés szimuláció",
"picture": "/static/sim.jpg",
"description": "A modellek Blenderben, a szimuláció Unityben készült.",
"more": [
"Egy versenyhez készült program. REST API-kon keresztül lehet a lámpák színét változtatni és a szimulációt befolyásolni, (akár még tweet-et is lehet beküldeni), az autók pedig ettől függően közlekednek, esetlegesen karamboloznak és felrobbannak.",
"Az egész érdekessége, hogy egy szerver-kliens architektúrát valósít meg, a szervezés egyszerűbbé tétele végett. Izgalmas kihívás volt a netes kommunikációból fakadó laggot kompenzálni.",
"Az összes képen látható modellt és animációt én készítettem. A scriptelés C# segítségével történt."
]
},
{
"date": "2018 június",
"title": "Színszerkesztő",
"picture": "/static/szinezo.jpg",
"description": "Egy innovatív color grader képekhez.",
"more": [
"Ki lehet választani bizonyos színeket, és a többi színt az előbbiektől lévő távolságának függvényében lehet módosítani, telitettséget, színezettségét változtatni.",
"Egyelőre proof of concept stádiumban van, viszont tervezem befejezni.",
"A színes gombokra való kattintással lehet az opciók közt váltani. Színes gombot a nagy körbe való kattintással lehet létrehozni (mozgatni pedig drag & droppal).",
{ "type": "a", "href": "/szinezo", "text": "schmelczer.hu/szinezo" }
]
},
{
"date": "2017 ősz",
"title": "Platform játék",
"picture": "/static/platform.png",
"description": "Írtam egy 3D-s játékot C-ben az SDL 1.2 segítségével.",
"more": [
"A pályák véletlenszerűen generálódnak, menthetők és rombolhatók is. A játékost repülő ellenségek üldözik.",
"Ez volt a Programozás alapjai I. tárgyhoz készített házifeladatom. Összességében egy élvezhető játék lett.",
{ "type": "a", "href": "/platform", "text": "schmelczer.hu/platform" }
]
},
{
"date": "2016 nyár",
"title": "Fényképek",
"picture": "/static/kepek.jpg",
"description": "Csináltam egy oldalt, ahol a fényképeimet lehet megnézni.",
"link": "schmelczer.hu/kepek"
},
{
"date": "2016 tavasz",
"title": "Zenére világító ledsorok",
"picture": "/static/LED.jpg",
"description": "Egy alkalmazást készítettem, amivel RGB ledsorok színét lehet a zene ritmusára változtatni.",
"more": [
"Ez volt az első nagyobb projektem, ez természetesen a megvalósítás minőségén is érezhető. Ettől független büszke vagyok a végeredményre.",
"Pythonban lett írva, amivel egy webes frontenden keresztül lehet kommunikálni. Továbbá beépítésre került egy zenelejátszó is a programba.",
"A működő rendszerről készítettem egy videót, ami alább tekinthető meg.",
{ "type": "video", "src": "static/led720.mp4" }
]
}
],
"footer": {
"email": "andras.schmelczer@schdesign.hu"
}
}

173
css/elements.css Normal file
View file

@ -0,0 +1,173 @@
/* X sign visible in the photo viewer mode. */
#exit {
position: absolute;
width: var(--exit-size);
height: var(--exit-size);
top: var(--exit-size);
right: var(--exit-size);
cursor: pointer;
}
#exit:before,
#exit:after {
content: "";
position: absolute;
width: var(--line-width);
height: calc(var(--exit-size) * 1.4142);
background: white;
border-radius: var(--border-radius);
top: calc(var(--exit-size) * -0.4142 / 2);
left: 50%;
}
#exit:before {
transform: rotate(45deg);
}
#exit:after {
transform: rotate(-45deg);
}
/**/
/* Links with interactive underline. */
a {
text-decoration: none;
position: relative;
border-bottom: solid 3px var(--light-accent-color);
cursor: pointer;
display: inline-block;
}
a:after {
content: "";
height: var(--line-width);
background-color: var(--accent-color);
position: absolute;
left: 0;
bottom: calc(-1 * var(--line-width));
width: 0;
transition: width var(--transition-time);
}
a:hover:after {
width: 100%;
}
/**/
/* Line with circle for the timeline sections. */
.line {
margin-left: calc(var(--dot-size) / 2);
margin-right: var(--line-height);
border-left: var(--line-width) solid var(--text-color);
position: relative;
}
.line:before {
content: "";
position: absolute;
left: calc(-1 / 2 * var(--dot-size) - 4px);
background: var(--bg-color);
top: 33%;
width: var(--dot-size);
height: var(--dot-size);
border-radius: 100%;
border: var(--line-width) var(--text-color) solid;
}
/**/
/* Activity cards. */
.card {
border-radius: var(--border-radius);
text-align: center;
padding: var(--margin);
box-shadow: 0 0 5px rgba(0, 0, 0, 0.125);
transition: box-shadow;
transition-duration: var(--transition-time);
background: var(--card-color);
}
.card:hover {
box-shadow: 0 0 3px rgba(0, 0, 0, 0.05);
}
/**/
/* Dates related to the lines and cards. */
.date-narrow-screen,
.date-wide-screen {
font: 400 1em "Open sans", sans-serif;
}
.date-narrow-screen {
display: none;
margin: 0;
margin-top: calc(var(--line-height) / 2.25) !important;
color: var(--light-text-color);
}
.date-wide-screen {
position: relative;
top: calc(33% + var(--dot-size) + 1ch);
margin: 0 var(--margin) 0 calc(var(--line-width) + 1ex);
width: 100px;
}
/**/
/* The photo viewer */
#photo-viewer {
width: 100%;
height: 100vh;
position: fixed;
top: 0;
left: 0;
background: var(--photo-viewer-color);
z-index: -3;
opacity: 0;
transition: opacity var(--transition-time);
}
/* #photo */
#photo-viewer > img {
max-width: 80vw;
max-height: 80vh;
}
/**/
/* Scrollbar. */
body::-webkit-scrollbar-track,
body::-webkit-scrollbar {
background-color: var(--scroll-color);
width: 12px;
}
body::-webkit-scrollbar-thumb {
background-color: var(--accent-color);
border-radius: var(--border-radius);
}
/**/
/* Selections. */
::-moz-selection {
background: var(--accent-color);
color: white;
}
::selection {
background: var(--accent-color);
color: white;
}
/**/
/* Absolute centering parent. */
.center {
display: flex;
align-items: center;
justify-content: center;
}
/**/
@media (max-width: 900px) {
.line {
display: none;
}
/* Disable animation. */
.card:hover {
box-shadow: 0 0 5px rgba(0, 0, 0, 0.125);
}
.date-narrow-screen {
display: block;
}
.date-wide-screen {
display: none;
}
}

28
css/main.css Normal file
View file

@ -0,0 +1,28 @@
@import url('https://fonts.googleapis.com/css?family=Open+Sans:400,400i|Raleway&subset=latin-ext');
:root {
--photo-viewer-color: rgba(0, 0, 0, 0.75);
--accent-color: #5264bf;
--light-accent-color: #e5e5ff;
--scroll-color: #ffd6d6;
--bg-color:linear-gradient(90deg, #fff9e0 0, #ffd6d6 100%);
--card-color: white;
--text-color: #31343f;
--light-text-color: #7a7d8e;
--dot-size: 25px;
--line-width: 3px;
--exit-size: 25px;
--line-height: 15px;
--smaller-margin: 25px;
--margin: 35px;
--border-radius: 5px;
--transition-time: 200ms;
--width: 765px;
}
@media (max-width: 900px) {
:root {
--exit-size: 20px;
--margin: 25px;
--smaller-margin: 20px;
}
}

131
css/page.css Normal file
View file

@ -0,0 +1,131 @@
* {
margin: 0;
box-sizing: content-box;
color: var(--text-color);
}
html {
background-color: #31343f;
background: var(--bg-color);
}
body {
width: var(--width);
font-size: 0.9em;
margin: auto;
visibility: hidden;
}
header,
footer,
#timeline,
#about > p:first-of-type,
#timeline > section:not(:first-of-type) > .card {
margin-top: var(--margin);
}
#header-pic,
h1 {
font: 400 3.33em "Raleway", sans-serif;
text-align: center;
}
#header-pic {
height: 4ch;
border-radius: 100%;
margin-right: 1.5ex;
}
p,
a {
font: 400 1.25em "Open sans", sans-serif;
}
#about > p {
text-align: justify;
hyphens: auto;
}
#timeline > section {
display: flex;
}
.card {
flex: 1;
}
#about > p,
.card > *:not(:first-child):not(:last-child),
.collapsed > *:not(:first-child) {
margin-top: var(--line-height);
}
h2 {
font: 400 2em "Raleway", sans-serif;
}
.card img,
video {
width: 100%;
}
img {
user-select: none;
border-radius: var(--border-radius);
cursor: pointer;
}
.description {
font-style: italic;
margin-bottom: var(--smaller-margin);
}
.collapsed > p {
text-align: left;
}
.collapsed {
height: 0;
overflow: hidden;
transition: height var(--transition-time);
}
.collapsed > *:last-child {
margin-bottom: var(--smaller-margin);
}
footer {
margin-bottom: var(--margin);
}
@media (max-width: 900px) {
body {
width: 85%;
font-size: 0.85em;
}
#photo {
max-width: 94vw;
}
.card {
font-size: 0.9em;
}
header {
flex-direction: column;
}
h1,
#header-pic {
font-size: 3em;
}
#header-pic {
height: 5.5ch;
margin: 0.75ex 0 0.5ex 0;
}
#about > p {
text-align: left;
}
}

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

48
index.html Normal file
View file

@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta property="og:image:width" content="1744" />
<meta property="og:image:height" content="913" />
<meta
property="og:description"
content="This is my portfolio where you can see my previous projects on a timeline."
/>
<meta property="og:title" content="Andr&aacute;s Schmelczer" />
<meta property="og:url" content="https://schmelczer.dev/" />
<meta
property="og:image"
content="https://schmelczer.dev/static/og-image.jpg"
/>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="theme-color" content="#31343F" />
<title>Portfolio</title>
<link rel="stylesheet" href="css/main.css" />
<link rel="stylesheet" href="css/elements.css" />
<link rel="stylesheet" href="css/page.css" />
</head>
<body>
<section class="center" id="photo-viewer">
<img id="photo" alt="currently opened photo" src=""/>
<div id="exit"></div>
</section>
<header class="center">
<img id="header-pic" alt="a picture of me" src=""/>
<h1 id="name"></h1>
</header>
<section id="about"></section>
<main id="timeline"></main>
<footer class="card center"><a id="email"></a></footer>
</body>
<script src="js/content.js"></script>
<script src="js/main.js"></script>
</html>

157
js/content.js Normal file
View file

@ -0,0 +1,157 @@
const createPageFactory = ({
nameId,
pictureId,
aboutId,
timelineId,
emailId,
photoId,
photoViewerId,
}) => {
const createPage = async (src) => {
const {config, header, timeline, footer} = await getData(src);
processHeader(header);
processTimeline(timeline, config);
processFooter(footer);
setupGlobals(config);
};
const processHeader = ({name, picture, about}) => {
document.title = name;
getElement(nameId).textContent = name;
getElement(pictureId).src = picture;
getElement(pictureId).onclick = () => showPhoto(picture);
getElement(aboutId).innerHTML = listToHtml(about);
};
const listToHtml = list => list.map(element => {
if (!element.type || element.type === 'p') {
return `<p>${element}</p>`;
} else if (element.type === 'a') {
return `<a href="${element.href}" target="_blank"> ${element.text} </a>`;
} else if (element.type === 'video') {
return `<video controls><source src="${element.src}" /></video>`;
} else return '';
}).join('\n');
const processTimeline = (timeline, {showMore}) => {
getElement(timelineId).innerHTML = timeline.map(
element => timelineElementToHTML(element, createId(), showMore)
).join('\n');
};
const timelineElementToHTML = ({date, title, picture, description, more, link}, id, showMore) => `
<section>
<div class="line">
${date ? `<p class="date-wide-screen">${date}</p>` : ''}
</div>
<div class="card">
<h2>${title}</h2>
${date ? `<p class="date-narrow-screen">${date}</p>` : ''}
${picture ? `<img src="${picture}" onclick="showPhoto('${picture}');" alt="${picture}"/>` : ''}
${description ? `<p class="description">${description}</p>` : ''}
${more ? `
<div class="collapsed" id="${idToActivityId(id)}">
${listToHtml(more)}
</div>
<a id="${idToButtonId(id)}" onclick="toggleLongDescription(${id})">
${showMore}
</a>
`
: (link ? `<a href="http://${link}" target="_blank">${link}</a>` : '')}
</div>
</section>
`;
const processFooter = ({email}) => {
getElement(emailId).href = `mailto:${email}`;
getElement(emailId).textContent = email;
};
const hideFrame = () => {
getElement(photoViewerId).style['z-index'] = -1;
getElement(photoViewerId).style.opacity = '0';
};
const showPhoto = src => {
getElement(photoId).src = src;
getElement(photoViewerId).style['z-index'] = 1000;
getElement(photoViewerId).style.opacity = '1';
};
const setupGlobals = config => {
window.toggleLongDescription = toggleLongDescriptionFactory(config);
window.showPhoto = showPhoto;
window.hideFrame = hideFrame;
getElement(photoViewerId).addEventListener('click', hideFrame);
window.addEventListener('resize', onResize);
document.body.addEventListener('keydown', handleEscape);
};
const toggleLongDescriptionFactory = ({showMore, showLess}) => (id) => {
const button = getElement(idToButtonId(id));
const element = getElement(idToActivityId(id));
if (isClosed(element)) {
open(element);
button.textContent = showLess;
} else {
close(element);
button.textContent = showMore;
}
};
const onResize = () => {
const elements = document.getElementsByClassName('collapsed');
Array.prototype.forEach.call(elements, element => {
if (isOpen(element)) {
element.style.height = 'auto';
setTimeout(() => open(element), 100);
}
});
};
const isClosed = element => ['0px', '0', 0, ''].includes(element.style.height);
const isOpen = element => !isClosed(element);
const close = element => element.style.height = '0';
const open = element => element.style.height = `${element.scrollHeight}px`;
const handleEscape = event => {
if (event.key === 'Escape') {
hideFrame();
}
};
const getElementFactory = () => {
const foundElements = {};
return id => {
if (!(id in foundElements)) {
foundElements[id] = document.getElementById(id);
}
return foundElements[id];
}
};
const getElement = getElementFactory();
const getData = async (src) => await (await fetch(src, {
method: 'GET',
mode: 'cors',
cache: 'no-cache'
})).json();
const createIdFactory = () => {
let id = 0;
return () => id++;
};
const createId = createIdFactory();
const idToButtonId = (id) => `button_${id}`;
const idToActivityId = (id) => `activity_${id}`;
return createPage;
};

15
js/main.js Normal file
View file

@ -0,0 +1,15 @@
(async () => {
const src = 'content-en.json';
const ids = {
pictureId: 'header-pic',
nameId: 'name',
aboutId: 'about',
timelineId: 'timeline',
emailId: 'email',
photoViewerId: 'photo-viewer',
photoId: 'photo'
};
await createPageFactory(ids)(src);
document.body.style.visibility = 'visible';
})();

BIN
static/avoid.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
static/color.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 KiB

BIN
static/forex.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 KiB

BIN
static/led.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

BIN
static/led720.mp4 Normal file

Binary file not shown.

BIN
static/me.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
static/my-notes.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 MiB

BIN
static/og-image.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

BIN
static/photos.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

BIN
static/platform.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

BIN
static/simulation.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 KiB