From 90a3e4f5333a41ca2ba162fbc8732c6dd374a2f2 Mon Sep 17 00:00:00 2001 From: rodude123 Date: Fri, 29 Jul 2022 20:00:36 +0100 Subject: [PATCH] Added in editor login feature to login into editor Signed-off-by: rodude123 --- composer.json | 3 +- composer.lock | 487 ++++++++++++++++++------------------- dist/api/.htaccess | 4 + dist/api/index.php | 102 +++++--- dist/api/projectData.php | 9 +- dist/api/timelineData.php | 14 +- dist/api/user.php | 38 +++ dist/css/main.css | 2 +- dist/editor/css/main.css | 1 + dist/editor/editor.html | 1 + dist/editor/index.html | 1 + dist/editor/js/editor.js | 1 + dist/editor/js/index.js | 1 + dist/editor/js/main.js | 1 + dist/index.html | 2 +- gulpfile.js | 24 +- package-lock.json | 14 ++ package.json | 1 + src/api/index.php | 90 +++++-- src/api/projectData.php | 7 +- src/api/timelineData.php | 14 +- src/api/user.php | 38 +++ src/css/contact.css | 90 ------- src/css/main.css | 28 --- src/css/templateStyles.css | 114 ++++++++- src/editor/css/login.css | 100 ++++++++ src/editor/css/main.css | 8 + src/editor/editor.html | 12 + src/editor/index.html | 36 +++ src/editor/js/editor.js | 12 + src/editor/js/index.js | 54 ++++ 31 files changed, 831 insertions(+), 478 deletions(-) create mode 100644 dist/api/.htaccess create mode 100644 dist/api/user.php create mode 100644 dist/editor/css/main.css create mode 100644 dist/editor/editor.html create mode 100644 dist/editor/index.html create mode 100644 dist/editor/js/editor.js create mode 100644 dist/editor/js/index.js create mode 100644 dist/editor/js/main.js create mode 100644 src/api/user.php create mode 100644 src/editor/css/login.css create mode 100644 src/editor/css/main.css create mode 100644 src/editor/editor.html create mode 100644 src/editor/index.html create mode 100644 src/editor/js/editor.js create mode 100644 src/editor/js/index.js diff --git a/composer.json b/composer.json index a95741b..ebb5a92 100644 --- a/composer.json +++ b/composer.json @@ -9,6 +9,7 @@ "http-interop/http-factory-guzzle": "^1.2", "laminas/laminas-diactoros": "^2.6", "laminas/laminas-httphandlerrunner": "^2.0", - "selective/samesite-cookie": "^0.3.0" + "selective/samesite-cookie": "^0.3.0", + "ext-json": "*" } } diff --git a/composer.lock b/composer.lock index 52c9b6a..3991802 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "5aafeb561d1b79ead81458f3e265c0c1", + "content-hash": "5ddb974cba41098a51ddfa43a17c9520", "packages": [ { "name": "fig/http-message-util", @@ -64,16 +64,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.0.0", + "version": "2.4.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "1dc8d9cba3897165e16d12bb13d813afb1eb3fe7" + "reference": "13388f00956b1503577598873fffb5ae994b5737" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/1dc8d9cba3897165e16d12bb13d813afb1eb3fe7", - "reference": "1dc8d9cba3897165e16d12bb13d813afb1eb3fe7", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/13388f00956b1503577598873fffb5ae994b5737", + "reference": "13388f00956b1503577598873fffb5ae994b5737", "shasum": "" }, "require": { @@ -97,7 +97,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.4-dev" } }, "autoload": { @@ -110,13 +110,34 @@ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, { "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", "homepage": "https://github.com/Tobion" }, { @@ -138,9 +159,23 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.0.0" + "source": "https://github.com/guzzle/psr7/tree/2.4.0" }, - "time": "2021-06-30T20:03:07+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2022-06-20T21:43:11+00:00" }, { "name": "http-interop/http-factory-guzzle", @@ -202,46 +237,43 @@ }, { "name": "laminas/laminas-diactoros", - "version": "2.6.0", + "version": "2.14.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-diactoros.git", - "reference": "7d2034110ae18afe05050b796a3ee4b3fe177876" + "reference": "6cb35f61913f06b2c91075db00f67cfd78869e28" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/7d2034110ae18afe05050b796a3ee4b3fe177876", - "reference": "7d2034110ae18afe05050b796a3ee4b3fe177876", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/6cb35f61913f06b2c91075db00f67cfd78869e28", + "reference": "6cb35f61913f06b2c91075db00f67cfd78869e28", "shasum": "" }, "require": { - "laminas/laminas-zendframework-bridge": "^1.0", - "php": "^7.3 || ~8.0.0", + "php": "^7.3 || ~8.0.0 || ~8.1.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.0" }, "conflict": { - "phpspec/prophecy": "<1.9.0" + "phpspec/prophecy": "<1.9.0", + "zendframework/zend-diactoros": "*" }, "provide": { "psr/http-factory-implementation": "1.0", "psr/http-message-implementation": "1.0" }, - "replace": { - "zendframework/zend-diactoros": "^2.2.1" - }, "require-dev": { "ext-curl": "*", "ext-dom": "*", "ext-gd": "*", "ext-libxml": "*", - "http-interop/http-factory-tests": "^0.8.0", - "laminas/laminas-coding-standard": "~1.0.0", - "php-http/psr7-integration-tests": "^1.1", + "http-interop/http-factory-tests": "^0.9.0", + "laminas/laminas-coding-standard": "~2.3.0", + "php-http/psr7-integration-tests": "^1.1.1", "phpspec/prophecy-phpunit": "^2.0", - "phpunit/phpunit": "^9.1", - "psalm/plugin-phpunit": "^0.14.0", - "vimeo/psalm": "^4.3" + "phpunit/phpunit": "^9.5", + "psalm/plugin-phpunit": "^0.17.0", + "vimeo/psalm": "^4.24.0" }, "type": "library", "extra": { @@ -300,34 +332,34 @@ "type": "community_bridge" } ], - "time": "2021-05-18T14:41:54+00:00" + "time": "2022-07-28T12:23:48+00:00" }, { "name": "laminas/laminas-httphandlerrunner", - "version": "2.0.1", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-httphandlerrunner.git", - "reference": "477b8fc161bc59dca2dab90bb01b5bf57f90eea6" + "reference": "4d337cde83e6b901a4443b0ab5c3b97cbaa46413" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/477b8fc161bc59dca2dab90bb01b5bf57f90eea6", - "reference": "477b8fc161bc59dca2dab90bb01b5bf57f90eea6", + "url": "https://api.github.com/repos/laminas/laminas-httphandlerrunner/zipball/4d337cde83e6b901a4443b0ab5c3b97cbaa46413", + "reference": "4d337cde83e6b901a4443b0ab5c3b97cbaa46413", "shasum": "" }, "require": { - "php": "^7.3 || ~8.0.0", + "php": "^7.3 || ~8.0.0 || ~8.1.0", "psr/http-message": "^1.0", "psr/http-message-implementation": "^1.0", "psr/http-server-handler": "^1.0" }, "require-dev": { "laminas/laminas-coding-standard": "~2.3.0", - "laminas/laminas-diactoros": "^2.1.1", - "phpunit/phpunit": "^9.3", - "psalm/plugin-phpunit": "^0.15.1", - "vimeo/psalm": "^4.6" + "laminas/laminas-diactoros": "^2.8.0", + "phpunit/phpunit": "^9.5.9", + "psalm/plugin-phpunit": "^0.16.1", + "vimeo/psalm": "^4.10.0" }, "type": "library", "extra": { @@ -367,118 +399,121 @@ "type": "community_bridge" } ], - "time": "2021-08-05T14:45:43+00:00" + "time": "2021-09-22T09:27:36+00:00" }, { - "name": "laminas/laminas-zendframework-bridge", - "version": "1.3.0", + "name": "laravel/serializable-closure", + "version": "v1.2.0", "source": { "type": "git", - "url": "https://github.com/laminas/laminas-zendframework-bridge.git", - "reference": "13af2502d9bb6f7d33be2de4b51fb68c6cdb476e" + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "09f0e9fb61829f628205b7c94906c28740ff9540" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/13af2502d9bb6f7d33be2de4b51fb68c6cdb476e", - "reference": "13af2502d9bb6f7d33be2de4b51fb68c6cdb476e", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/09f0e9fb61829f628205b7c94906c28740ff9540", + "reference": "09f0e9fb61829f628205b7c94906c28740ff9540", "shasum": "" }, "require": { - "php": "^7.3 || ^8.0" + "php": "^7.3|^8.0" }, "require-dev": { - "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.1 || ^9.3", - "psalm/plugin-phpunit": "^0.15.1", - "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^4.6" + "pestphp/pest": "^1.18", + "phpstan/phpstan": "^0.12.98", + "symfony/var-dumper": "^5.3" }, "type": "library", "extra": { - "laminas": { - "module": "Laminas\\ZendFrameworkBridge" + "branch-alias": { + "dev-master": "1.x-dev" } }, "autoload": { - "files": [ - "src/autoload.php" - ], "psr-4": { - "Laminas\\ZendFrameworkBridge\\": "src//" + "Laravel\\SerializableClosure\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "Alias legacy ZF class names to Laminas Project equivalents.", - "keywords": [ - "ZendFramework", - "autoloading", - "laminas", - "zf" - ], - "support": { - "forum": "https://discourse.laminas.dev/", - "issues": "https://github.com/laminas/laminas-zendframework-bridge/issues", - "rss": "https://github.com/laminas/laminas-zendframework-bridge/releases.atom", - "source": "https://github.com/laminas/laminas-zendframework-bridge" - }, - "funding": [ + "authors": [ { - "url": "https://funding.communitybridge.org/projects/laminas-project", - "type": "community_bridge" + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" } ], - "time": "2021-06-24T12:49:22+00:00" + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2022-05-16T17:09:47+00:00" }, { "name": "monolog/monolog", - "version": "2.3.2", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "71312564759a7db5b789296369c1a264efc43aad" + "reference": "720488632c590286b88b80e62aa3d3d551ad4a50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/71312564759a7db5b789296369c1a264efc43aad", - "reference": "71312564759a7db5b789296369c1a264efc43aad", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/720488632c590286b88b80e62aa3d3d551ad4a50", + "reference": "720488632c590286b88b80e62aa3d3d551ad4a50", "shasum": "" }, "require": { "php": ">=7.2", - "psr/log": "^1.0.1" + "psr/log": "^1.0.1 || ^2.0 || ^3.0" }, "provide": { - "psr/log-implementation": "1.0.0" + "psr/log-implementation": "1.0.0 || 2.0.0 || 3.0.0" }, "require-dev": { "aws/aws-sdk-php": "^2.4.9 || ^3.0", "doctrine/couchdb": "~1.0@dev", - "elasticsearch/elasticsearch": "^7", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", "graylog2/gelf-php": "^1.4.2", + "guzzlehttp/guzzle": "^7.4", + "guzzlehttp/psr7": "^2.2", "mongodb/mongodb": "^1.8", - "php-amqplib/php-amqplib": "~2.4", - "php-console/php-console": "^3.1.3", - "phpspec/prophecy": "^1.6.1", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpspec/prophecy": "^1.15", "phpstan/phpstan": "^0.12.91", - "phpunit/phpunit": "^8.5", - "predis/predis": "^1.1", - "rollbar/rollbar": "^1.3", - "ruflin/elastica": ">=0.90 <7.0.1", - "swiftmailer/swiftmailer": "^5.3|^6.0" + "phpunit/phpunit": "^8.5.14", + "predis/predis": "^1.1 || ^2.0", + "rollbar/rollbar": "^1.3 || ^2 || ^3", + "ruflin/elastica": "^7", + "swiftmailer/swiftmailer": "^5.3|^6.0", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" }, "suggest": { "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", "doctrine/couchdb": "Allow sending log messages to a CouchDB server", "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", "ext-mbstring": "Allow to work properly with unicode symbols", "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", - "php-console/php-console": "Allow sending log messages to Google Chrome", "rollbar/rollbar": "Allow sending log messages to Rollbar", "ruflin/elastica": "Allow sending log messages to an Elastic Search server" }, @@ -513,7 +548,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/2.3.2" + "source": "https://github.com/Seldaek/monolog/tree/2.8.0" }, "funding": [ { @@ -525,7 +560,7 @@ "type": "tidelift" } ], - "time": "2021-07-23T07:42:52+00:00" + "time": "2022-07-24T11:55:47+00:00" }, { "name": "nikic/fast-route", @@ -549,12 +584,12 @@ }, "type": "library", "autoload": { - "psr-4": { - "FastRoute\\": "src/" - }, "files": [ "src/functions.php" - ] + ], + "psr-4": { + "FastRoute\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -579,16 +614,16 @@ }, { "name": "nyholm/psr7", - "version": "1.4.1", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/Nyholm/psr7.git", - "reference": "2212385b47153ea71b1c1b1374f8cb5e4f7892ec" + "reference": "f734364e38a876a23be4d906a2a089e1315be18a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Nyholm/psr7/zipball/2212385b47153ea71b1c1b1374f8cb5e4f7892ec", - "reference": "2212385b47153ea71b1c1b1374f8cb5e4f7892ec", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/f734364e38a876a23be4d906a2a089e1315be18a", + "reference": "f734364e38a876a23be4d906a2a089e1315be18a", "shasum": "" }, "require": { @@ -640,7 +675,7 @@ ], "support": { "issues": "https://github.com/Nyholm/psr7/issues", - "source": "https://github.com/Nyholm/psr7/tree/1.4.1" + "source": "https://github.com/Nyholm/psr7/tree/1.5.1" }, "funding": [ { @@ -652,7 +687,7 @@ "type": "github" } ], - "time": "2021-07-02T08:32:20+00:00" + "time": "2022-06-22T07:13:36+00:00" }, { "name": "nyholm/psr7-server", @@ -720,83 +755,18 @@ ], "time": "2021-05-12T11:11:27+00:00" }, - { - "name": "opis/closure", - "version": "3.6.2", - "source": { - "type": "git", - "url": "https://github.com/opis/closure.git", - "reference": "06e2ebd25f2869e54a306dda991f7db58066f7f6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/opis/closure/zipball/06e2ebd25f2869e54a306dda991f7db58066f7f6", - "reference": "06e2ebd25f2869e54a306dda991f7db58066f7f6", - "shasum": "" - }, - "require": { - "php": "^5.4 || ^7.0 || ^8.0" - }, - "require-dev": { - "jeremeamia/superclosure": "^2.0", - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.6.x-dev" - } - }, - "autoload": { - "psr-4": { - "Opis\\Closure\\": "src/" - }, - "files": [ - "functions.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marius Sarca", - "email": "marius.sarca@gmail.com" - }, - { - "name": "Sorin Sarca", - "email": "sarca_sorin@hotmail.com" - } - ], - "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.", - "homepage": "https://opis.io/closure", - "keywords": [ - "anonymous functions", - "closure", - "function", - "serializable", - "serialization", - "serialize" - ], - "support": { - "issues": "https://github.com/opis/closure/issues", - "source": "https://github.com/opis/closure/tree/3.6.2" - }, - "time": "2021-04-09T13:42:10+00:00" - }, { "name": "php-di/invoker", - "version": "2.3.2", + "version": "2.3.3", "source": { "type": "git", "url": "https://github.com/PHP-DI/Invoker.git", - "reference": "5214cbe5aad066022cd845dbf313f0e47aed928f" + "reference": "cd6d9f267d1a3474bdddf1be1da079f01b942786" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/5214cbe5aad066022cd845dbf313f0e47aed928f", - "reference": "5214cbe5aad066022cd845dbf313f0e47aed928f", + "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/cd6d9f267d1a3474bdddf1be1da079f01b942786", + "reference": "cd6d9f267d1a3474bdddf1be1da079f01b942786", "shasum": "" }, "require": { @@ -830,7 +800,7 @@ ], "support": { "issues": "https://github.com/PHP-DI/Invoker/issues", - "source": "https://github.com/PHP-DI/Invoker/tree/2.3.2" + "source": "https://github.com/PHP-DI/Invoker/tree/2.3.3" }, "funding": [ { @@ -838,25 +808,25 @@ "type": "github" } ], - "time": "2021-07-30T15:05:32+00:00" + "time": "2021-12-13T09:22:56+00:00" }, { "name": "php-di/php-di", - "version": "6.3.4", + "version": "6.4.0", "source": { "type": "git", "url": "https://github.com/PHP-DI/PHP-DI.git", - "reference": "f53bcba06ab31b18e911b77c039377f4ccd1f7a5" + "reference": "ae0f1b3b03d8b29dff81747063cbfd6276246cc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/f53bcba06ab31b18e911b77c039377f4ccd1f7a5", - "reference": "f53bcba06ab31b18e911b77c039377f4ccd1f7a5", + "url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/ae0f1b3b03d8b29dff81747063cbfd6276246cc4", + "reference": "ae0f1b3b03d8b29dff81747063cbfd6276246cc4", "shasum": "" }, "require": { - "opis/closure": "^3.5.5", - "php": ">=7.2.0", + "laravel/serializable-closure": "^1.0", + "php": ">=7.4.0", "php-di/invoker": "^2.0", "php-di/phpdoc-reader": "^2.0.1", "psr/container": "^1.0" @@ -865,12 +835,12 @@ "psr/container-implementation": "^1.0" }, "require-dev": { - "doctrine/annotations": "~1.2", + "doctrine/annotations": "~1.10", "friendsofphp/php-cs-fixer": "^2.4", "mnapoli/phpunit-easymock": "^1.2", - "ocramius/proxy-manager": "^2.0.2", + "ocramius/proxy-manager": "^2.11.2", "phpstan/phpstan": "^0.12", - "phpunit/phpunit": "^8.5|^9.0" + "phpunit/phpunit": "^9.5" }, "suggest": { "doctrine/annotations": "Install it if you want to use annotations (version ~1.2)", @@ -878,12 +848,12 @@ }, "type": "library", "autoload": { - "psr-4": { - "DI\\": "src/" - }, "files": [ "src/functions.php" - ] + ], + "psr-4": { + "DI\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -902,7 +872,7 @@ ], "support": { "issues": "https://github.com/PHP-DI/PHP-DI/issues", - "source": "https://github.com/PHP-DI/PHP-DI/tree/6.3.4" + "source": "https://github.com/PHP-DI/PHP-DI/tree/6.4.0" }, "funding": [ { @@ -914,7 +884,7 @@ "type": "tidelift" } ], - "time": "2021-06-10T08:04:48+00:00" + "time": "2022-04-09T16:46:38+00:00" }, { "name": "php-di/phpdoc-reader", @@ -1014,20 +984,20 @@ }, { "name": "psr/container", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", - "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", "shasum": "" }, "require": { - "php": ">=7.2.0" + "php": ">=7.4.0" }, "type": "library", "autoload": { @@ -1056,9 +1026,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.1" + "source": "https://github.com/php-fig/container/tree/1.1.2" }, - "time": "2021-03-05T17:36:06+00:00" + "time": "2021-11-05T16:50:12+00:00" }, { "name": "psr/http-factory", @@ -1284,30 +1254,30 @@ }, { "name": "psr/log", - "version": "1.1.4", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Log\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1328,9 +1298,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" + "source": "https://github.com/php-fig/log/tree/3.0.0" }, - "time": "2021-05-03T11:20:27+00:00" + "time": "2021-07-14T16:46:02+00:00" }, { "name": "ralouphie/getallheaders", @@ -1431,25 +1401,25 @@ }, { "name": "slim/psr7", - "version": "1.4", + "version": "1.5", "source": { "type": "git", "url": "https://github.com/slimphp/Slim-Psr7.git", - "reference": "0dca983ca32a26f4a91fb11173b7b9eaee29e9d6" + "reference": "a47b43a8da7c0208b4c228af0cb29ea36080635a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim-Psr7/zipball/0dca983ca32a26f4a91fb11173b7b9eaee29e9d6", - "reference": "0dca983ca32a26f4a91fb11173b7b9eaee29e9d6", + "url": "https://api.github.com/repos/slimphp/Slim-Psr7/zipball/a47b43a8da7c0208b4c228af0cb29ea36080635a", + "reference": "a47b43a8da7c0208b4c228af0cb29ea36080635a", "shasum": "" }, "require": { "fig/http-message-util": "^1.1.5", - "php": "^7.2 || ^8.0", + "php": "^7.3 || ^8.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.0", - "ralouphie/getallheaders": "^3", - "symfony/polyfill-php80": "^1.22" + "ralouphie/getallheaders": "^3.0", + "symfony/polyfill-php80": "^1.23" }, "provide": { "psr/http-factory-implementation": "1.0", @@ -1460,10 +1430,11 @@ "ext-json": "*", "http-interop/http-factory-tests": "^0.9.0", "php-http/psr7-integration-tests": "dev-master", - "phpstan/phpstan": "^0.12", - "phpunit/phpunit": "^8.5 || ^9.5", - "squizlabs/php_codesniffer": "^3.6", - "weirdan/prophecy-shim": "^1.0 || ^2.0.2" + "phpspec/prophecy": "^1.14", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/phpstan": "^0.12.99", + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.6" }, "type": "library", "autoload": { @@ -1506,50 +1477,51 @@ ], "support": { "issues": "https://github.com/slimphp/Slim-Psr7/issues", - "source": "https://github.com/slimphp/Slim-Psr7/tree/1.4" + "source": "https://github.com/slimphp/Slim-Psr7/tree/1.5" }, - "time": "2021-05-08T18:22:56+00:00" + "time": "2021-09-22T04:33:00+00:00" }, { "name": "slim/slim", - "version": "4.8.1", + "version": "4.10.0", "source": { "type": "git", "url": "https://github.com/slimphp/Slim.git", - "reference": "c8934c35d9d98b1a1df9f99ee69b77a59e0aa820" + "reference": "0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim/zipball/c8934c35d9d98b1a1df9f99ee69b77a59e0aa820", - "reference": "c8934c35d9d98b1a1df9f99ee69b77a59e0aa820", + "url": "https://api.github.com/repos/slimphp/Slim/zipball/0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0", + "reference": "0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0", "shasum": "" }, "require": { "ext-json": "*", "nikic/fast-route": "^1.3", - "php": "^7.2 || ^8.0", + "php": "^7.4 || ^8.0", "psr/container": "^1.0 || ^2.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.0", "psr/http-server-handler": "^1.0", "psr/http-server-middleware": "^1.0", - "psr/log": "^1.1" + "psr/log": "^1.1 || ^2.0 || ^3.0" }, "require-dev": { "adriansuter/php-autoload-override": "^1.2", "ext-simplexml": "*", - "guzzlehttp/psr7": "^1.8", - "http-interop/http-factory-guzzle": "^1.0", - "laminas/laminas-diactoros": "^2.4", - "nyholm/psr7": "^1.4", - "nyholm/psr7-server": "^1.0.1", - "phpspec/prophecy": "^1.13", - "phpstan/phpstan": "^0.12.85", - "phpunit/phpunit": "^8.5.13 || ^9.3.8", + "guzzlehttp/psr7": "^2.1", + "httpsoft/http-message": "^1.0", + "httpsoft/http-server-request": "^1.0", + "laminas/laminas-diactoros": "^2.8", + "nyholm/psr7": "^1.5", + "nyholm/psr7-server": "^1.0", + "phpspec/prophecy": "^1.15", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9.5", "slim/http": "^1.2", - "slim/psr7": "^1.3", - "squizlabs/php_codesniffer": "^3.6", - "weirdan/prophecy-shim": "^1.0 || ^2.0.2" + "slim/psr7": "^1.5", + "squizlabs/php_codesniffer": "^3.6" }, "suggest": { "ext-simplexml": "Needed to support XML format in BodyParsingMiddleware", @@ -1622,35 +1594,37 @@ "type": "tidelift" } ], - "time": "2021-06-29T19:41:06+00:00" + "time": "2022-03-14T14:18:23+00:00" }, { "name": "slim/slim-skeleton", - "version": "4.3.0", + "version": "4.4.0", "source": { "type": "git", "url": "https://github.com/slimphp/Slim-Skeleton.git", - "reference": "e3927652b1f008f1808b6fc2e9ac55f1b71021da" + "reference": "8a91d18b0a8e4623f1af9207e97477c7b8437f57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slimphp/Slim-Skeleton/zipball/e3927652b1f008f1808b6fc2e9ac55f1b71021da", - "reference": "e3927652b1f008f1808b6fc2e9ac55f1b71021da", + "url": "https://api.github.com/repos/slimphp/Slim-Skeleton/zipball/8a91d18b0a8e4623f1af9207e97477c7b8437f57", + "reference": "8a91d18b0a8e4623f1af9207e97477c7b8437f57", "shasum": "" }, "require": { "ext-json": "*", - "monolog/monolog": "^2.2", - "php": "^7.2 || ^8.0", + "monolog/monolog": "^2.3", + "php": "^7.4 || ^8.0", "php-di/php-di": "^6.3", - "slim/psr7": "^1.3", - "slim/slim": "^4.7" + "slim/psr7": "^1.5", + "slim/slim": "^4.9" }, "require-dev": { - "jangregor/phpstan-prophecy": "^0.8.1", + "jangregor/phpstan-prophecy": "^1.0.0", + "phpspec/prophecy-phpunit": "^2.0", "phpstan/extension-installer": "^1.1.0", - "phpstan/phpstan": "^0.12.80", - "phpunit/phpunit": "^8.0 || ^9.0" + "phpstan/phpstan": "^1.3", + "phpunit/phpunit": "^9.5.11", + "squizlabs/php_codesniffer": "^3.6" }, "type": "library", "autoload": { @@ -1684,22 +1658,22 @@ ], "support": { "issues": "https://github.com/slimphp/Slim-Skeleton/issues", - "source": "https://github.com/slimphp/Slim-Skeleton/tree/4.3.0" + "source": "https://github.com/slimphp/Slim-Skeleton/tree/4.4.0" }, - "time": "2021-04-26T02:48:52+00:00" + "time": "2022-01-14T19:30:57+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.23.1", + "version": "v1.26.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be" + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be", - "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", "shasum": "" }, "require": { @@ -1708,7 +1682,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.26-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1716,12 +1690,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, "files": [ "bootstrap.php" ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, "classmap": [ "Resources/stubs" ] @@ -1753,7 +1727,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" }, "funding": [ { @@ -1769,7 +1743,7 @@ "type": "tidelift" } ], - "time": "2021-07-28T13:41:28+00:00" + "time": "2022-05-10T07:21:04+00:00" } ], "packages-dev": [], @@ -1779,8 +1753,9 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "ext-pdo": "*" + "ext-pdo": "*", + "ext-json": "*" }, "platform-dev": [], - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } diff --git a/dist/api/.htaccess b/dist/api/.htaccess new file mode 100644 index 0000000..11b28de --- /dev/null +++ b/dist/api/.htaccess @@ -0,0 +1,4 @@ +RewriteEngine On +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^ index.php [QSA,L] \ No newline at end of file diff --git a/dist/api/index.php b/dist/api/index.php index 5ddbc95..006b5d2 100644 --- a/dist/api/index.php +++ b/dist/api/index.php @@ -1,4 +1,5 @@ add(new SameSiteCookieMiddleware($ssConfig)); // for error checking $errorMiddleware = $app->addErrorMiddleware(true, true, true); +// set base path for all routes $app->setBasePath("/api"); +// return all responses as JSON +$app->add(function($request, $handler) { + $response = $handler->handle($request); + return $response->withHeader('Content-Type', 'application/json'); +}); + $timelineData = new timelineData(); $projectData = new projectData(); +$user = new user(); $app->get("/timelineData/{timeline}", function (Request $request, Response $response, array $args) { global $timelineData; - $json = $result = ""; //check if route is available if it is get the data //otherwise return an error if($args["timeline"] == "edu") { - $result = $timelineData->getEduData(); + return $response->getBody()->write(json_encode($timelineData->getEduData())); } - else if($args["timeline"] == "work") + + if($args["timeline"] == "work") { - $result = $timelineData->getWorkData(); + return $response->getBody()->write(json_encode($timelineData->getWorkData())); } - else - { - $result = array(array("errorMessage" => "Error, timeline data not found")); - } - - $json = json_encode($result); - - $response->getBody()->write($json); - //if it is an error give a 404 code since it can't find the data - if(array_key_exists("errorMessage", $result)) - { - $response = $response->withStatus(404); - } - - //use content type json to indicate json data on frontend. - return $response->withHeader("Content-Type", "application/json"); + + // something went wrong + $response->getBody()->write(json_encode(array("errorMessage" => "Error, timeline data not found"))); + return $response->withStatus(404); }); $app->get('/projectData', function (Request $request, Response $response) @@ -83,7 +83,7 @@ $app->get('/projectData', function (Request $request, Response $response) } //use content type json to indicate json data on frontend. - return $response->withHeader("Content-Type", "application/json"); + return $response; }); $app->post('/contact', function (Request $request, Response $response) @@ -92,20 +92,19 @@ $app->post('/contact', function (Request $request, Response $response) if(empty($data["fName"]) || empty($data["lName"]) || empty($data["email"]) || empty($data["subject"]) || empty($data["message"])) { $response->getBody()->write(json_encode(array("errorMessage" => "Please fill out all the fields"))); - $response = $response->withStatus(400); - return $response->withHeader("Content-Type", "application/json"); + return $response->withStatus(400); } if (!filter_var($data["email"], FILTER_VALIDATE_EMAIL)) { $response->getBody()->write(json_encode(array("errorMessage" => "Email is not the correct format"))); $response = $response->withStatus(400); - return $response->withHeader("Content-Type", "application/json"); + return $response; } // email form filler/conatcter - $headers1 = "From: noreply@rohitpai.tech\r\n"; - $headers1 .= "Reply-To: rohit@rohitpai.tech\r\n"; + $headers1 = "From: noreply@rohitpai.co.uk\r\n"; + $headers1 .= "Reply-To: rohit@rohitpai.co.uk\r\n"; $headers1 .= "MIME-Version: 1.0\r\n"; $headers1 .= "Content-Type: text/html; charset=UTF-8\r\n"; @@ -176,7 +175,7 @@ $app->post('/contact', function (Request $request, Response $response)

-

Regards,
Rohit Pai
rohit@rohitpai.tech +

Regards,
Rohit Pai
rohit@rohitpai.co.uk "; @@ -184,7 +183,7 @@ $app->post('/contact', function (Request $request, Response $response) mail($data["email"], $data["subject"], $message1, $headers1); // email to me - $headers2 = "From: noreply@rohitpai.tech\r\n"; + $headers2 = "From: noreply@rohitpai.co.uk\r\n"; $headers2 .= "Reply-To: {$data['email']}\r\n"; $headers2 .= "MIME-Version: 1.0\r\n"; $headers2 .= "Content-Type: text/html; charset=UTF-8\r\n"; @@ -256,8 +255,51 @@ $app->post('/contact', function (Request $request, Response $response) "; - mail("rohit@rohitpai.tech", "{$data['fName']} {$data['lName']} filled in the form", $message2, $headers2); + mail("rohit@rohitpai.co.uk", "{$data['fName']} {$data['lName']} filled in the form", $message2, $headers2); return $response->withStatus(201); }); -$app->run(); \ No newline at end of file +$app->post('/user/login', function (Request $request, Response $response) { + + global $user; + + // get request data + $data = $request->getParsedBody(); + + if (empty($data["username"]) || empty($data["password"])) + { + // uh oh user sent empty data + return $response->withStatus(400); + } + + if ($user->checkUser($data["username"], $data["password"])) + { + // yay user is logged in + $_SESSION["token"] = $user->createToken(); + $_SESSION["username"] = $data["username"]; + return $response; + } + return $response->withStatus(401); +}); + +$app->get('/user/isLoggedIn', function (Request $request, Response $response) { + + global $user; + + if (empty($_SESSION["token"]) && empty($_SESSION["username"])) + { + // uh oh user not logged in + return $response->withStatus(401); + } + + if (empty($_SESSION["token"])) + { + // user is logged in but no token was created + $_SESSION["token"] = $user->createToken(); + return $response; + } + + return $response->getBody()->write(json_encode(array("token" => $_SESSION["token"]))); +}); + +$app->run(); diff --git a/dist/api/projectData.php b/dist/api/projectData.php index 9125f2a..fc5bf68 100644 --- a/dist/api/projectData.php +++ b/dist/api/projectData.php @@ -10,7 +10,7 @@ require_once "./config.php"; */ class projectData { - function getProjectData() + function getProjectData(): array { $conn = dbConn(); $stmt = $conn->prepare("SELECT title, isMainProject, information, imgLocation, projectLink, githubLink FROM projects order by date LIMIT 4;"); @@ -23,9 +23,6 @@ class projectData { return $result; } - else - { - return array(array("errorMessage" => "Error, project data not found")); - } + return array("errorMessage" => "Error, project data not found"); } -} \ No newline at end of file +} diff --git a/dist/api/timelineData.php b/dist/api/timelineData.php index 5686f13..67eb608 100644 --- a/dist/api/timelineData.php +++ b/dist/api/timelineData.php @@ -10,7 +10,7 @@ require_once "./config.php"; */ class timelineData { - function getEduData() + function getEduData(): array { $conn = dbConn(); $stmt = $conn->prepare("SELECT DATE_FORMAT(startPeriod, '%b, %Y') as startPeriod, DATE_FORMAT(endPeriod, '%b, %Y') as endPeriod, grade, course FROM edu ORDER BY startPeriod DESC;"); @@ -23,13 +23,10 @@ class timelineData { return $result; } - else - { - return array("errorMessage" => "Error, edu data not found"); - } + return array("errorMessage" => "Error, edu data not found"); } - function getWorkData() + function getWorkData(): array { $conn = dbConn(); $stmt = $conn->prepare("SELECT DATE_FORMAT(startPeriod, '%b, %Y') as startPeriod, DATE_FORMAT(endPeriod, '%b, %Y') as endPeriod, companyName, area, title FROM work ORDER BY work.startPeriod DESC;"); @@ -42,10 +39,7 @@ class timelineData { return $result; } - else - { - return array("errorMessage" => "Error, work data not found"); - } + return array("errorMessage" => "Error, work data not found"); } diff --git a/dist/api/user.php b/dist/api/user.php new file mode 100644 index 0000000..d47c3cd --- /dev/null +++ b/dist/api/user.php @@ -0,0 +1,38 @@ +prepare("SELECT * FROM users WHERE username = :username"); + $stmt->bindParam(":username", $username); + $stmt->execute(); + + // set the resulting array to associative + $result = $stmt->fetchAll(PDO::FETCH_ASSOC); + + if ($result) + { + if (password_verify($password, $result[0]["password"])) + { + return true; + } + return false; + } + return false; + } + + function createToken(): string + { + return uniqid("rpe-"); + } +} \ No newline at end of file diff --git a/dist/css/main.css b/dist/css/main.css index efba8f6..16c0e04 100644 --- a/dist/css/main.css +++ b/dist/css/main.css @@ -1 +1 @@ -/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}html{scroll-behavior:smooth}body{font-family:Noto Sans KR,sans-serif;font-style:normal;font-weight:500;font-size:var(--generalFS);line-height:1.625rem}a:visited{color:inherit}h1,nav{font-family:Share Tech Mono,monospace;font-style:normal;font-weight:400;font-size:var(--titleFS);line-height:2.5625rem;text-transform:lowercase}h2{font-family:Noto Sans KR,sans-serif;font-style:normal;font-weight:500;font-size:var(--headingFS);line-height:2.1875rem}a.btn,form input[type=submit]{text-decoration:none;display:inline-block;padding:1rem 2rem;border-radius:.625em;border:.3215em solid var(--primaryDefault);color:#fff;text-align:center}a.btn:hover,form input[type=submit]:hover{border:.3215em solid var(--primaryHover)}a.btnPrimary,form input[type=submit]{background:var(--primaryDefault);cursor:pointer}a.btnOutline{background:#fff;color:var(--primaryDefault)}a.btnPrimary[disabled]{pointer-events:none;background:var(--notAvailableDefault);border:.3215em solid var(--notAvailableDefault)}a.btnPrimary[disabled]:hover{background:var(--notAvailableHover);border:.3215em solid var(--notAvailableHover)}a.btnPrimary:hover,form input[type=submit]:hover{background:var(--primaryHover)}a.btn:active,form input[type=submit]:active{padding:.8rem 1.8rem}.boxShadowOut:hover{box-shadow:0 6px 4px 0 var(--mutedBlack)}.boxShadowIn:active{box-shadow:inset 0 6px 4px 0 var(--mutedBlack)}.textShadow:hover{text-shadow:0 6px 4px var(--mutedBlack)}section#about,section#curriculumVitae h1{padding:0 5rem}header{background:#6a6a6a url(../imgs/hero.jpg) no-repeat bottom;background-size:cover;height:40%;color:#fff;backdrop-filter:grayscale(100%);position:relative}nav{display:flex;flex-direction:row;justify-content:space-between;padding:.25em;position:fixed;top:0;width:100%;transition:background-color .4s ease-in}nav.scrolled{background-color:var(--navBack);z-index:1}nav #nav-check{display:none}nav .nav-btn{display:none}nav h1{margin:0}nav a{text-decoration:none;color:#fff}nav ul{display:flex;flex-direction:row;gap:1em;margin:0;justify-content:flex-end;align-items:flex-end}nav ul li{list-style:none}nav ul li span{visibility:hidden}nav ul li .active span,nav ul li a:hover span{visibility:visible}header div{display:flex;flex-direction:column;justify-content:center;align-items:center;padding-top:10em}header div .btn{margin:2em 0}header div button{background:0 0;border:none;display:inline-block;text-align:center;text-decoration:none;font-size:2rem;cursor:pointer}i.fa-chevron-down{color:hsla(0,0%,67%,.58);font-size:3.75em;margin:1.5rem 0}div h1 span{visibility:visible;animation:caret 1s steps(1) infinite}@keyframes caret{50%{visibility:hidden}}section#about{margin-bottom:5rem}section#about div{padding:.1em 5em}section#curriculumVitae{background-color:var(--primaryDefault);color:#fff;padding:2em 0}section#curriculumVitae .cvGrid{display:flex;flex-direction:row;padding:0 1.5rem;flex-wrap:wrap}section#curriculumVitae .cvGrid>div{width:45%;display:flex;flex-direction:column;min-height:100%}section#curriculumVitae .cvGrid h2{text-align:center}section#curriculumVitae .timeline{position:relative;max-width:30em;gap:1em;display:flex;flex-direction:column;height:100%}section#curriculumVitae #work{margin:0 auto 0 8rem}section#curriculumVitae .timeline:before{content:"";position:absolute;height:100%;border:4px var(--timelineItemBrdr) solid;right:194px;top:0}section#curriculumVitae .timeline:after{content:"";display:table;clear:both}section#curriculumVitae .timelineItem{border:2px solid var(--timelineItemBrdr);-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;padding:0 1rem;width:50%;position:relative;background-color:var(--primaryHover)}.timelineItem:after,section#curriculumVitae .timelineItem:before{content:'';position:absolute}section#curriculumVitae .timelineItem:before{content:'';right:-20px;top:calc(50% - 5px);border-style:solid;border-color:var(--timelineItemBrdr) var(--timelineItemBrdr) transparent transparent;border-width:20px;transform:rotate(45deg)}section#curriculumVitae .timelineItem:nth-child(2n){margin-left:21em}section#curriculumVitae .timelineItem:nth-child(2n):before{right:auto;left:-20px;border-color:transparent transparent var(--timelineItemBrdr) var(--timelineItemBrdr)}section#curriculumVitae .timelineItem h3{font-weight:400}section#curriculumVitae .timelineItem span{color:#e5e5e5}section#projects{display:flex;flex-direction:row;padding:0 2.5rem;border-bottom:2px solid var(--mutedGrey)}section#projects .mainProj,section#projects .otherProj{width:50%;display:flex;flex-direction:column;align-items:center;gap:1em}section#projects .mainProj{border-right:2px solid var(--mutedGrey);padding:0 2.5em 5em 0}section#projects .mainProj img,section#projects .otherProj .oProjItem img{-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;display:block;margin:1em auto}section#projects .mainProj img{width:100%;max-width:40rem}section#projects .mainProj .flexRow{display:flex;flex-direction:row;gap:4em}section#projects .mainProj .flexCol{display:flex;flex-direction:column;gap:2.5em}section#projects .otherProj>a{margin:5rem 0}section#projects .otherProj>div{display:flex;flex-direction:column;gap:2em}section#projects .otherProj>div .oProjItem{display:flex;justify-content:center;align-items:center;flex-direction:row;margin:0 auto;width:90%;border:1px solid var(--grey);gap:1em;box-shadow:0 6px 4px 0 var(--mutedBlack);-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;padding:0 1em}section#projects .otherProj>div .oProjItem:nth-child(2){flex-direction:row-reverse}section#projects .otherProj .oProjItem img{max-width:15rem;width:100%;padding:0 1em}section#projects .oProjItem .flexCol div:nth-child(2){display:flex;flex-direction:row;justify-content:flex-start;gap:3em;margin-left:2em}section#projects .flexCol div:nth-child(2) .btn{padding:.25em .5em}section#contact{display:flex;flex-direction:row;padding:0 2.5em}div#findMe,div#sayHello{width:50%;display:flex;flex-direction:column;align-items:center;gap:1em}div#findMe .findMeContainer{display:flex;flex-direction:row;justify-content:space-around;align-items:center;gap:2em;width:100%;height:100%;margin:5em 0}div#findMe .findMeContainer .profile{-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%}div#findMe .socialIcons{display:flex;flex-direction:row;justify-content:center;align-items:center;gap:2em}div#findMe .socialIcons div{display:flex;flex-direction:column;gap:1.5em}div#findMe .socialIcons div svg{width:2.5em;fill:var(--primaryDefault);font-size:2em}div#findMe .socialIcons div a:hover svg{fill:var(--primaryHover)}div#sayHello #contactForm{display:flex;flex-direction:column;justify-content:center;align-items:center}#contactForm .flName{display:flex;flex-direction:row;gap:1em}#contactForm .formControl{width:100%}#contactForm input[type=submit]{margin-top:1em;align-self:flex-start}#contactForm .formControl input:not([type=submit]),#contactForm .formControl textarea{width:100%;border:4px solid var(--primaryDefault);background:0 0;outline:0;-webkit-border-radius:1em;-moz-border-radius:1em;border-radius:.5em;padding:0 .5em}#contactForm .formControl textarea{padding:.5em}#contactForm .formControl input:not([type=submit]).invalid:invalid,#contactForm .formControl textarea.invalid:invalid{border:4px solid var(--errorDefault)}#contactForm .formControl input:not([type=submit]).invalid:invalid:focus,#contactForm .formControl textarea.invalid:invalid:focus{border:4px solid var(--errorHover);box-shadow:0 4px 2px 0 var(--mutedBlack)}#contactForm .formControl input:not([type=submit]):focus,#contactForm .formControl textarea:focus{border:4px solid var(--primaryHover)}#contactForm .formControl input:not([type=submit]){height:3em}#contactForm .flName{display:flex;flex-direction:row;gap:1em}#contactForm .formControl{width:100%}#contactForm input[type=submit]{margin-top:1em;align-self:flex-start}#contactForm .formControl input:not([type=submit]),#contactForm .formControl textarea{width:100%;border:4px solid var(--primaryDefault);background:0 0;outline:0;-webkit-border-radius:1em;-moz-border-radius:1em;border-radius:.5em;padding:0 .5em}#contactForm .formControl textarea{padding:.5em}#contactForm .formControl input:not([type=submit]).invalid:invalid,#contactForm .formControl textarea.invalid:invalid{border:4px solid var(--errorDefault)}#contactForm .formControl input:not([type=submit]).invalid:invalid:focus,#contactForm .formControl textarea.invalid:invalid:focus{border:4px solid var(--errorHover);box-shadow:0 4px 2px 0 var(--mutedBlack)}#contactForm .formControl input:not([type=submit]):focus,#contactForm .formControl textarea:focus{border:4px solid var(--primaryHover)}#contactForm .formControl input:not([type=submit]){height:3em}div.message{background:var(--primaryDefault);color:#fff;padding:.5em .8em;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;display:flex;justify-content:center;align-items:center;align-self:flex-start;flex-direction:row-reverse;position:relative;height:75px;visibility:visible;overflow:hidden;-webkit-transition:all .5s ease-in-out;-moz-transition:all .5s ease-in-out;-ms-transition:all .5s ease-in-out;-o-transition:all .5s ease-in-out;transition:all .5s ease-in-out;opacity:1;margin-top:1em}div.message.error{background:var(--errorDefault)}div.message button{border:none;background:0 0;outline:0;cursor:pointer;color:#fff;font-size:1.25rem;margin-top:-5px;position:absolute;transform:translate(0,0);transform-origin:0 0;right:10px;top:10px}div.message.hidden{opacity:0;visibility:hidden;height:0}div.message button:hover{text-shadow:-1px 2px var(--mutedBlack)}footer{background-color:var(--primaryDefault);margin-top:5em;padding:2em;display:flex;color:#fff}footer .spacer{width:100%;margin-right:auto}footer p{margin:auto;width:100%;text-align:center}footer .button{margin-left:auto;width:100%;text-align:center}footer .button button{border:5px solid #fff;background:0 0;font-size:3em;padding:.5rem 1rem;width:2em;color:#fff;-webkit-border-radius:.25em;-moz-border-radius:.25em;border-radius:.25em;cursor:pointer}:root{--mainHue:79;--mainSat:62%;--mainLight:51%;--primaryDefault:hsla(var(--mainHue), var(--mainSat), var(--mainLight), 1);--primaryHover:hsla(var(--mainHue), var(--mainSat), calc(var(--mainLight) - 10%), 1);--timelineItemBrdr:hsla(var(--mainHue), var(--mainSat), calc(var(--mainLight) - 20%), 1);--errorDefault:hsla(0, calc(var(--mainSat) + 10%),calc(var(--mainLight) + 10%), 1);--errorHover:hsla(0, calc(var(--mainSat) + 10%), calc(var(--mainLight) - 10%), 1);--grey:hsla(0, 0%, 39%, 1);--notAvailableDefault:hsla(0, 0%, 39%, 1);--notAvailableHover:hsla(0, 0%,32%, 1);--mutedGrey:hsla(0, 0%, 78%, 1);--mutedBlack:hsla(0, 0%, 0%, 0.25);--navBack:hsla(0, 0%, 30%, 1);--titleFS:2.25rem;--generalFS:1.125rem;--headingFS:1.5rem}*{box-sizing:border-box}@media screen and (max-width:90em){section#curriculumVitae .cvGrid{flex-direction:column;justify-content:center;align-items:center}section#curriculumVitae .cvGrid>div{width:100%}section#curriculumVitae .cvGrid>div:first-child{padding-bottom:2.5em;margin-bottom:2.5em;border-bottom:5px #fff solid}section#curriculumVitae .cvGrid h2{margin-left:5em}section#curriculumVitae .cvGrid .timeline{margin:0 auto}}@media screen and (max-width:75em){section#about,section#curriculumVitae h1{padding:0 1em}nav{display:block;height:50px;width:100%;background-color:var(--navBack);position:fixed;top:0;padding:0}nav a h1{margin-left:1ch}nav .nav-btn{display:inline-block;position:absolute;right:75px;top:-360px}nav ul{position:fixed;display:block;width:100%;background-color:#333;transition:all .4s ease-in;overflow-y:hidden;padding-left:0;margin-top:7px}nav ul li a{display:block;width:100%;transform:translateX(-30px);transition:all .4s ease-in;opacity:0}.nav-btn label{display:inline-block;cursor:pointer;width:60px;height:50px;position:fixed;-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);transform:rotate(0);-webkit-transition:.5s ease-in;-moz-transition:.5s ease-in;-o-transition:.5s ease-in;transition:.5s ease-in}.nav-btn label span{display:block;position:absolute;height:5px;width:100%;background-color:#fff;opacity:1;right:0;top:20px;-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);transform:rotate(0);-webkit-transition:.25s ease-in;-moz-transition:.25s ease-in;-o-transition:.25s ease-in;transition:.25s ease-in}nav #nav-check:not(:checked)~ul{height:auto;max-height:0}nav #nav-check:not(:checked)~.nav-btn label span:nth-child(1){top:8px;-webkit-transform-origin:left center;-moz-transform-origin:left center;-o-transform-origin:left center;transform-origin:left center}nav #nav-check:not(:checked)~.nav-btn label span:nth-child(2){top:23px;-webkit-transform-origin:left center;-moz-transform-origin:left center;-o-transform-origin:left center;transform-origin:left center}nav #nav-check:not(:checked)~.nav-btn label span:nth-child(3){top:38px;-webkit-transform-origin:left center;-moz-transform-origin:left center;-o-transform-origin:left center;transform-origin:left center}nav #nav-check:checked~.nav-btn label,nav .nav-btn label:hover{background-color:rgba(-1,0,0,.3)}nav #nav-check:checked~ul{max-height:50vh;overflow-y:hidden}nav #nav-check:checked~ul li a{opacity:1;transform:translateX(0)}nav #nav-check:checked~ul li:nth-child(1) a{transition-delay:.15s}nav #nav-check:checked~ul li:nth-child(2) a{transition-delay:.25s}nav #nav-check:checked~ul li:nth-child(3) a{transition-delay:.35s}nav #nav-check:checked~ul li:nth-child(4) a{transition-delay:.45s}nav #nav-check:checked~ul li:nth-child(5) a{transition-delay:.55s}nav #nav-check:checked~.nav-btn label span:first-child{-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}nav #nav-check:checked~.nav-btn label span:nth-child(2){width:0;opacity:0}nav #nav-check:checked~.nav-btn label span:last-child{-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg)}section#about div{padding:.1em 2.5em}section#curriculumVitae .cvGrid{padding:0}section#projects{flex-direction:column;justify-content:center;align-items:center}section#projects .mainProj{border-right:0;padding:0;width:100%;margin:0 5em}section#projects .mainProj img{padding:0 1em}section#projects .mainProj .flexRow{flex-direction:column;margin:0 2.5em}section#projects .mainProj .flexCol{flex-direction:row;justify-content:center;align-items:center}section#projects .otherProj{width:100%}section#projects .otherProj .btn{width:10em;text-align:center}section#projects .otherProj>div .oProjItem,section#projects .otherProj>div .oProjItem:nth-child(2){flex-direction:column}section#projects .oProjItem .flexCol div:nth-child(2){justify-content:center;margin-left:0;margin-bottom:1em}section#projects .otherProj>a{margin-left:3em;margin-right:3em;text-align:center}section#contact{flex-direction:column;justify-content:center;align-items:center}div#findMe,div#sayHello{width:100%}div#findMe .findMeContainer{flex-direction:column;justify-content:center}div#findMe .socialIcons{flex-direction:row}div#findMe .socialIcons div{flex-direction:row}}@media screen and (max-width:55em){section#curriculumVitae .cvGrid .timeline,section#curriculumVitae .cvGrid .timeline#work{margin:0 auto;width:100%}section#curriculumVitae .timeline:before{border:none}section#curriculumVitae .timelineItem,section#curriculumVitae .timelineItem:nth-child(2n){width:95%;padding:0;margin:0 auto}section#curriculumVitae .timelineItem:before{right:unset;left:unset;border:none}div#findMe .socialIcons{flex-direction:column}#contactForm .flName{flex-direction:column;gap:0;width:100%}}@media screen and (max-width:31em){section#about,section#curriculumVitae h1{padding:0 1em}header div h1{text-align:center;height:5.125rem}section#about div{padding:.1em 1em}section#projects .mainProj .flexCol{flex-direction:column}section#projects .oProjItem .flexCol div:nth-child(2){flex-direction:column;justify-content:center;align-items:center}} \ No newline at end of file +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}:root{--mainHue:80;--mainSat:60%;--mainLight:50%;--primaryDefault:hsla(var(--mainHue), var(--mainSat), var(--mainLight), 1);--primaryHover:hsla(var(--mainHue), var(--mainSat), calc(var(--mainLight) - 10%), 1);--timelineItemBrdr:hsla(var(--mainHue), var(--mainSat), calc(var(--mainLight) - 20%), 1);--errorDefault:hsla(0, calc(var(--mainSat) + 10%),calc(var(--mainLight) + 10%), 1);--errorHover:hsla(0, calc(var(--mainSat) + 10%), calc(var(--mainLight) - 10%), 1);--grey:hsla(0, 0%, 39%, 1);--notAvailableDefault:hsla(0, 0%, 39%, 1);--notAvailableHover:hsla(0, 0%,32%, 1);--mutedGrey:hsla(0, 0%, 78%, 1);--mutedBlack:hsla(0, 0%, 0%, 0.25);--navBack:hsla(0, 0%, 30%, 1);--titleFS:2.25rem;--generalFS:1.125rem;--headingFS:1.5rem}*{box-sizing:border-box}html{scroll-behavior:smooth}body{font-family:Noto Sans KR,sans-serif;font-style:normal;font-weight:500;font-size:var(--generalFS);line-height:1.625rem}a:visited{color:inherit}h1,nav{font-family:Share Tech Mono,monospace;font-style:normal;font-weight:400;font-size:var(--titleFS);line-height:2.5625rem;text-transform:lowercase}h2{font-family:Noto Sans KR,sans-serif;font-style:normal;font-weight:500;font-size:var(--headingFS);line-height:2.1875rem}a.btn,form input[type=submit]{text-decoration:none;display:inline-block;padding:1rem 2rem;border-radius:.625em;border:.3215em solid var(--primaryDefault);color:#fff;text-align:center}a.btn:hover,form input[type=submit]:hover{border:.3215em solid var(--primaryHover)}a.btnPrimary,form input[type=submit]{background:var(--primaryDefault);cursor:pointer}a.btnOutline{background:#fff;color:var(--primaryDefault)}a.btnPrimary[disabled]{pointer-events:none;background:var(--notAvailableDefault);border:.3215em solid var(--notAvailableDefault)}a.btnPrimary[disabled]:hover{background:var(--notAvailableHover);border:.3215em solid var(--notAvailableHover)}a.btnPrimary:hover,form input[type=submit]:hover{background:var(--primaryHover)}a.btn:active,form input[type=submit]:active{padding:.8rem 1.8rem}.boxShadowOut:hover{box-shadow:0 6px 4px 0 var(--mutedBlack)}.boxShadowIn:active{box-shadow:inset 0 6px 4px 0 var(--mutedBlack)}.textShadow:hover{text-shadow:0 6px 4px var(--mutedBlack)}form .formControl{width:100%}form input[type=submit]{margin-top:1em;align-self:flex-start}form .formControl input:not([type=submit]),form .formControl textarea{width:100%;border:4px solid var(--primaryDefault);background:0 0;outline:0;-webkit-border-radius:1em;-moz-border-radius:1em;border-radius:.5em;padding:0 .5em}form .formControl textarea{padding:.5em}form .formControl input:not([type=submit]).invalid:invalid,form .formControl textarea.invalid:invalid{border:4px solid var(--errorDefault)}form .formControl input:not([type=submit]).invalid:invalid:focus,form .formControl textarea.invalid:invalid:focus{border:4px solid var(--errorHover);box-shadow:0 4px 2px 0 var(--mutedBlack)}form .formControl input:not([type=submit]):focus,form .formControl textarea:focus{border:4px solid var(--primaryHover)}form .formControl input:not([type=submit]){height:3em}form .formControl{width:100%}form input[type=submit]{margin-top:1em;align-self:flex-start}form .formControl input:not([type=submit]),form .formControl textarea{width:100%;border:4px solid var(--primaryDefault);background:0 0;outline:0;-webkit-border-radius:1em;-moz-border-radius:1em;border-radius:.5em;padding:0 .5em}form .formControl textarea{padding:.5em}form .formControl input:not([type=submit]).invalid:invalid,form .formControl textarea.invalid:invalid{border:4px solid var(--errorDefault)}form .formControl input:not([type=submit]).invalid:invalid:focus,form .formControl textarea.invalid:invalid:focus{border:4px solid var(--errorHover);box-shadow:0 4px 2px 0 var(--mutedBlack)}form .formControl input:not([type=submit]):focus,form .formControl textarea:focus{border:4px solid var(--primaryHover)}form .formControl input:not([type=submit]){height:3em}section#about,section#curriculumVitae h1{padding:0 5rem}header{background:#6a6a6a url(../imgs/hero.jpg) no-repeat bottom;background-size:cover;height:40%;color:#fff;backdrop-filter:grayscale(100%);position:relative}nav{display:flex;flex-direction:row;justify-content:space-between;padding:.25em;position:fixed;top:0;width:100%;transition:background-color .4s ease-in}nav.scrolled{background-color:var(--navBack);z-index:1}nav #nav-check{display:none}nav .nav-btn{display:none}nav h1{margin:0}nav a{text-decoration:none;color:#fff}nav ul{display:flex;flex-direction:row;gap:1em;margin:0;justify-content:flex-end;align-items:flex-end}nav ul li{list-style:none}nav ul li span{visibility:hidden}nav ul li .active span,nav ul li a:hover span{visibility:visible}header div{display:flex;flex-direction:column;justify-content:center;align-items:center;padding-top:10em}header div .btn{margin:2em 0}header div button{background:0 0;border:none;display:inline-block;text-align:center;text-decoration:none;font-size:2rem;cursor:pointer}i.fa-chevron-down{color:hsla(0,0%,67%,.58);font-size:3.75em;margin:1.5rem 0}div h1 span{visibility:visible;animation:caret 1s steps(1) infinite}@keyframes caret{50%{visibility:hidden}}section#about{margin-bottom:5rem}section#about div{padding:.1em 5em}section#curriculumVitae{background-color:var(--primaryDefault);color:#fff;padding:2em 0}section#curriculumVitae .cvGrid{display:flex;flex-direction:row;padding:0 1.5rem;flex-wrap:wrap}section#curriculumVitae .cvGrid>div{width:45%;display:flex;flex-direction:column;min-height:100%}section#curriculumVitae .cvGrid h2{text-align:center}section#curriculumVitae .timeline{position:relative;max-width:30em;gap:1em;display:flex;flex-direction:column;height:100%}section#curriculumVitae #work{margin:0 auto 0 8rem}section#curriculumVitae .timeline:before{content:"";position:absolute;height:100%;border:4px var(--timelineItemBrdr) solid;right:194px;top:0}section#curriculumVitae .timeline:after{content:"";display:table;clear:both}section#curriculumVitae .timelineItem{border:2px solid var(--timelineItemBrdr);-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;padding:0 1rem;width:50%;position:relative;background-color:var(--primaryHover)}.timelineItem:after,section#curriculumVitae .timelineItem:before{content:'';position:absolute}section#curriculumVitae .timelineItem:before{content:'';right:-20px;top:calc(50% - 5px);border-style:solid;border-color:var(--timelineItemBrdr) var(--timelineItemBrdr) transparent transparent;border-width:20px;transform:rotate(45deg)}section#curriculumVitae .timelineItem:nth-child(2n){margin-left:21em}section#curriculumVitae .timelineItem:nth-child(2n):before{right:auto;left:-20px;border-color:transparent transparent var(--timelineItemBrdr) var(--timelineItemBrdr)}section#curriculumVitae .timelineItem h3{font-weight:400}section#curriculumVitae .timelineItem span{color:#e5e5e5}section#projects{display:flex;flex-direction:row;padding:0 2.5rem;border-bottom:2px solid var(--mutedGrey)}section#projects .mainProj,section#projects .otherProj{width:50%;display:flex;flex-direction:column;align-items:center;gap:1em}section#projects .mainProj{border-right:2px solid var(--mutedGrey);padding:0 2.5em 5em 0}section#projects .mainProj img,section#projects .otherProj .oProjItem img{-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;display:block;margin:1em auto}section#projects .mainProj img{width:100%;max-width:40rem}section#projects .mainProj .flexRow{display:flex;flex-direction:row;gap:4em}section#projects .mainProj .flexCol{display:flex;flex-direction:column;gap:2.5em}section#projects .otherProj>a{margin:5rem 0}section#projects .otherProj>div{display:flex;flex-direction:column;gap:2em}section#projects .otherProj>div .oProjItem{display:flex;justify-content:center;align-items:center;flex-direction:row;margin:0 auto;width:90%;border:1px solid var(--grey);gap:1em;box-shadow:0 6px 4px 0 var(--mutedBlack);-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;padding:0 1em}section#projects .otherProj>div .oProjItem:nth-child(2){flex-direction:row-reverse}section#projects .otherProj .oProjItem img{max-width:15rem;width:100%;padding:0 1em}section#projects .oProjItem .flexCol div:nth-child(2){display:flex;flex-direction:row;justify-content:flex-start;gap:3em;margin-left:2em}section#projects .flexCol div:nth-child(2) .btn{padding:.25em .5em}section#contact{display:flex;flex-direction:row;padding:0 2.5em}div#findMe,div#sayHello{width:50%;display:flex;flex-direction:column;align-items:center;gap:1em}div#findMe .findMeContainer{display:flex;flex-direction:row;justify-content:space-around;align-items:center;gap:2em;width:100%;height:100%;margin:5em 0}div#findMe .findMeContainer .profile{-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%}div#findMe .socialIcons{display:flex;flex-direction:row;justify-content:center;align-items:center;gap:2em}div#findMe .socialIcons div{display:flex;flex-direction:column;gap:1.5em}div#findMe .socialIcons div svg{width:2.5em;fill:var(--primaryDefault);font-size:2em}div#findMe .socialIcons div a:hover svg{fill:var(--primaryHover)}div#sayHello #contactForm{display:flex;flex-direction:column;justify-content:center;align-items:center}#contactForm .flName{display:flex;flex-direction:row;gap:1em}div.message{background:var(--primaryDefault);color:#fff;padding:.5em .8em;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;display:flex;justify-content:center;align-items:center;align-self:flex-start;flex-direction:row-reverse;position:relative;height:75px;visibility:visible;overflow:hidden;-webkit-transition:all .5s ease-in-out;-moz-transition:all .5s ease-in-out;-ms-transition:all .5s ease-in-out;-o-transition:all .5s ease-in-out;transition:all .5s ease-in-out;opacity:1;margin-top:1em}div.message.error{background:var(--errorDefault)}div.message button{border:none;background:0 0;outline:0;cursor:pointer;color:#fff;font-size:1.25rem;margin-top:-5px;position:absolute;transform:translate(0,0);transform-origin:0 0;right:10px;top:10px}div.message.hidden{opacity:0;visibility:hidden;height:0}div.message button:hover{text-shadow:-1px 2px var(--mutedBlack)}footer{background-color:var(--primaryDefault);margin-top:5em;padding:2em;display:flex;color:#fff}footer .spacer{width:100%;margin-right:auto}footer p{margin:auto;width:100%;text-align:center}footer .button{margin-left:auto;width:100%;text-align:center}footer .button button{border:5px solid #fff;background:0 0;font-size:3em;padding:.5rem 1rem;width:2em;color:#fff;-webkit-border-radius:.25em;-moz-border-radius:.25em;border-radius:.25em;cursor:pointer}@media screen and (max-width:90em){section#curriculumVitae .cvGrid{flex-direction:column;justify-content:center;align-items:center}section#curriculumVitae .cvGrid>div{width:100%}section#curriculumVitae .cvGrid>div:first-child{padding-bottom:2.5em;margin-bottom:2.5em;border-bottom:5px #fff solid}section#curriculumVitae .cvGrid h2{margin-left:5em}section#curriculumVitae .cvGrid .timeline{margin:0 auto}}@media screen and (max-width:75em){section#about,section#curriculumVitae h1{padding:0 1em}nav{display:block;height:50px;width:100%;background-color:var(--navBack);position:fixed;top:0;padding:0}nav a h1{margin-left:1ch}nav .nav-btn{display:inline-block;position:absolute;right:75px;top:-360px}nav ul{position:fixed;display:block;width:100%;background-color:#333;transition:all .4s ease-in;overflow-y:hidden;padding-left:0;margin-top:7px}nav ul li a{display:block;width:100%;transform:translateX(-30px);transition:all .4s ease-in;opacity:0}.nav-btn label{display:inline-block;cursor:pointer;width:60px;height:50px;position:fixed;-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);transform:rotate(0);-webkit-transition:.5s ease-in;-moz-transition:.5s ease-in;-o-transition:.5s ease-in;transition:.5s ease-in}.nav-btn label span{display:block;position:absolute;height:5px;width:100%;background-color:#fff;opacity:1;right:0;top:20px;-webkit-transform:rotate(0);-moz-transform:rotate(0);-o-transform:rotate(0);transform:rotate(0);-webkit-transition:.25s ease-in;-moz-transition:.25s ease-in;-o-transition:.25s ease-in;transition:.25s ease-in}nav #nav-check:not(:checked)~ul{height:auto;max-height:0}nav #nav-check:not(:checked)~.nav-btn label span:nth-child(1){top:8px;-webkit-transform-origin:left center;-moz-transform-origin:left center;-o-transform-origin:left center;transform-origin:left center}nav #nav-check:not(:checked)~.nav-btn label span:nth-child(2){top:23px;-webkit-transform-origin:left center;-moz-transform-origin:left center;-o-transform-origin:left center;transform-origin:left center}nav #nav-check:not(:checked)~.nav-btn label span:nth-child(3){top:38px;-webkit-transform-origin:left center;-moz-transform-origin:left center;-o-transform-origin:left center;transform-origin:left center}nav #nav-check:checked~.nav-btn label,nav .nav-btn label:hover{background-color:rgba(-1,0,0,.3)}nav #nav-check:checked~ul{max-height:50vh;overflow-y:hidden}nav #nav-check:checked~ul li a{opacity:1;transform:translateX(0)}nav #nav-check:checked~ul li:nth-child(1) a{transition-delay:.15s}nav #nav-check:checked~ul li:nth-child(2) a{transition-delay:.25s}nav #nav-check:checked~ul li:nth-child(3) a{transition-delay:.35s}nav #nav-check:checked~ul li:nth-child(4) a{transition-delay:.45s}nav #nav-check:checked~ul li:nth-child(5) a{transition-delay:.55s}nav #nav-check:checked~.nav-btn label span:first-child{-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}nav #nav-check:checked~.nav-btn label span:nth-child(2){width:0;opacity:0}nav #nav-check:checked~.nav-btn label span:last-child{-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg)}section#about div{padding:.1em 2.5em}section#curriculumVitae .cvGrid{padding:0}section#projects{flex-direction:column;justify-content:center;align-items:center}section#projects .mainProj{border-right:0;padding:0;width:100%;margin:0 5em}section#projects .mainProj img{padding:0 1em}section#projects .mainProj .flexRow{flex-direction:column;margin:0 2.5em}section#projects .mainProj .flexCol{flex-direction:row;justify-content:center;align-items:center}section#projects .otherProj{width:100%}section#projects .otherProj .btn{width:10em;text-align:center}section#projects .otherProj>div .oProjItem,section#projects .otherProj>div .oProjItem:nth-child(2){flex-direction:column}section#projects .oProjItem .flexCol div:nth-child(2){justify-content:center;margin-left:0;margin-bottom:1em}section#projects .otherProj>a{margin-left:3em;margin-right:3em;text-align:center}section#contact{flex-direction:column;justify-content:center;align-items:center}div#findMe,div#sayHello{width:100%}div#findMe .findMeContainer{flex-direction:column;justify-content:center}div#findMe .socialIcons{flex-direction:row}div#findMe .socialIcons div{flex-direction:row}}@media screen and (max-width:55em){section#curriculumVitae .cvGrid .timeline,section#curriculumVitae .cvGrid .timeline#work{margin:0 auto;width:100%}section#curriculumVitae .timeline:before{border:none}section#curriculumVitae .timelineItem,section#curriculumVitae .timelineItem:nth-child(2n){width:95%;padding:0;margin:0 auto}section#curriculumVitae .timelineItem:before{right:unset;left:unset;border:none}div#findMe .socialIcons{flex-direction:column}#contactForm .flName{flex-direction:column;gap:0;width:100%}}@media screen and (max-width:31em){section#about,section#curriculumVitae h1{padding:0 1em}header div h1{text-align:center;height:5.125rem}section#about div{padding:.1em 1em}section#projects .mainProj .flexCol{flex-direction:column}section#projects .oProjItem .flexCol div:nth-child(2){flex-direction:column;justify-content:center;align-items:center}} \ No newline at end of file diff --git a/dist/editor/css/main.css b/dist/editor/css/main.css new file mode 100644 index 0000000..a8332a6 --- /dev/null +++ b/dist/editor/css/main.css @@ -0,0 +1 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}:root{--mainHue:80;--mainSat:60%;--mainLight:50%;--primaryDefault:hsla(var(--mainHue), var(--mainSat), var(--mainLight), 1);--primaryHover:hsla(var(--mainHue), var(--mainSat), calc(var(--mainLight) - 10%), 1);--timelineItemBrdr:hsla(var(--mainHue), var(--mainSat), calc(var(--mainLight) - 20%), 1);--errorDefault:hsla(0, calc(var(--mainSat) + 10%),calc(var(--mainLight) + 10%), 1);--errorHover:hsla(0, calc(var(--mainSat) + 10%), calc(var(--mainLight) - 10%), 1);--grey:hsla(0, 0%, 39%, 1);--notAvailableDefault:hsla(0, 0%, 39%, 1);--notAvailableHover:hsla(0, 0%,32%, 1);--mutedGrey:hsla(0, 0%, 78%, 1);--mutedBlack:hsla(0, 0%, 0%, 0.25);--navBack:hsla(0, 0%, 30%, 1);--titleFS:2.25rem;--generalFS:1.125rem;--headingFS:1.5rem}*{box-sizing:border-box}html{scroll-behavior:smooth}body{font-family:Noto Sans KR,sans-serif;font-style:normal;font-weight:500;font-size:var(--generalFS);line-height:1.625rem}a:visited{color:inherit}h1,nav{font-family:Share Tech Mono,monospace;font-style:normal;font-weight:400;font-size:var(--titleFS);line-height:2.5625rem;text-transform:lowercase}h2{font-family:Noto Sans KR,sans-serif;font-style:normal;font-weight:500;font-size:var(--headingFS);line-height:2.1875rem}a.btn,form input[type=submit]{text-decoration:none;display:inline-block;padding:1rem 2rem;border-radius:.625em;border:.3215em solid var(--primaryDefault);color:#fff;text-align:center}a.btn:hover,form input[type=submit]:hover{border:.3215em solid var(--primaryHover)}a.btnPrimary,form input[type=submit]{background:var(--primaryDefault);cursor:pointer}a.btnOutline{background:#fff;color:var(--primaryDefault)}a.btnPrimary[disabled]{pointer-events:none;background:var(--notAvailableDefault);border:.3215em solid var(--notAvailableDefault)}a.btnPrimary[disabled]:hover{background:var(--notAvailableHover);border:.3215em solid var(--notAvailableHover)}a.btnPrimary:hover,form input[type=submit]:hover{background:var(--primaryHover)}a.btn:active,form input[type=submit]:active{padding:.8rem 1.8rem}.boxShadowOut:hover{box-shadow:0 6px 4px 0 var(--mutedBlack)}.boxShadowIn:active{box-shadow:inset 0 6px 4px 0 var(--mutedBlack)}.textShadow:hover{text-shadow:0 6px 4px var(--mutedBlack)}form .formControl{width:100%}form input[type=submit]{margin-top:1em;align-self:flex-start}form .formControl input:not([type=submit]),form .formControl textarea{width:100%;border:4px solid var(--primaryDefault);background:0 0;outline:0;-webkit-border-radius:1em;-moz-border-radius:1em;border-radius:.5em;padding:0 .5em}form .formControl textarea{padding:.5em}form .formControl input:not([type=submit]).invalid:invalid,form .formControl textarea.invalid:invalid{border:4px solid var(--errorDefault)}form .formControl input:not([type=submit]).invalid:invalid:focus,form .formControl textarea.invalid:invalid:focus{border:4px solid var(--errorHover);box-shadow:0 4px 2px 0 var(--mutedBlack)}form .formControl input:not([type=submit]):focus,form .formControl textarea:focus{border:4px solid var(--primaryHover)}form .formControl input:not([type=submit]){height:3em}form .formControl{width:100%}form input[type=submit]{margin-top:1em;align-self:flex-start}form .formControl input:not([type=submit]),form .formControl textarea{width:100%;border:4px solid var(--primaryDefault);background:0 0;outline:0;-webkit-border-radius:1em;-moz-border-radius:1em;border-radius:.5em;padding:0 .5em}form .formControl textarea{padding:.5em}form .formControl input:not([type=submit]).invalid:invalid,form .formControl textarea.invalid:invalid{border:4px solid var(--errorDefault)}form .formControl input:not([type=submit]).invalid:invalid:focus,form .formControl textarea.invalid:invalid:focus{border:4px solid var(--errorHover);box-shadow:0 4px 2px 0 var(--mutedBlack)}form .formControl input:not([type=submit]):focus,form .formControl textarea:focus{border:4px solid var(--primaryHover)}form .formControl input:not([type=submit]){height:3em}section#about,section#curriculumVitae h1{padding:0 5rem}h1{text-transform:none}body,html{height:100%}main{height:100%;display:flex;flex-direction:column;justify-content:center;align-items:center;background-image:radial-gradient(var(--primaryDefault),#597226)}div#login{display:flex;flex-direction:column;justify-content:center;align-items:center;background-color:#fff;padding:2em 5em;-webkit-border-radius:1em;-moz-border-radius:1em;border-radius:1em;box-shadow:0 6px 4px 0 var(--mutedBlack)}div#login form{display:flex;flex-direction:column;justify-content:center;align-items:center;gap:1em}div#login #password{font-family:Verdana,serif;letter-spacing:.125em}div#login input[type=submit]{margin:0}div.error{background:var(--errorDefault);color:#fff;padding:.5em .8em;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;display:flex;justify-content:center;align-items:center;align-self:flex-start;flex-direction:row-reverse;position:relative;height:75px;visibility:visible;overflow:hidden;-webkit-transition:all .5s ease-in-out;-moz-transition:all .5s ease-in-out;-ms-transition:all .5s ease-in-out;-o-transition:all .5s ease-in-out;transition:all .5s ease-in-out;opacity:1;margin-top:1em}div.error button{border:none;background:0 0;outline:0;cursor:pointer;color:#fff;font-size:1.25rem;margin-top:-5px;position:absolute;transform:translate(0,0);transform-origin:0 0;right:10px;top:10px}div.error.hidden{opacity:0;visibility:hidden;height:0;margin:0;padding:0}div.error button:hover{text-shadow:-1px 2px var(--mutedBlack)} \ No newline at end of file diff --git a/dist/editor/editor.html b/dist/editor/editor.html new file mode 100644 index 0000000..22de6ac --- /dev/null +++ b/dist/editor/editor.html @@ -0,0 +1 @@ +Editor

Editor

\ No newline at end of file diff --git a/dist/editor/index.html b/dist/editor/index.html new file mode 100644 index 0000000..1d2c702 --- /dev/null +++ b/dist/editor/index.html @@ -0,0 +1 @@ +Editor

Login To Editor

\ No newline at end of file diff --git a/dist/editor/js/editor.js b/dist/editor/js/editor.js new file mode 100644 index 0000000..1f745e2 --- /dev/null +++ b/dist/editor/js/editor.js @@ -0,0 +1 @@ +document.addEventListener("DOMContentLoaded",(e=>{fetch("/api/user/isLoggedIn").then((e=>{e.ok||(window.location.href="./")}))})); \ No newline at end of file diff --git a/dist/editor/js/index.js b/dist/editor/js/index.js new file mode 100644 index 0000000..9f37fa5 --- /dev/null +++ b/dist/editor/js/index.js @@ -0,0 +1 @@ +function showErrorMessage(e){document.querySelector("#loginError").classList.remove("hidden"),document.querySelector("#loginError div").innerText=e}document.addEventListener("DOMContentLoaded",(e=>{fetch("/api/user/isLoggedIn").then((e=>{e.ok&&(window.location.href="./editor.html")}))})),document.querySelector("#login form").addEventListener("submit",(e=>{e.preventDefault();let r=new FormData;if(e.target.username.value.length>0&&e.target.password.value.length>0)return r.append("username",e.target.username.value),r.append("password",e.target.password.value),void fetch("/api/user/login",{method:"POST",body:r}).then((e=>{e.ok?window.location.href="./editor.html":400!==e.status?(document.querySelector("#loginError").classList.remove("hidden"),document.querySelector("#loginError div").innerHTML="Invalid username or password"):showErrorMessage("Please type in a username and password.")}));document.querySelector("#loginError").classList.remove("hidden"),document.querySelector("#loginError div").innerHTML="Please type in a username and password"})),document.querySelector("#loginError .close").addEventListener("click",(()=>document.querySelector("#loginError").classList.toggle("hidden"))); \ No newline at end of file diff --git a/dist/editor/js/main.js b/dist/editor/js/main.js new file mode 100644 index 0000000..9f37fa5 --- /dev/null +++ b/dist/editor/js/main.js @@ -0,0 +1 @@ +function showErrorMessage(e){document.querySelector("#loginError").classList.remove("hidden"),document.querySelector("#loginError div").innerText=e}document.addEventListener("DOMContentLoaded",(e=>{fetch("/api/user/isLoggedIn").then((e=>{e.ok&&(window.location.href="./editor.html")}))})),document.querySelector("#login form").addEventListener("submit",(e=>{e.preventDefault();let r=new FormData;if(e.target.username.value.length>0&&e.target.password.value.length>0)return r.append("username",e.target.username.value),r.append("password",e.target.password.value),void fetch("/api/user/login",{method:"POST",body:r}).then((e=>{e.ok?window.location.href="./editor.html":400!==e.status?(document.querySelector("#loginError").classList.remove("hidden"),document.querySelector("#loginError div").innerHTML="Invalid username or password"):showErrorMessage("Please type in a username and password.")}));document.querySelector("#loginError").classList.remove("hidden"),document.querySelector("#loginError div").innerHTML="Please type in a username and password"})),document.querySelector("#loginError .close").addEventListener("click",(()=>document.querySelector("#loginError").classList.toggle("hidden"))); \ No newline at end of file diff --git a/dist/index.html b/dist/index.html index 7d10ff6..b2b646d 100644 --- a/dist/index.html +++ b/dist/index.html @@ -1 +1 @@ -Rohit Pai - Portfolio

full stack developer

Contact Me

about

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Beatae debitis dolore, illum minus molestias officiis quidem similique ut. Autem consectetur eum, fugit illum ipsam laudantium magnam magni minima nesciunt numquam officia, soluta unde, voluptates! Aliquid aut, beatae dignissimos, dolorem ex exercitationem fugiat harum itaque laudantium placeat repellat suscipit velit! Aliquam architecto autem beatae consectetur, dicta dolorum eligendi esse harum hic iure labore, libero molestias nemo neque nisi nostrum odio sed sunt tempora totam voluptatem voluptatibus.

Download CV

curriculum vitae

Education

Work

other projects

View More

find me

say hello

© 2021 Rohit Pai all rights reserved

\ No newline at end of file +Rohit Pai - Portfolio

full stack developer

Contact Me

about

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Beatae debitis dolore, illum minus molestias officiis quidem similique ut. Autem consectetur eum, fugit illum ipsam laudantium magnam magni minima nesciunt numquam officia, soluta unde, voluptates! Aliquid aut, beatae dignissimos, dolorem ex exercitationem fugiat harum itaque laudantium placeat repellat suscipit velit! Aliquam architecto autem beatae consectetur, dicta dolorum eligendi esse harum hic iure labore, libero molestias nemo neque nisi nostrum odio sed sunt tempora totam voluptatem voluptatibus.

Download CV

curriculum vitae

Education

Work

other projects

View More

find me

say hello

© 2021 Rohit Pai all rights reserved

\ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index bef7016..50f028e 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -8,16 +8,16 @@ const github = (process.env.github) ? true : false; gulp.task("minifyHTML", () => { - return gulp.src("src/*.html" ) + return gulp.src("src/**/*.html" ) .pipe(htmlMin({collapseWhitespace: true})) - .pipe(gulp.dest("dist")); + .pipe(gulp.dest("dist/")); }); gulp.task("minifyCSS", () => { - return gulp.src("src/css/main.css") + return gulp.src("src/**/main.css") .pipe(cssMin({compatibility: "ie8"})) - .pipe(gulp.dest("dist/css")); + .pipe(gulp.dest("dist/")); }); gulp.task("minifyJS", () => @@ -30,11 +30,11 @@ gulp.task("minifyJS", () => }; } - return gulp.src("src/js/*.js") + return gulp.src("src/**/*.js") .on("error", createErrorHandler("gulp.src")) .pipe(terser()) .on("error", createErrorHandler("terser")) - .pipe(gulp.dest("dist/js")) + .pipe(gulp.dest("dist/")) .on("error", createErrorHandler("gulp.dest")); }); @@ -46,18 +46,15 @@ gulp.task("movePHPFiles", () => gulp.task("watchFiles", () => { - gulp.watch("src/*.html", gulp.task("minifyHTML")); - gulp.watch("src/css/*.css", gulp.task("minifyCSS")); - gulp.watch("src/js/*.js", gulp.task("minifyJS")); + gulp.watch("src/**/*.html", gulp.task("minifyHTML")); + gulp.watch("src/**/*.css", gulp.task("minifyCSS")); + gulp.watch("src/**/*.js", gulp.task("minifyJS")); gulp.watch("src/api/*.php", gulp.task("movePHPFiles")) }); gulp.task("browserSync", () => { browserSync.init({ - // server: { - // baseDir: "dist" - // }, proxy: "https://rohitpai.co.uk/", serveStatic: ["./dist"] @@ -76,6 +73,3 @@ gulp.task("default", async () => (gulp.series(gulp.parallel("watchFiles", "browserSync"))()); } }); - -//gulp.task("default", gulp.series(gulp.parallel("watchFiles", "browserSync"))); - diff --git a/package-lock.json b/package-lock.json index 88c4de5..d8012a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "jest": "^27.2.0", "normalize.css": "^8.0.1", "require": "^2.4.20", + "source-map-generator": "^0.8.0", "vinyl-ftp": "^0.6.1" } }, @@ -9310,6 +9311,14 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-generator": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/source-map-generator/-/source-map-generator-0.8.0.tgz", + "integrity": "sha512-psgxdGMwl5MZM9S3FWee4EgsEaIjahYV5AzGnwUvPhWeITz/j6rKpysQHlQ4USdxvINlb8lKfWGIXwfkrgtqkA==", + "engines": { + "node": ">= 10" + } + }, "node_modules/source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", @@ -17973,6 +17982,11 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, + "source-map-generator": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/source-map-generator/-/source-map-generator-0.8.0.tgz", + "integrity": "sha512-psgxdGMwl5MZM9S3FWee4EgsEaIjahYV5AzGnwUvPhWeITz/j6rKpysQHlQ4USdxvINlb8lKfWGIXwfkrgtqkA==" + }, "source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", diff --git a/package.json b/package.json index 7ca1822..726354a 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "jest": "^27.2.0", "normalize.css": "^8.0.1", "require": "^2.4.20", + "source-map-generator": "^0.8.0", "vinyl-ftp": "^0.6.1" } } diff --git a/src/api/index.php b/src/api/index.php index 81615ad..006b5d2 100644 --- a/src/api/index.php +++ b/src/api/index.php @@ -1,4 +1,5 @@ add(new SameSiteCookieMiddleware($ssConfig)); // for error checking $errorMiddleware = $app->addErrorMiddleware(true, true, true); +// set base path for all routes $app->setBasePath("/api"); +// return all responses as JSON +$app->add(function($request, $handler) { + $response = $handler->handle($request); + return $response->withHeader('Content-Type', 'application/json'); +}); + $timelineData = new timelineData(); $projectData = new projectData(); +$user = new user(); $app->get("/timelineData/{timeline}", function (Request $request, Response $response, array $args) { global $timelineData; - $json = $result = ""; //check if route is available if it is get the data //otherwise return an error if($args["timeline"] == "edu") { - $result = $timelineData->getEduData(); + return $response->getBody()->write(json_encode($timelineData->getEduData())); } - else if($args["timeline"] == "work") + + if($args["timeline"] == "work") { - $result = $timelineData->getWorkData(); + return $response->getBody()->write(json_encode($timelineData->getWorkData())); } - else - { - $result = array(array("errorMessage" => "Error, timeline data not found")); - } - - $json = json_encode($result); - - $response->getBody()->write($json); - //if it is an error give a 404 code since it can't find the data - if(array_key_exists("errorMessage", $result)) - { - $response = $response->withStatus(404); - } - - //use content type json to indicate json data on frontend. - return $response->withHeader("Content-Type", "application/json"); + + // something went wrong + $response->getBody()->write(json_encode(array("errorMessage" => "Error, timeline data not found"))); + return $response->withStatus(404); }); $app->get('/projectData', function (Request $request, Response $response) @@ -83,7 +83,7 @@ $app->get('/projectData', function (Request $request, Response $response) } //use content type json to indicate json data on frontend. - return $response->withHeader("Content-Type", "application/json"); + return $response; }); $app->post('/contact', function (Request $request, Response $response) @@ -92,15 +92,14 @@ $app->post('/contact', function (Request $request, Response $response) if(empty($data["fName"]) || empty($data["lName"]) || empty($data["email"]) || empty($data["subject"]) || empty($data["message"])) { $response->getBody()->write(json_encode(array("errorMessage" => "Please fill out all the fields"))); - $response = $response->withStatus(400); - return $response->withHeader("Content-Type", "application/json"); + return $response->withStatus(400); } if (!filter_var($data["email"], FILTER_VALIDATE_EMAIL)) { $response->getBody()->write(json_encode(array("errorMessage" => "Email is not the correct format"))); $response = $response->withStatus(400); - return $response->withHeader("Content-Type", "application/json"); + return $response; } // email form filler/conatcter @@ -260,4 +259,47 @@ $app->post('/contact', function (Request $request, Response $response) return $response->withStatus(201); }); +$app->post('/user/login', function (Request $request, Response $response) { + + global $user; + + // get request data + $data = $request->getParsedBody(); + + if (empty($data["username"]) || empty($data["password"])) + { + // uh oh user sent empty data + return $response->withStatus(400); + } + + if ($user->checkUser($data["username"], $data["password"])) + { + // yay user is logged in + $_SESSION["token"] = $user->createToken(); + $_SESSION["username"] = $data["username"]; + return $response; + } + return $response->withStatus(401); +}); + +$app->get('/user/isLoggedIn', function (Request $request, Response $response) { + + global $user; + + if (empty($_SESSION["token"]) && empty($_SESSION["username"])) + { + // uh oh user not logged in + return $response->withStatus(401); + } + + if (empty($_SESSION["token"])) + { + // user is logged in but no token was created + $_SESSION["token"] = $user->createToken(); + return $response; + } + + return $response->getBody()->write(json_encode(array("token" => $_SESSION["token"]))); +}); + $app->run(); diff --git a/src/api/projectData.php b/src/api/projectData.php index cef6109..fc5bf68 100644 --- a/src/api/projectData.php +++ b/src/api/projectData.php @@ -10,7 +10,7 @@ require_once "./config.php"; */ class projectData { - function getProjectData() + function getProjectData(): array { $conn = dbConn(); $stmt = $conn->prepare("SELECT title, isMainProject, information, imgLocation, projectLink, githubLink FROM projects order by date LIMIT 4;"); @@ -23,9 +23,6 @@ class projectData { return $result; } - else - { - return array(array("errorMessage" => "Error, project data not found")); - } + return array("errorMessage" => "Error, project data not found"); } } diff --git a/src/api/timelineData.php b/src/api/timelineData.php index 5686f13..67eb608 100644 --- a/src/api/timelineData.php +++ b/src/api/timelineData.php @@ -10,7 +10,7 @@ require_once "./config.php"; */ class timelineData { - function getEduData() + function getEduData(): array { $conn = dbConn(); $stmt = $conn->prepare("SELECT DATE_FORMAT(startPeriod, '%b, %Y') as startPeriod, DATE_FORMAT(endPeriod, '%b, %Y') as endPeriod, grade, course FROM edu ORDER BY startPeriod DESC;"); @@ -23,13 +23,10 @@ class timelineData { return $result; } - else - { - return array("errorMessage" => "Error, edu data not found"); - } + return array("errorMessage" => "Error, edu data not found"); } - function getWorkData() + function getWorkData(): array { $conn = dbConn(); $stmt = $conn->prepare("SELECT DATE_FORMAT(startPeriod, '%b, %Y') as startPeriod, DATE_FORMAT(endPeriod, '%b, %Y') as endPeriod, companyName, area, title FROM work ORDER BY work.startPeriod DESC;"); @@ -42,10 +39,7 @@ class timelineData { return $result; } - else - { - return array("errorMessage" => "Error, work data not found"); - } + return array("errorMessage" => "Error, work data not found"); } diff --git a/src/api/user.php b/src/api/user.php new file mode 100644 index 0000000..d47c3cd --- /dev/null +++ b/src/api/user.php @@ -0,0 +1,38 @@ +prepare("SELECT * FROM users WHERE username = :username"); + $stmt->bindParam(":username", $username); + $stmt->execute(); + + // set the resulting array to associative + $result = $stmt->fetchAll(PDO::FETCH_ASSOC); + + if ($result) + { + if (password_verify($password, $result[0]["password"])) + { + return true; + } + return false; + } + return false; + } + + function createToken(): string + { + return uniqid("rpe-"); + } +} \ No newline at end of file diff --git a/src/css/contact.css b/src/css/contact.css index bc4dda2..79a347a 100644 --- a/src/css/contact.css +++ b/src/css/contact.css @@ -66,96 +66,6 @@ div#sayHello #contactForm{ gap: 1em; } -#contactForm .formControl { - width: 100%; -} - -#contactForm input[type="submit"] { - margin-top: 1em; - align-self: flex-start; -} - -#contactForm .formControl input:not([type="submit"]), #contactForm .formControl textarea { - width: 100%; - border: 4px solid var(--primaryDefault); - background: none; - outline: none; - -webkit-border-radius: 1em; - -moz-border-radius: 1em; - border-radius: 0.5em; - padding: 0 0.5em; -} - -#contactForm .formControl textarea { - padding: 0.5em; -} - - -#contactForm .formControl input:not([type="submit"]).invalid:invalid, #contactForm .formControl textarea.invalid:invalid { - border: 4px solid var(--errorDefault); -} - -#contactForm .formControl input:not([type="submit"]).invalid:invalid:focus, #contactForm .formControl textarea.invalid:invalid:focus { - border: 4px solid var(--errorHover); - box-shadow: 0 4px 2px 0 var(--mutedBlack); -} - -#contactForm .formControl input:not([type="submit"]):focus, #contactForm .formControl textarea:focus { - border: 4px solid var(--primaryHover); -} - -#contactForm .formControl input:not([type="submit"]) { - height: 3em; -} - -#contactForm .flName { - display: flex; - flex-direction: row; - gap: 1em; -} - -#contactForm .formControl { - width: 100%; -} - -#contactForm input[type="submit"] { - margin-top: 1em; - align-self: flex-start; -} - -#contactForm .formControl input:not([type="submit"]), #contactForm .formControl textarea { - width: 100%; - border: 4px solid var(--primaryDefault); - background: none; - outline: none; - -webkit-border-radius: 1em; - -moz-border-radius: 1em; - border-radius: 0.5em; - padding: 0 0.5em; -} - -#contactForm .formControl textarea { - padding: 0.5em; -} - - -#contactForm .formControl input:not([type="submit"]).invalid:invalid, #contactForm .formControl textarea.invalid:invalid { - border: 4px solid var(--errorDefault); -} - -#contactForm .formControl input:not([type="submit"]).invalid:invalid:focus, #contactForm .formControl textarea.invalid:invalid:focus { - border: 4px solid var(--errorHover); - box-shadow: 0 4px 2px 0 var(--mutedBlack); -} - -#contactForm .formControl input:not([type="submit"]):focus, #contactForm .formControl textarea:focus { - border: 4px solid var(--primaryHover); -} - -#contactForm .formControl input:not([type="submit"]) { - height: 3em; -} - div.message { background: var(--primaryDefault); color: #FFFFFF; diff --git a/src/css/main.css b/src/css/main.css index 3a9a906..002360b 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -10,34 +10,6 @@ @import "contact.css"; @import "footer.css"; -/****** Root Style ******/ -:root { - /* Colours */ - --mainHue: 79; - --mainSat: 62%; - --mainLight: 51%; - --primaryDefault: hsla(var(--mainHue), var(--mainSat), var(--mainLight), 1); - --primaryHover: hsla(var(--mainHue), var(--mainSat), calc(var(--mainLight) - 10%), 1); - --timelineItemBrdr: hsla(var(--mainHue), var(--mainSat), calc(var(--mainLight) - 20%), 1); - --errorDefault: hsla(0, calc(var(--mainSat) + 10%),calc(var(--mainLight) + 10%), 1); - --errorHover: hsla(0, calc(var(--mainSat) + 10%), calc(var(--mainLight) - 10%), 1); - --grey: hsla(0, 0%, 39%, 1); - --notAvailableDefault: hsla(0, 0%, 39%, 1); - --notAvailableHover: hsla(0, 0%,32%, 1); - --mutedGrey: hsla(0, 0%, 78%, 1); - --mutedBlack: hsla(0, 0%, 0%, 0.25); - --navBack: hsla(0, 0%, 30%, 1); - - /* Font Sizes */ - --titleFS: 2.25rem; - --generalFS: 1.125rem; - --headingFS: 1.5rem; -} - -*{ - box-sizing: border-box; -} - /**** Media Queries *****/ @media screen and (max-width: 90em) { diff --git a/src/css/templateStyles.css b/src/css/templateStyles.css index 51de6bf..94af096 100644 --- a/src/css/templateStyles.css +++ b/src/css/templateStyles.css @@ -1,4 +1,33 @@ -/*** Template Styles ****/ +/*** Template Styles ***/ + +/*** Root Style ***/ + +:root { + /* Colours */ + --mainHue: 80; + --mainSat: 60%; + --mainLight: 50%; + --primaryDefault: hsla(var(--mainHue), var(--mainSat), var(--mainLight), 1); + --primaryHover: hsla(var(--mainHue), var(--mainSat), calc(var(--mainLight) - 10%), 1); + --timelineItemBrdr: hsla(var(--mainHue), var(--mainSat), calc(var(--mainLight) - 20%), 1); + --errorDefault: hsla(0, calc(var(--mainSat) + 10%),calc(var(--mainLight) + 10%), 1); + --errorHover: hsla(0, calc(var(--mainSat) + 10%), calc(var(--mainLight) - 10%), 1); + --grey: hsla(0, 0%, 39%, 1); + --notAvailableDefault: hsla(0, 0%, 39%, 1); + --notAvailableHover: hsla(0, 0%,32%, 1); + --mutedGrey: hsla(0, 0%, 78%, 1); + --mutedBlack: hsla(0, 0%, 0%, 0.25); + --navBack: hsla(0, 0%, 30%, 1); + + /* Font Sizes */ + --titleFS: 2.25rem; + --generalFS: 1.125rem; + --headingFS: 1.5rem; +} + +*{ + box-sizing: border-box; +} html { scroll-behavior: smooth; @@ -88,6 +117,89 @@ a.btn:active, form input[type="submit"]:active { text-shadow: 0 6px 4px var(--mutedBlack); } +form .formControl { + width: 100%; +} + +form input[type="submit"] { + margin-top: 1em; + align-self: flex-start; +} + +form .formControl input:not([type="submit"]), form .formControl textarea { + width: 100%; + border: 4px solid var(--primaryDefault); + background: none; + outline: none; + -webkit-border-radius: 1em; + -moz-border-radius: 1em; + border-radius: 0.5em; + padding: 0 0.5em; +} + +form .formControl textarea { + padding: 0.5em; +} + + +form .formControl input:not([type="submit"]).invalid:invalid, form .formControl textarea.invalid:invalid { + border: 4px solid var(--errorDefault); +} + +form .formControl input:not([type="submit"]).invalid:invalid:focus, form .formControl textarea.invalid:invalid:focus { + border: 4px solid var(--errorHover); + box-shadow: 0 4px 2px 0 var(--mutedBlack); +} + +form .formControl input:not([type="submit"]):focus, form .formControl textarea:focus { + border: 4px solid var(--primaryHover); +} + +form .formControl input:not([type="submit"]) { + height: 3em; +} + +form .formControl { + width: 100%; +} + +form input[type="submit"] { + margin-top: 1em; + align-self: flex-start; +} + +form .formControl input:not([type="submit"]), form .formControl textarea { + width: 100%; + border: 4px solid var(--primaryDefault); + background: none; + outline: none; + -webkit-border-radius: 1em; + -moz-border-radius: 1em; + border-radius: 0.5em; + padding: 0 0.5em; +} + +form .formControl textarea { + padding: 0.5em; +} + +form .formControl input:not([type="submit"]).invalid:invalid, form .formControl textarea.invalid:invalid { + border: 4px solid var(--errorDefault); +} + +form .formControl input:not([type="submit"]).invalid:invalid:focus, form .formControl textarea.invalid:invalid:focus { + border: 4px solid var(--errorHover); + box-shadow: 0 4px 2px 0 var(--mutedBlack); +} + +form .formControl input:not([type="submit"]):focus, form .formControl textarea:focus { + border: 4px solid var(--primaryHover); +} + +form .formControl input:not([type="submit"]) { + height: 3em; +} + section#about, section#curriculumVitae h1 { padding: 0 5rem; } diff --git a/src/editor/css/login.css b/src/editor/css/login.css new file mode 100644 index 0000000..ca240c1 --- /dev/null +++ b/src/editor/css/login.css @@ -0,0 +1,100 @@ +/*** Login Styles ***/ + +h1 { + text-transform: none; +} + +html, body { + height: 100%; +} + +main { + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-image: radial-gradient(var(--primaryDefault), hsl(80, 50%, 30%)); +} + +div#login { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-color: #FFFFFF; + padding: 2em 5em; + -webkit-border-radius: 1em; + -moz-border-radius: 1em; + border-radius: 1em; + box-shadow: 0 6px 4px 0 var(--mutedBlack); +} + +div#login form { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 1em; +} + +div#login #password { + font-family: Verdana, serif; + letter-spacing: 0.125em; +} + +div#login input[type=submit]{ + margin: 0; +} + +div.error { + background: var(--errorDefault); + color: #FFFFFF; + padding: 0.5em 0.8em; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + display: flex; + justify-content: center; + align-items: center; + align-self: flex-start; + flex-direction: row-reverse; + position: relative; + height: 75px; + visibility: visible; + overflow: hidden; + -webkit-transition: all 0.5s ease-in-out; + -moz-transition: all 0.5s ease-in-out; + -ms-transition: all 0.5s ease-in-out; + -o-transition: all 0.5s ease-in-out; + transition: all 0.5s ease-in-out; + opacity: 1; + margin-top: 1em; +} + +div.error button { + border: none; + background: none; + outline: none; + cursor: pointer; + color: #FFFFFF; + font-size: 1.25rem; + margin-top: -5px; + position: absolute; + transform: translate(0, 0); + transform-origin: 0 0; + right: 10px; + top: 10px; +} + +div.error.hidden { + opacity: 0; + visibility: hidden; + height: 0; + margin: 0; + padding: 0; +} + +div.error button:hover { + text-shadow: -1px 2px var(--mutedBlack); +} \ No newline at end of file diff --git a/src/editor/css/main.css b/src/editor/css/main.css new file mode 100644 index 0000000..64ed05a --- /dev/null +++ b/src/editor/css/main.css @@ -0,0 +1,8 @@ +/******** Imports *******/ +@import "/node_modules/normalize.css/normalize.css"; +@import url("https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,400;0,700;1,400;1,700&family=Share+Tech+Mono&family=Source+Sans+Pro:ital,wght@0,200;0,300;0,400;0,600;0,700;0,900;1,200;1,300;1,400;1,600;1,700;1,900&display=swap"); +/*local imports*/ +@import "../../css/templateStyles.css"; +@import "login.css"; + +/*other styles*/ \ No newline at end of file diff --git a/src/editor/editor.html b/src/editor/editor.html new file mode 100644 index 0000000..728d015 --- /dev/null +++ b/src/editor/editor.html @@ -0,0 +1,12 @@ + + + + + Editor + + +

Editor

+ + + + \ No newline at end of file diff --git a/src/editor/index.html b/src/editor/index.html new file mode 100644 index 0000000..8a43f28 --- /dev/null +++ b/src/editor/index.html @@ -0,0 +1,36 @@ + + + + + Editor + + + +
+
+

Login To Editor

+ +
+
+ + +
+ +
+ + +
+ + + + + +
+
+
+ + + \ No newline at end of file diff --git a/src/editor/js/editor.js b/src/editor/js/editor.js new file mode 100644 index 0000000..bdf084c --- /dev/null +++ b/src/editor/js/editor.js @@ -0,0 +1,12 @@ + +document.addEventListener('DOMContentLoaded', e => +{ + // check if the user is logged in, if not redirect to login + fetch('/api/user/isLoggedIn').then(res => + { + if (!res.ok) + { + window.location.href = './'; + } + }); +}) \ No newline at end of file diff --git a/src/editor/js/index.js b/src/editor/js/index.js new file mode 100644 index 0000000..de3fee4 --- /dev/null +++ b/src/editor/js/index.js @@ -0,0 +1,54 @@ + +document.addEventListener("DOMContentLoaded", e => +{ + // check if the user is logged in and if so load the editor + fetch("/api/user/isLoggedIn").then(res => + { + if (res.ok) + { + window.location.href = "./editor.html"; + } + }); +}); + +function showErrorMessage(message) +{ + document.querySelector("#loginError").classList.remove("hidden"); + document.querySelector("#loginError div").innerText = message; +} + +document.querySelector("#login form").addEventListener("submit", e => +{ + e.preventDefault(); + let loginData = new FormData(); + if (e.target.username.value.length > 0 && e.target.password.value.length > 0) + { + loginData.append("username", e.target.username.value); + loginData.append("password", e.target.password.value); + fetch("/api/user/login", + { + method: "POST", + body: loginData + }).then(res => + { + if (res.ok) + { + window.location.href = "./editor.html"; + return; + } + if (res.status === 400) + { + showErrorMessage("Please type in a username and password."); + return; + } + document.querySelector("#loginError").classList.remove("hidden"); + document.querySelector("#loginError div").innerHTML = "Invalid username or password"; + }); + return; + } + document.querySelector("#loginError").classList.remove("hidden"); + document.querySelector("#loginError div").innerHTML = "Please type in a username and password"; +}); + +document.querySelector("#loginError .close").addEventListener("click", () => + document.querySelector("#loginError").classList.toggle("hidden"));