Add webpack support
4
.gitignore
vendored
|
|
@ -1 +1,3 @@
|
||||||
# Created by .ignore support plugin (hsz.mobi)
|
node_modules
|
||||||
|
dist
|
||||||
|
package-lock.json
|
||||||
|
|
|
||||||
45
.idea/watcherTasks.xml
generated
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectTasksOptions" suppressed-tasks="SCSS">
|
||||||
|
<TaskOptions isEnabled="true">
|
||||||
|
<option name="arguments" value="--write $FilePathRelativeToProjectRoot$" />
|
||||||
|
<option name="checkSyntaxErrors" value="true" />
|
||||||
|
<option name="description" />
|
||||||
|
<option name="exitCodeBehavior" value="ERROR" />
|
||||||
|
<option name="fileExtension" value="js" />
|
||||||
|
<option name="immediateSync" value="false" />
|
||||||
|
<option name="name" value="Prettier" />
|
||||||
|
<option name="output" value="$FilePathRelativeToProjectRoot$" />
|
||||||
|
<option name="outputFilters">
|
||||||
|
<array />
|
||||||
|
</option>
|
||||||
|
<option name="outputFromStdout" value="false" />
|
||||||
|
<option name="program" value="$ProjectFileDir$/node_modules/.bin/prettier" />
|
||||||
|
<option name="runOnExternalChanges" value="true" />
|
||||||
|
<option name="scopeName" value="Project Files" />
|
||||||
|
<option name="trackOnlyRoot" value="false" />
|
||||||
|
<option name="workingDir" value="$ProjectFileDir$" />
|
||||||
|
<envs />
|
||||||
|
</TaskOptions>
|
||||||
|
<TaskOptions isEnabled="true">
|
||||||
|
<option name="arguments" value="--write $FilePathRelativeToProjectRoot$" />
|
||||||
|
<option name="checkSyntaxErrors" value="true" />
|
||||||
|
<option name="description" />
|
||||||
|
<option name="exitCodeBehavior" value="ERROR" />
|
||||||
|
<option name="fileExtension" value="xhtml" />
|
||||||
|
<option name="immediateSync" value="false" />
|
||||||
|
<option name="name" value="Prettier" />
|
||||||
|
<option name="output" value="$FilePathRelativeToProjectRoot$" />
|
||||||
|
<option name="outputFilters">
|
||||||
|
<array />
|
||||||
|
</option>
|
||||||
|
<option name="outputFromStdout" value="false" />
|
||||||
|
<option name="program" value="$ProjectFileDir$/node_modules/.bin/prettier" />
|
||||||
|
<option name="runOnExternalChanges" value="true" />
|
||||||
|
<option name="scopeName" value="Project Files" />
|
||||||
|
<option name="trackOnlyRoot" value="false" />
|
||||||
|
<option name="workingDir" value="$ProjectFileDir$" />
|
||||||
|
<envs />
|
||||||
|
</TaskOptions>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
45
.idea/workspace.xml
generated
|
|
@ -2,16 +2,30 @@
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="8edc47ab-1265-4111-9771-536b24cc9310" name="Default Changelist" comment="">
|
<list default="true" id="8edc47ab-1265-4111-9771-536b24cc9310" name="Default Changelist" comment="">
|
||||||
<change afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.gitignore" beforeDir="false" afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
|
||||||
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/og-image.jpg" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/static/og.jpg" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/content-en.json" beforeDir="false" afterPath="$PROJECT_DIR$/content-en.json" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/content-en.json" beforeDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/css/elements.css" beforeDir="false" afterPath="$PROJECT_DIR$/css/elements.css" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/content-hu.json" beforeDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/css/page.css" beforeDir="false" afterPath="$PROJECT_DIR$/css/page.css" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/css/elements.css" beforeDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/index.html" beforeDir="false" afterPath="$PROJECT_DIR$/index.html" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/css/main.css" beforeDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/static/my-notes.png" beforeDir="false" />
|
<change beforePath="$PROJECT_DIR$/css/page.css" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/index.html" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/js/content.js" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/js/main.js" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/static/avoid.jpg" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/static/color.jpg" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/static/forex.gif" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/static/led.jpg" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/static/led720.mp4" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/static/me.jpg" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/static/my-notes.jpg" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/static/og-image.jpg" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/static/og.jpg" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/static/photos.jpg" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/static/platform.png" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/static/process-simulator-input.jpg" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/static/process-simulator.jpg" beforeDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/static/simulation.jpg" beforeDir="false" />
|
||||||
</list>
|
</list>
|
||||||
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
|
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
|
|
@ -23,6 +37,7 @@
|
||||||
<option name="RECENT_TEMPLATES">
|
<option name="RECENT_TEMPLATES">
|
||||||
<list>
|
<list>
|
||||||
<option value="JavaScript File" />
|
<option value="JavaScript File" />
|
||||||
|
<option value="HTML File" />
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
|
@ -35,13 +50,22 @@
|
||||||
</component>
|
</component>
|
||||||
<component name="PropertiesComponent">
|
<component name="PropertiesComponent">
|
||||||
<property name="ASKED_ADD_EXTERNAL_FILES" value="true" />
|
<property name="ASKED_ADD_EXTERNAL_FILES" value="true" />
|
||||||
|
<property name="DefaultHtmlFileTemplate" value="HTML File" />
|
||||||
<property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
|
<property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
|
||||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||||
<property name="ignore_missing_gitignore" value="true" />
|
<property name="ignore_missing_gitignore" value="true" />
|
||||||
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
|
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
|
||||||
|
<property name="nodejs_package_manager_path" value="npm" />
|
||||||
<property name="prettierjs.PrettierConfiguration.Package" value="/usr/local/lib/node_modules/prettier" />
|
<property name="prettierjs.PrettierConfiguration.Package" value="/usr/local/lib/node_modules/prettier" />
|
||||||
|
<property name="settings.editor.selected.configurable" value="watcher.settings" />
|
||||||
|
<property name="ts.external.directory.path" value="C:\Projects\portfolio\CompiledCV\node_modules\typescript\lib" />
|
||||||
</component>
|
</component>
|
||||||
<component name="RecentsManager">
|
<component name="RecentsManager">
|
||||||
|
<key name="MoveFile.RECENT_KEYS">
|
||||||
|
<recent name="C:\Projects\portfolio\CompiledCV\src\content" />
|
||||||
|
<recent name="C:\Projects\portfolio\CompiledCV\dist" />
|
||||||
|
<recent name="C:\Projects\portfolio\CompiledCV\src" />
|
||||||
|
</key>
|
||||||
<key name="CopyFile.RECENT_KEYS">
|
<key name="CopyFile.RECENT_KEYS">
|
||||||
<recent name="$PROJECT_DIR$" />
|
<recent name="$PROJECT_DIR$" />
|
||||||
</key>
|
</key>
|
||||||
|
|
@ -72,7 +96,8 @@
|
||||||
<workItem from="1576164066512" duration="3000" />
|
<workItem from="1576164066512" duration="3000" />
|
||||||
<workItem from="1576250286627" duration="24922000" />
|
<workItem from="1576250286627" duration="24922000" />
|
||||||
<workItem from="1576342852221" duration="24000" />
|
<workItem from="1576342852221" duration="24000" />
|
||||||
<workItem from="1576352253939" duration="106000" />
|
<workItem from="1576352253939" duration="353000" />
|
||||||
|
<workItem from="1576748546157" duration="8755000" />
|
||||||
</task>
|
</task>
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
|
|
|
||||||
24
custom.d.ts
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
declare module "*.svg" {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.png" {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.jpg" {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.jpeg" {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "*.gif" {
|
||||||
|
const content: string;
|
||||||
|
export default content;
|
||||||
|
}
|
||||||
42
index.html
|
|
@ -1,42 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
|
|
||||||
<meta property="og:image:width" content="1500">
|
|
||||||
<meta property="og:image:height" content="785">
|
|
||||||
<meta property="og:title" content="Portfolio - András Schmelczer">
|
|
||||||
<meta property="og:description" content="You can view my projects on a timeline.">
|
|
||||||
<meta property="og:url" content="https://schmelczer.dev">
|
|
||||||
<meta property="og:image" content="https://schmelczer.dev/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
|
|
@ -1,157 +0,0 @@
|
||||||
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
|
|
@ -1,15 +0,0 @@
|
||||||
(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';
|
|
||||||
})();
|
|
||||||
35
package.json
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"name": "portfolio",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "An easily configurable portfolio.",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"build": "webpack"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/schmelczerandras/timeline.git"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/schmelczerandras/timeline/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/schmelczerandras/timeline#readme",
|
||||||
|
"devDependencies": {
|
||||||
|
"clean-webpack-plugin": "^3.0.0",
|
||||||
|
"css-loader": "^3.4.0",
|
||||||
|
"file-loader": "^5.0.2",
|
||||||
|
"html-webpack-plugin": "^3.2.0",
|
||||||
|
"node-sass": "^4.13.0",
|
||||||
|
"prettier": "^1.19.1",
|
||||||
|
"sass-loader": "^8.0.0",
|
||||||
|
"style-loader": "^1.0.2",
|
||||||
|
"ts-loader": "^6.2.1",
|
||||||
|
"typescript": "^3.7.3",
|
||||||
|
"webpack": "^4.41.4",
|
||||||
|
"webpack-cli": "^3.3.10"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,116 +1,138 @@
|
||||||
{
|
import me from "../static/me.jpg";
|
||||||
"config": {
|
import forex from "../static/forex.gif";
|
||||||
"showMore": "Show details",
|
import myNotes from "../static/my-notes.jpg";
|
||||||
"showLess": "Show less"
|
import processSimulator from "../static/process-simulator.jpg";
|
||||||
},
|
import processSimulatorInput from "../static/process-simulator-input.jpg";
|
||||||
"header": {
|
import citySimulation from "../static/simulation.jpg";
|
||||||
"name": "András Schmelczer",
|
import color from "../static/color.jpg";
|
||||||
"picture": "/static/me.jpg",
|
import platform from "../static/platform.png";
|
||||||
"about": [
|
import photos from "../static/photos.jpg";
|
||||||
"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.",
|
import led from "../static/led.jpg";
|
||||||
"You can see some of the more interesting projects I have worked on below."
|
|
||||||
]
|
export const content = {
|
||||||
},
|
config: {
|
||||||
"timeline": [
|
showMore: "Show details",
|
||||||
{
|
showLess: "Show less"
|
||||||
"date": "2019 Autumn",
|
},
|
||||||
"title": "Predicting foreign exchange rates",
|
header: {
|
||||||
"picture": "/static/forex.gif",
|
name: "András Schmelczer",
|
||||||
"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).",
|
picture: me,
|
||||||
"more": [
|
about: [
|
||||||
"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.",
|
"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.",
|
||||||
"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."
|
"You can see some of the more interesting projects I have worked on below."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
timeline: [
|
||||||
"date": "2019 November",
|
{
|
||||||
"title": "My Notes",
|
date: "2019 Autumn",
|
||||||
"picture": "/static/my-notes.jpg",
|
title: "Predicting foreign exchange rates",
|
||||||
"description": "A minimalist note organizer and editor powered by Markwon.",
|
picture: forex,
|
||||||
"more": [
|
description:
|
||||||
{ "type": "a", "href": "https://github.com/schmelczerandras/my-notes", "text": "MyNotes on GitHub" },
|
"From the animation we can see that my algorithm does a somewhat acceptable job at predicting (blue graph) the EUR/USD rates (green graph).",
|
||||||
"A basic android app for creating and filtering notes written in markdown.",
|
more: [
|
||||||
"It was my homework for BME's Android and web development course. It was also my first experience with Android development."
|
"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": "2018 October - November",
|
{
|
||||||
"title": "Simulating the cooling system of a nuclear facility",
|
date: "2019 November",
|
||||||
"picture": "/static/process-simulator.jpg",
|
title: "My Notes",
|
||||||
"description": "Dynamically calculating the temperatures and flow velocities in a fluid based cooling system based on a simple model.",
|
picture: myNotes,
|
||||||
"more": [
|
description: "A minimalist note organizer and editor powered by Markwon.",
|
||||||
"A simulated system can contain reactors (heaters / coolers), pumps, heat exchangers, drains sources, and of course, pipes.",
|
more: [
|
||||||
"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."
|
type: "a",
|
||||||
]
|
href: "https://github.com/schmelczerandras/my-notes",
|
||||||
},
|
text: "MyNotes on GitHub"
|
||||||
{
|
},
|
||||||
"date": "2018 October - November",
|
"A basic android app for creating and filtering notes written in markdown.",
|
||||||
"title": "Graph editing application",
|
"It was my homework for BME's Android and web development course. It was also my first experience with Android development."
|
||||||
"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.",
|
date: "2018 October - November",
|
||||||
"The UI is built with JavaFX. The output can be exported as JSON or directly uploaded to the simulation backend."
|
title: "Simulating the cooling system of a nuclear facility",
|
||||||
]
|
picture: processSimulator,
|
||||||
},
|
description:
|
||||||
{
|
"Dynamically calculating the temperatures and flow velocities in a fluid based cooling system based on a simple model.",
|
||||||
"date": "2018 July-August",
|
more: [
|
||||||
"title": "City simulation",
|
"A simulated system can contain reactors (heaters / coolers), pumps, heat exchangers, drains sources, and of course, pipes.",
|
||||||
"picture": "/static/simulation.jpg",
|
"The algorithm takes advantages of graphs and matrices to get to a next time frame.",
|
||||||
"description": "Simulating a city where car crashes are more frequent than usual.",
|
"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."
|
||||||
"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 the 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.",
|
date: "2018 October - November",
|
||||||
"The program is made with Unity using C# as the scripting language. The models and animations were also made by me using Blender."
|
title: "Graph editing application",
|
||||||
]
|
picture: processSimulatorInput,
|
||||||
},
|
description:
|
||||||
{
|
"An intuitive editor to create and edit input files for the nuclear facility simulator.",
|
||||||
"date": "2018 June",
|
more: [
|
||||||
"title": "Photo color grader",
|
"Nodes can be moved with drag&drop gestures. Editing the parameters of elements can be done on the right panel.",
|
||||||
"picture": "/static/color.jpg",
|
"The UI is built with JavaFX. The output can be exported as JSON or directly uploaded to the simulation backend."
|
||||||
"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.",
|
date: "2018 July-August",
|
||||||
"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).",
|
title: "City simulation",
|
||||||
{ "type": "a", "href": "color", "text": "schmelczer.dev/color" }
|
picture: citySimulation,
|
||||||
]
|
description:
|
||||||
},
|
"Simulating a city where car crashes are more frequent than usual.",
|
||||||
{
|
more: [
|
||||||
"date": "2017 autumn",
|
"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 the contestants could instantly see the effect of their work.",
|
||||||
"title": "Platform game",
|
"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.",
|
||||||
"picture": "/static/platform.png",
|
"The program is made with Unity using C# as the scripting language. The models and animations were also made by me using Blender."
|
||||||
"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: "2018 June",
|
||||||
]
|
title: "Photo color grader",
|
||||||
},
|
picture: color,
|
||||||
{
|
description:
|
||||||
"date": "2016 summer",
|
"An innovative (at least I thought so) color grader web application.",
|
||||||
"title": "Photos",
|
more: [
|
||||||
"picture": "/static/photos.jpg",
|
"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. ",
|
||||||
"description": "A simple web page where you can view my photos.",
|
"You can select some colors and then apply transformations to the other colors as a function of their distance to the selected color.",
|
||||||
"link": "schmelczer.dev/photos"
|
"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": "2016 spring",
|
},
|
||||||
"title": "Lights synchronised to music",
|
{
|
||||||
"picture": "/static/led.jpg",
|
date: "2017 autumn",
|
||||||
"description": "A full stack application with a built-in music player which music controls the color of some RGB LED strips.",
|
|
||||||
"more": [
|
title: "Platform game",
|
||||||
"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.",
|
picture: platform,
|
||||||
"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.",
|
description:
|
||||||
"Below is a video showing the system in work.",
|
"A 3D game written in C with the help of SDL 1.2 (I haven't heard of GPU programming at the time).",
|
||||||
{ "type": "video", "src": "static/led720.mp4" }
|
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."
|
||||||
],
|
]
|
||||||
"footer": {
|
},
|
||||||
"email": "andras.schmelczer@schdesign.hu",
|
{
|
||||||
"cv": "/static/andras_schmelczer_cv.pdf"
|
date: "2016 summer",
|
||||||
}
|
title: "Photos",
|
||||||
}
|
picture: photos,
|
||||||
|
description: "A simple web page where you can view my photos.",
|
||||||
|
link: "schmelczer.dev/photos"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
date: "2016 spring",
|
||||||
|
title: "Lights synchronised to music",
|
||||||
|
picture: led,
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,93 +1,94 @@
|
||||||
{
|
/*{
|
||||||
"header": {
|
"header": {
|
||||||
"name": "Schmelczer András",
|
"name": "Schmelczer András",
|
||||||
"picture": "/static/me.jpg",
|
"picture": "/static/me.jpg",
|
||||||
"about": [
|
"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.",
|
"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.",
|
"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."
|
"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": [
|
"timeline": [
|
||||||
{
|
{
|
||||||
"date": "2018 október - november",
|
"date": "2018 október - november",
|
||||||
"title": "Atomreaktor hűtőrendszerének szimulációja",
|
"title": "Atomreaktor hűtőrendszerének szimulációja",
|
||||||
"picture": "/static/process-simulator.jpg",
|
"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.",
|
"description": "Egy csőrendszerben lévő víz hőmérsékletének és áramlásának dinamikus számítása.",
|
||||||
"more": [
|
"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.",
|
"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.",
|
"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."
|
"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",
|
"date": "2018 október - november",
|
||||||
"title": "Gráf szerkesztő alkalmazás",
|
"title": "Gráf szerkesztő alkalmazás",
|
||||||
"picture": "/static/process-simulator-input.jpg",
|
"picture": "/static/process-simulator-input.jpg",
|
||||||
"description": "A fentebb látható szoftverhez tartozó csőrendszert lehet vele létrehozni.",
|
"description": "A fentebb látható szoftverhez tartozó csőrendszert lehet vele létrehozni.",
|
||||||
"more": [
|
"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.",
|
"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.",
|
"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."
|
"Java-ban lett írva, a megjelenítést a JavaFX biztosítja."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"date": "2018 július - augusztus",
|
"date": "2018 július - augusztus",
|
||||||
"title": "Közlekedés szimuláció",
|
"title": "Közlekedés szimuláció",
|
||||||
"picture": "/static/sim.jpg",
|
"picture": "/static/sim.jpg",
|
||||||
"description": "A modellek Blenderben, a szimuláció Unityben készült.",
|
"description": "A modellek Blenderben, a szimuláció Unityben készült.",
|
||||||
"more": [
|
"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.",
|
"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 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."
|
"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",
|
"date": "2018 június",
|
||||||
"title": "Színszerkesztő",
|
"title": "Színszerkesztő",
|
||||||
"picture": "/static/szinezo.jpg",
|
"picture": "/static/szinezo.jpg",
|
||||||
"description": "Egy innovatív color grader képekhez.",
|
"description": "Egy innovatív color grader képekhez.",
|
||||||
"more": [
|
"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.",
|
"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.",
|
"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).",
|
"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" }
|
{ "type": "a", "href": "/szinezo", "text": "schmelczer.hu/szinezo" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"date": "2017 ősz",
|
"date": "2017 ősz",
|
||||||
"title": "Platform játék",
|
"title": "Platform játék",
|
||||||
"picture": "/static/platform.png",
|
"picture": "/static/platform.png",
|
||||||
"description": "Írtam egy 3D-s játékot C-ben az SDL 1.2 segítségével.",
|
"description": "Írtam egy 3D-s játékot C-ben az SDL 1.2 segítségével.",
|
||||||
"more": [
|
"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.",
|
"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.",
|
"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" }
|
{ "type": "a", "href": "/platform", "text": "schmelczer.hu/platform" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"date": "2016 nyár",
|
"date": "2016 nyár",
|
||||||
"title": "Fényképek",
|
"title": "Fényképek",
|
||||||
"picture": "/static/kepek.jpg",
|
"picture": "/static/kepek.jpg",
|
||||||
"description": "Csináltam egy oldalt, ahol a fényképeimet lehet megnézni.",
|
"description": "Csináltam egy oldalt, ahol a fényképeimet lehet megnézni.",
|
||||||
"link": "schmelczer.hu/kepek"
|
"link": "schmelczer.hu/kepek"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"date": "2016 tavasz",
|
"date": "2016 tavasz",
|
||||||
"title": "Zenére világító ledsorok",
|
"title": "Zenére világító ledsorok",
|
||||||
"picture": "/static/LED.jpg",
|
"picture": "/static/LED.jpg",
|
||||||
"description": "Egy alkalmazást készítettem, amivel RGB ledsorok színét lehet a zene ritmusára változtatni.",
|
"description": "Egy alkalmazást készítettem, amivel RGB ledsorok színét lehet a zene ritmusára változtatni.",
|
||||||
"more": [
|
"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.",
|
"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.",
|
"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.",
|
"A működő rendszerről készítettem egy videót, ami alább tekinthető meg.",
|
||||||
{ "type": "video", "src": "static/led720.mp4" }
|
{ "type": "video", "src": "static/led720.mp4" }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"footer": {
|
"footer": {
|
||||||
"email": "andras.schmelczer@schdesign.hu"
|
"email": "andras.schmelczer@schdesign.hu"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
35
src/index.html
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
|
||||||
|
<meta property="og:image:width" content="1500">
|
||||||
|
<meta property="og:image:height" content="785">
|
||||||
|
<meta property="og:title" content="Portfolio - András Schmelczer">
|
||||||
|
<meta property="og:description" content="You can view my projects on a timeline.">
|
||||||
|
<meta property="og:url" content="https://schmelczer.dev">
|
||||||
|
<meta property="og:image" content="https://schmelczer.dev/og-image.jpg">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#31343F" />
|
||||||
|
|
||||||
|
<title>Portfolio</title>
|
||||||
|
</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>
|
||||||
|
</html>
|
||||||
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 570 KiB After Width: | Height: | Size: 570 KiB |
|
Before Width: | Height: | Size: 475 KiB After Width: | Height: | Size: 475 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 135 KiB After Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 213 KiB After Width: | Height: | Size: 213 KiB |
|
Before Width: | Height: | Size: 227 KiB After Width: | Height: | Size: 227 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 366 KiB After Width: | Height: | Size: 366 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 457 KiB After Width: | Height: | Size: 457 KiB |
|
|
@ -1,173 +1,173 @@
|
||||||
/* X sign visible in the photo viewer mode. */
|
/* X sign visible in the photo viewer mode. */
|
||||||
#exit {
|
#exit {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: var(--exit-size);
|
width: var(--exit-size);
|
||||||
height: var(--exit-size);
|
height: var(--exit-size);
|
||||||
top: var(--exit-size);
|
top: var(--exit-size);
|
||||||
right: var(--exit-size);
|
right: var(--exit-size);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
#exit:before,
|
#exit:before,
|
||||||
#exit:after {
|
#exit:after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: var(--line-width);
|
width: var(--line-width);
|
||||||
height: calc(var(--exit-size) * 1.4142);
|
height: calc(var(--exit-size) * 1.4142);
|
||||||
background: white;
|
background: white;
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
top: calc(var(--exit-size) * -0.4142 / 2);
|
top: calc(var(--exit-size) * -0.4142 / 2);
|
||||||
left: 50%;
|
left: 50%;
|
||||||
}
|
}
|
||||||
#exit:before {
|
#exit:before {
|
||||||
transform: rotate(45deg);
|
transform: rotate(45deg);
|
||||||
}
|
}
|
||||||
#exit:after {
|
#exit:after {
|
||||||
transform: rotate(-45deg);
|
transform: rotate(-45deg);
|
||||||
}
|
}
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
/* Links with interactive underline. */
|
/* Links with interactive underline. */
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-bottom: solid 3px var(--light-accent-color);
|
border-bottom: solid 3px var(--light-accent-color);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
a:after {
|
a:after {
|
||||||
content: "";
|
content: "";
|
||||||
height: var(--line-width);
|
height: var(--line-width);
|
||||||
background-color: var(--accent-color);
|
background-color: var(--accent-color);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: calc(-1 * var(--line-width));
|
bottom: calc(-1 * var(--line-width));
|
||||||
width: 0;
|
width: 0;
|
||||||
transition: width var(--transition-time);
|
transition: width var(--transition-time);
|
||||||
}
|
}
|
||||||
a:hover:after {
|
a:hover:after {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
/* Line with circle for the timeline sections. */
|
/* Line with circle for the timeline sections. */
|
||||||
.line {
|
.line {
|
||||||
margin-left: calc(var(--dot-size) / 2);
|
margin-left: calc(var(--dot-size) / 2);
|
||||||
margin-right: var(--line-height);
|
margin-right: var(--line-height);
|
||||||
border-left: var(--line-width) solid var(--text-color);
|
border-left: var(--line-width) solid var(--text-color);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
.line:before {
|
.line:before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: calc(-1 / 2 * var(--dot-size) - 1.5 * var(--line-width));
|
left: calc(-1 / 2 * var(--dot-size) - 1.5 * var(--line-width));
|
||||||
background: var(--bg-color);
|
background: var(--bg-color);
|
||||||
top: 33%;
|
top: 33%;
|
||||||
width: var(--dot-size);
|
width: var(--dot-size);
|
||||||
height: var(--dot-size);
|
height: var(--dot-size);
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
border: var(--line-width) var(--text-color) solid;
|
border: var(--line-width) var(--text-color) solid;
|
||||||
}
|
}
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
/* Activity cards. */
|
/* Activity cards. */
|
||||||
.card {
|
.card {
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: var(--margin);
|
padding: var(--margin);
|
||||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.125);
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.125);
|
||||||
transition: box-shadow;
|
transition: box-shadow;
|
||||||
transition-duration: var(--transition-time);
|
transition-duration: var(--transition-time);
|
||||||
background: var(--card-color);
|
background: var(--card-color);
|
||||||
}
|
}
|
||||||
.card:hover {
|
.card:hover {
|
||||||
box-shadow: 0 0 3px rgba(0, 0, 0, 0.05);
|
box-shadow: 0 0 3px rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
/* Dates related to the lines and cards. */
|
/* Dates related to the lines and cards. */
|
||||||
.date-narrow-screen,
|
.date-narrow-screen,
|
||||||
.date-wide-screen {
|
.date-wide-screen {
|
||||||
font: 400 1em "Open sans", sans-serif;
|
font: 400 1em "Open sans", sans-serif;
|
||||||
}
|
}
|
||||||
.date-narrow-screen {
|
.date-narrow-screen {
|
||||||
display: none;
|
display: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-top: calc(var(--line-height) / 2.25) !important;
|
margin-top: calc(var(--line-height) / 2.25) !important;
|
||||||
color: var(--light-text-color);
|
color: var(--light-text-color);
|
||||||
}
|
}
|
||||||
.date-wide-screen {
|
.date-wide-screen {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: calc(33% + var(--dot-size) + 1ch);
|
top: calc(33% + var(--dot-size) + 1ch);
|
||||||
margin: 0 var(--margin) 0 calc(var(--line-width) + 1ex);
|
margin: 0 var(--margin) 0 calc(var(--line-width) + 1ex);
|
||||||
width: 100px;
|
width: 100px;
|
||||||
}
|
}
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
/* The photo viewer */
|
/* The photo viewer */
|
||||||
#photo-viewer {
|
#photo-viewer {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
background: var(--photo-viewer-color);
|
background: var(--photo-viewer-color);
|
||||||
z-index: -3;
|
z-index: -3;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity var(--transition-time);
|
transition: opacity var(--transition-time);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* #photo */
|
/* #photo */
|
||||||
#photo-viewer > img {
|
#photo-viewer > img {
|
||||||
max-width: 80vw;
|
max-width: 80vw;
|
||||||
max-height: 80vh;
|
max-height: 80vh;
|
||||||
}
|
}
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
/* Scrollbar. */
|
/* Scrollbar. */
|
||||||
body::-webkit-scrollbar-track,
|
body::-webkit-scrollbar-track,
|
||||||
body::-webkit-scrollbar {
|
body::-webkit-scrollbar {
|
||||||
background-color: var(--scroll-color);
|
background-color: var(--scroll-color);
|
||||||
width: 12px;
|
width: 12px;
|
||||||
}
|
}
|
||||||
body::-webkit-scrollbar-thumb {
|
body::-webkit-scrollbar-thumb {
|
||||||
background-color: var(--accent-color);
|
background-color: var(--accent-color);
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
}
|
}
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
/* Selections. */
|
/* Selections. */
|
||||||
::-moz-selection {
|
::-moz-selection {
|
||||||
background: var(--accent-color);
|
background: var(--accent-color);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
::selection {
|
::selection {
|
||||||
background: var(--accent-color);
|
background: var(--accent-color);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
/* Absolute centering parent. */
|
/* Absolute centering parent. */
|
||||||
.center {
|
.center {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
/**/
|
/**/
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
.line {
|
.line {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Disable animation. */
|
/* Disable animation. */
|
||||||
.card:hover {
|
.card:hover {
|
||||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.125);
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.125);
|
||||||
}
|
}
|
||||||
|
|
||||||
.date-narrow-screen {
|
.date-narrow-screen {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.date-wide-screen {
|
.date-wide-screen {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
3
src/styles/index.scss
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
@import "main";
|
||||||
|
@import "elements";
|
||||||
|
@import "page";
|
||||||
|
|
@ -1,28 +1,28 @@
|
||||||
@import url('https://fonts.googleapis.com/css?family=Open+Sans:400,400i|Raleway&subset=latin-ext');
|
@import url('https://fonts.googleapis.com/css?family=Open+Sans:400,400i|Raleway&subset=latin-ext');
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--photo-viewer-color: rgba(0, 0, 0, 0.75);
|
--photo-viewer-color: rgba(0, 0, 0, 0.75);
|
||||||
--accent-color: #5264bf;
|
--accent-color: #5264bf;
|
||||||
--light-accent-color: #e5e5ff;
|
--light-accent-color: #e5e5ff;
|
||||||
--scroll-color: #ffd6d6;
|
--scroll-color: #ffd6d6;
|
||||||
--bg-color:linear-gradient(90deg, #fff9e0 0, #ffd6d6 100%);
|
--bg-color:linear-gradient(90deg, #fff9e0 0, #ffd6d6 100%);
|
||||||
--card-color: white;
|
--card-color: white;
|
||||||
--text-color: #31343f;
|
--text-color: #31343f;
|
||||||
--light-text-color: #7a7d8e;
|
--light-text-color: #7a7d8e;
|
||||||
--dot-size: 25px;
|
--dot-size: 25px;
|
||||||
--line-width: 3px;
|
--line-width: 3px;
|
||||||
--exit-size: 25px;
|
--exit-size: 25px;
|
||||||
--line-height: 15px;
|
--line-height: 15px;
|
||||||
--smaller-margin: 25px;
|
--smaller-margin: 25px;
|
||||||
--margin: 35px;
|
--margin: 35px;
|
||||||
--border-radius: 5px;
|
--border-radius: 5px;
|
||||||
--transition-time: 200ms;
|
--transition-time: 200ms;
|
||||||
--width: 765px;
|
--width: 765px;
|
||||||
}
|
}
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
:root {
|
:root {
|
||||||
--exit-size: 20px;
|
--exit-size: 20px;
|
||||||
--margin: 25px;
|
--margin: 25px;
|
||||||
--smaller-margin: 20px;
|
--smaller-margin: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,130 +1,130 @@
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
background-color: #31343f;
|
background-color: #31343f;
|
||||||
background: var(--bg-color);
|
background: var(--bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
width: var(--width);
|
width: var(--width);
|
||||||
margin: auto;
|
margin: auto;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
header,
|
header,
|
||||||
footer,
|
footer,
|
||||||
#timeline,
|
#timeline,
|
||||||
#about > p:first-of-type,
|
#about > p:first-of-type,
|
||||||
#timeline > section:not(:first-of-type) > .card {
|
#timeline > section:not(:first-of-type) > .card {
|
||||||
margin-top: var(--margin);
|
margin-top: var(--margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
#header-pic,
|
#header-pic,
|
||||||
h1 {
|
h1 {
|
||||||
font: 400 3.33em "Raleway", sans-serif;
|
font: 400 3.33em "Raleway", sans-serif;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#header-pic {
|
#header-pic {
|
||||||
height: 4ch;
|
height: 4ch;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
margin-right: 1.5ex;
|
margin-right: 1.5ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
p,
|
p,
|
||||||
a {
|
a {
|
||||||
font: 400 1.125em "Open sans", sans-serif;
|
font: 400 1.125em "Open sans", sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
#about > p {
|
#about > p {
|
||||||
text-align: justify;
|
text-align: justify;
|
||||||
hyphens: auto;
|
hyphens: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#timeline > section {
|
#timeline > section {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#about > p,
|
#about > p,
|
||||||
.card > *:not(:first-child):not(:last-child),
|
.card > *:not(:first-child):not(:last-child),
|
||||||
.collapsed > *:not(:first-child) {
|
.collapsed > *:not(:first-child) {
|
||||||
margin-top: var(--line-height);
|
margin-top: var(--line-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font: 400 2em "Raleway", sans-serif;
|
font: 400 2em "Raleway", sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card img,
|
.card img,
|
||||||
video {
|
video {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
margin-bottom: var(--smaller-margin);
|
margin-bottom: var(--smaller-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapsed > p {
|
.collapsed > p {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.collapsed {
|
.collapsed {
|
||||||
height: 0;
|
height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: height var(--transition-time);
|
transition: height var(--transition-time);
|
||||||
}
|
}
|
||||||
.collapsed > *:last-child {
|
.collapsed > *:last-child {
|
||||||
margin-bottom: var(--smaller-margin);
|
margin-bottom: var(--smaller-margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
margin-bottom: var(--margin);
|
margin-bottom: var(--margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
body {
|
body {
|
||||||
width: 85%;
|
width: 85%;
|
||||||
font-size: 0.85em;
|
font-size: 0.85em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#photo {
|
#photo {
|
||||||
max-width: 94vw;
|
max-width: 94vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
#header-pic {
|
#header-pic {
|
||||||
font-size: 3em;
|
font-size: 3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#header-pic {
|
#header-pic {
|
||||||
height: 5.5ch;
|
height: 5.5ch;
|
||||||
margin: 0.75ex 0 0.5ex 0;
|
margin: 0.75ex 0 0.5ex 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#about > p {
|
#about > p {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
19
src/ts/index.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { createPageFactory } from "./parser";
|
||||||
|
import { content } from "../content/en";
|
||||||
|
|
||||||
|
import "../styles/index.scss";
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const ids = {
|
||||||
|
pictureId: "header-pic",
|
||||||
|
nameId: "name",
|
||||||
|
aboutId: "about",
|
||||||
|
timelineId: "timeline",
|
||||||
|
emailId: "email",
|
||||||
|
photoViewerId: "photo-viewer",
|
||||||
|
photoId: "photo"
|
||||||
|
};
|
||||||
|
|
||||||
|
await createPageFactory(ids)(content);
|
||||||
|
document.body.style.visibility = "visible";
|
||||||
|
})();
|
||||||
168
src/ts/parser.ts
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
export const createPageFactory = ({
|
||||||
|
nameId,
|
||||||
|
pictureId,
|
||||||
|
aboutId,
|
||||||
|
timelineId,
|
||||||
|
emailId,
|
||||||
|
photoId,
|
||||||
|
photoViewerId
|
||||||
|
}) => {
|
||||||
|
const createPage = content => {
|
||||||
|
const { config, header, timeline, footer } = content;
|
||||||
|
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 as any).toggleLongDescription = toggleLongDescriptionFactory(config);
|
||||||
|
(window as any).showPhoto = showPhoto;
|
||||||
|
|
||||||
|
(window as any).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 createIdFactory = () => {
|
||||||
|
let id = 0;
|
||||||
|
return () => id++;
|
||||||
|
};
|
||||||
|
const createId = createIdFactory();
|
||||||
|
|
||||||
|
const idToButtonId = id => `button_${id}`;
|
||||||
|
|
||||||
|
const idToActivityId = id => `activity_${id}`;
|
||||||
|
|
||||||
|
return createPage;
|
||||||
|
};
|
||||||
10
tsconfig.json
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./dist/",
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"module": "es6",
|
||||||
|
"target": "es5",
|
||||||
|
"sourceMap": true,
|
||||||
|
"allowJs": true
|
||||||
|
}
|
||||||
|
}
|
||||||
48
webpack.config.js
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
const path = require("path");
|
||||||
|
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||||
|
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mode: "development",
|
||||||
|
devtool: "inline-source-map",
|
||||||
|
plugins: [
|
||||||
|
new CleanWebpackPlugin(),
|
||||||
|
new HtmlWebpackPlugin({
|
||||||
|
hash: true,
|
||||||
|
xhtml: true,
|
||||||
|
template: "./src/index.html"
|
||||||
|
})
|
||||||
|
],
|
||||||
|
entry: {
|
||||||
|
index: "./src/ts/index.ts"
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.s[ac]ss$/i,
|
||||||
|
use: ["style-loader", "css-loader", "sass-loader"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: "ts-loader",
|
||||||
|
exclude: /node_modules/
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(png|svg|jpe?g|gif)$/,
|
||||||
|
use: {
|
||||||
|
loader: "file-loader",
|
||||||
|
query: {
|
||||||
|
outputPath: "images"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: [".ts"]
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: "[name].bundle.js",
|
||||||
|
path: path.resolve(__dirname, "dist")
|
||||||
|
}
|
||||||
|
};
|
||||||