diff --git a/README.md b/README.md index 39e2788d..40b8ffd5 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,30 @@ -# cs4241-FinalProject - -For your final project, you'll implement a course project that exhibits your mastery of the course materials. -Similar to A4, this project gives you an opportunity to be creative and to pursue individual research and learning. - -## General description - -Your project should consist of a complete Web application, exhibiting facets of the three main sections of the course material: - -- Static Web page content and design. You should have a project that is accessible, easily navigable, and features significant content. -- Dynamic behavior implemented with JavaScript. -- Server-side programming *using Node.js*. Typically this will take the form of some sort of persistent data, authentication, and possibly server-side computation. - -Additionally, you should incorporate features that you independently research, design, and implement for your project. - -## Project ideation - -Excellent projects serve someone/some group; for this assignment you need to define your users and stakeholders. I encourage you to identify projects that will have impact, either artistically, politically, or in terms of productivity. Consider creating something useful for a cause or hobby you care about. - -## Logistics - -### Team size -Students are encouraged to work in teams of 2-5 students for the project. This will allow you to build a good project without expending an excessive amount of effort. While I would expect a team of four or five students to produce a project with more features, I expect a every team's work to exhibit all of the required facets described above. - -### Deliverables - -__Proposal:__ -Provide an outline of your project direction and the names of the team members. -The outline should have enough detail so that staff can determine if it meets the minimum expectations, or if it goes too far to be reasonable by the deadline. -This file must be named proposal.md so we can find it. -Submit a PR to turn it in by Monday, September 30th, before class - -There are no other scheduled checkpoints for your project. -You must be done in time to present before the final project demo day (October 10th). - -#### Turning in Your Outline / Project - -**NOTE: code is due before the project presentation day due to the end of term / grading schedule constraints** -Submit a second PR on the final project repo to turn in your app and code. - -Deploy your app, in the form of a webpage, to Glitch/Heroku/Digital Ocean or some other service. -Folks on the same team do not need to post the same webpage, but must instead clearly state who is on the team in their proposal. -(Staff will use the proposal to build the grading sheet.) - -## Final Presentation - -Presentations will occur during the final day of class. - -## FAQs - -- **Can I use XYZ framework?** You can use any web-based frameworks or tools available, but for your server programming you need to use node.js. +## Aquatic Dating Software (Teender) + +http://fp-percyjiang.glitch.me + +**Brief Description**: An online dating software for aquatic creatures. + +**Instructions**: + - npm i + - npm start + - register an account and login + +**Technologies**: + - dat.gui + - Web Audio API + - Canvas + - Express + - Passport + - Bootstrap + - Mongo + - Sweet Alert + - Font Awesome + +**Challenges**: + - None of us have experience with dating software so we made up dummy users of cartoons. + - We implemented a large amount of user interactions and additional features. + +**Roles**: + - Zonglin Peng: The dat.gui fizzy text and audio visualization. + - Rui Huang: Front end design of the whole project + - Haozhe Jiang: The rest of the project \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..29ac03c2 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,586 @@ +{ + "name": "fp", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bootstrap": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.3.1.tgz", + "integrity": "sha512-rXqOmH1VilAt2DyPzluTi2blhk17bO7ef+zLLPlWvG494pDxcM234pJ8wTc/6R40UWizAIIMgxjvxZg5kmsbag==" + }, + "bson": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.1.tgz", + "integrity": "sha512-jCGVYLoYMHDkOsbwJZBCqwMHyH4c+wzgI9hG7Z6SZJRXWr+x58pdIbm2i9a/jFGCkRJqRUr8eoI7lDWa0hTkxg==" + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "connect-ensure-login": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/connect-ensure-login/-/connect-ensure-login-0.1.1.tgz", + "integrity": "sha1-F03MUSQ7nqwj+NmCFa62aU4uihI=" + }, + "connect-flash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", + "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA=" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-parser": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.4.tgz", + "integrity": "sha512-lo13tqF3JEtFO7FyA49CqbhaFkskRJ0u/UAiINgrIXeRCY41c88/zxtrECl8AKH3B0hj9q10+h3Kt8I7KlW4tw==", + "requires": { + "cookie": "0.3.1", + "cookie-signature": "1.0.6" + } + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + } + } + }, + "express-flash": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/express-flash/-/express-flash-0.0.2.tgz", + "integrity": "sha1-I9GovPP5DXB5KOSJ+Whp7K0KzaI=", + "requires": { + "connect-flash": "0.1.x" + } + }, + "express-session": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.16.2.tgz", + "integrity": "sha512-oy0sRsdw6n93E9wpCNWKRnSsxYnSDX9Dnr9mhZgqUEEorzcq5nshGYSZ4ZReHFhKQ80WI5iVUUSPW7u3GaKauw==", + "requires": { + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.1.2", + "uid-safe": "~2.1.5" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + }, + "jquery": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz", + "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + }, + "mongodb": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.3.2.tgz", + "integrity": "sha512-fqJt3iywelk4yKu/lfwQg163Bjpo5zDKhXiohycvon4iQHbrfflSAz9AIlRE6496Pm/dQKQK5bMigdVo2s6gBg==", + "requires": { + "bson": "^1.1.1", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2" + } + }, + "morgan": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", + "integrity": "sha512-HQStPIV4y3afTiCYVxirakhlCfGkI161c76kKFca7Fk1JusM//Qeo1ej2XaMniiNeaZklMVrh3vTtIzpzwbpmA==", + "requires": { + "basic-auth": "~2.0.0", + "debug": "2.6.9", + "depd": "~1.1.2", + "on-finished": "~2.3.0", + "on-headers": "~1.0.1" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "node-statsd": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/node-statsd/-/node-statsd-0.1.1.tgz", + "integrity": "sha1-J6WTSHY9CvegN6wqAx/vPwUQE9M=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "passport": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz", + "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=", + "requires": { + "passport-strategy": "1.x.x", + "pause": "0.0.1" + } + }, + "passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", + "requires": { + "passport-strategy": "1.x.x" + } + }, + "passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + }, + "popper.js": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.15.0.tgz", + "integrity": "sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA==" + }, + "proxy-addr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.0" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "requires": { + "resolve-from": "^2.0.0", + "semver": "^5.1.0" + } + }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, + "response-time": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/response-time/-/response-time-2.3.2.tgz", + "integrity": "sha1-/6cbq5UtYvfB1Jt0NDVfvGjf/Fo=", + "requires": { + "depd": "~1.1.0", + "on-headers": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-favicon": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz", + "integrity": "sha1-k10kDN/g9YBTB/3+ln2IlCosvPA=", + "requires": { + "etag": "~1.8.1", + "fresh": "0.5.2", + "ms": "2.1.1", + "parseurl": "~1.3.2", + "safe-buffer": "5.1.1" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "requires": { + "random-bytes": "~1.0.0" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..52caa6f7 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "fp", + "version": "", + "description": "", + "author": "", + "scripts": { + "start": "node server.js" + }, + "dependencies": { + "body-parser": "latest", + "bootstrap": "^4.3.1", + "connect-ensure-login": "^0.1.1", + "cookie-parser": "^1.4.4", + "express": "^4.17.1", + "express-flash": "0.0.2", + "express-session": "^1.16.2", + "jquery": "^3.4.1", + "mime": "^2.4.4", + "mongodb": "^3.3.2", + "morgan": "^1.9.1", + "node-statsd": "^0.1.1", + "passport": "^0.4.0", + "passport-local": "^1.0.0", + "popper.js": "^1.15.0", + "response-time": "^2.3.2", + "serve-favicon": "^2.5.0" + } +} diff --git a/proposal.md b/proposal.md new file mode 100644 index 00000000..cb356c1f --- /dev/null +++ b/proposal.md @@ -0,0 +1,34 @@ +# Aquatic Dating Software + +## The Team + - Haozhe Jiang - @PercyJiang + - Zonglin Peng - @zonglinpeng + - Rui Huang - @Ryc1x + +## Overview +An online dating software for aquatic creatures with some nice additional features. + +## Base Features + - Allow users to register and login using authentication. + - Allow users to see profiles of other users. + - Allow users to “like” other users or “blacklist” other users. + - Automatic generated “match score” between current user and other users. + - Mobile capabilities. + +## Stretch Features + - Allow users to upload profile pictures. + - Allow users to comment on other users’ profiles. + - Dynamic background picture. + - Live page titles using dat.GUI. + - Page music. + - Other more personalized interactions. + +## Technologies Used + - Express + - Local Strategy Passport Authentication + - Bootstrap + - Sweet Alert + - Font Awesome + - Three.js + - Web Audio API + - dat.GUI diff --git a/public/canvas/index.html b/public/canvas/index.html new file mode 100644 index 00000000..0ad976a8 --- /dev/null +++ b/public/canvas/index.html @@ -0,0 +1,23 @@ + + + + + Music Player Demo + + + + + +
+

+ + + \ No newline at end of file diff --git a/public/canvas/main.js b/public/canvas/main.js new file mode 100644 index 00000000..36d4e8d1 --- /dev/null +++ b/public/canvas/main.js @@ -0,0 +1,104 @@ +window.onload = function() { + let fizzyText = new FizzyText('Teender') + this.initPage() +} + +var canvas, ctx, center_x, center_y, radius, bars, + x_end, y_end, bar_height, bar_width, + frequency_array, context, analyser; + +bars = 200; +bar_width = 2; + +function initPage() { + let playme = document.getElementById('playme') + playme.onclick = function() { + if (context !== undefined && context.state === 'running') { + context.suspend().then(function() { + console.log("music paused.") + playme.textContent = 'Resume' + }) + } else if (context !== undefined && context.state === 'suspended') { + context.resume().then(function() { + console.log("music resumed.") + playme.textContent = 'Pause' + }) + } else { + context = new AudioContext(); + analyser = context.createAnalyser(); + audio = new Audio(); + audio.src = "../canvas/test.flac"; // <----- the source path + source = context.createMediaElementSource(audio); + source.connect(analyser); + analyser.connect(context.destination); + frequency_array = new Uint8Array(analyser.frequencyBinCount); + + audio.play(); + animationLooper(); + } + } + animationLooper(); +} + +function animationLooper() { + + // set to the size of device + canvas = document.getElementById("renderer"); + canvas.width = 300; + canvas.height = 300; + ctx = canvas.getContext("2d"); + + // find the center of the window + center_x = canvas.width / 2; + center_y = canvas.height / 2; + radius = 40; + + // style the background + var gradient = ctx.createLinearGradient(0, 0, 0, canvas.height); + gradient.addColorStop(0, "rgba(35, 7, 77, 1)"); + gradient.addColorStop(1, "rgba(204, 83, 51, 1)"); + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + //draw a circle + ctx.beginPath(); + ctx.arc(center_x, center_y, radius, 0, 2 * Math.PI); + ctx.fillStyle = 'white'; + ctx.fill(); + + if (analyser !== undefined) { + analyser.getByteFrequencyData(frequency_array); + + for (var i = 0; i < bars; i++) { + + //divide a circle into equal parts + rads = Math.PI * 2 / bars; + + bar_height = frequency_array[i] * 0.7; + + // set coordinates + x = center_x + Math.cos(rads * i) * (radius); + y = center_y + Math.sin(rads * i) * (radius); + x_end = center_x + Math.cos(rads * i) * (radius + bar_height); + y_end = center_y + Math.sin(rads * i) * (radius + bar_height); + + //draw a bar + drawBar(x, y, x_end, y_end, bar_width, frequency_array[i]); + + } + } + window.requestAnimationFrame(animationLooper); +} + +// for drawing a bar +function drawBar(x1, y1, x2, y2, width, frequency) { + + var lineColor = "rgb(" + frequency + ", " + frequency + ", " + 205 + ")"; + + ctx.strokeStyle = lineColor; + ctx.lineWidth = width; + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); +} \ No newline at end of file diff --git a/public/canvas/test.flac b/public/canvas/test.flac new file mode 100644 index 00000000..87f7e315 Binary files /dev/null and b/public/canvas/test.flac differ diff --git a/public/fizzy/improvedNoise.js b/public/fizzy/improvedNoise.js new file mode 100644 index 00000000..49472bcb --- /dev/null +++ b/public/fizzy/improvedNoise.js @@ -0,0 +1,181 @@ +// http://mrl.nyu.edu/~perlin/noise/ + +var ImprovedNoise = function () { + + var p = [151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10, + 23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87, + 174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211, + 133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208, + 89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,5, + 202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,183,170,213,119, + 248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,224,232, + 178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249, + 14,239,107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,205, + 93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180]; + + for ( var i = 0; i < 256 ; i++ ) { + + p[ 256 + i ] = p[ i ]; + + } + + function fade( t ) { + + return t * t * t * ( t * ( t * 6 - 15 ) + 10 ); + + } + + function lerp( t, a, b ) { + + return a + t * ( b - a ); + + } + + function grad( hash, x, y, z ) { + + var h = hash & 15; + var u = h < 8 ? x : y, v = h < 4 ? y : h == 12 || h == 14 ? x : z; + return ( ( h & 1 ) == 0 ? u : -u ) + ( ( h & 2 ) == 0 ? v : -v ); + + } + + return { + + noise: function ( x, y, z ) { + + var floorX = Math.floor( x ), floorY = Math.floor( y ), floorZ = Math.floor( z ); + + var X = floorX & 255, Y = floorY & 255, Z = floorZ & 255; + + x -= floorX; + y -= floorY; + z -= floorZ; + + var xMinus1 = x -1, yMinus1 = y - 1, zMinus1 = z - 1; + + var u = fade( x ), v = fade( y ), w = fade( z ); + + var A = p[ X ] + Y, AA = p[ A ] + Z, AB = p[ A + 1 ] + Z, B = p[ X + 1 ] + Y, BA = p[ B ] + Z, BB = p[ B + 1 ] + Z; + + return lerp( w, lerp( v, lerp( u, grad( p[ AA ], x, y, z ), + grad( p[ BA ], xMinus1, y, z ) ), + lerp( u, grad( p[ AB ], x, yMinus1, z ), + grad( p[ BB ], xMinus1, yMinus1, z ) ) ), + lerp( v, lerp( u, grad( p[ AA + 1 ], x, y, zMinus1 ), + grad( p[ BA + 1 ], xMinus1, y, z - 1 ) ), + lerp( u, grad( p[ AB + 1 ], x, yMinus1, zMinus1 ), + grad( p[ BB + 1 ], xMinus1, yMinus1, zMinus1 ) ) ) ); + + } + } +} + +var currentRandom = Math.random; + +// Pseudo-random generator +function Marsaglia(i1, i2) { +// from http://www.math.uni-bielefeld.de/~sillke/ALGORITHMS/random/marsaglia-c +var z=i1 || 362436069, w= i2 || 521288629; +var nextInt = function() { + z=(36969*(z&65535)+(z>>>16)) & 0xFFFFFFFF; + w=(18000*(w&65535)+(w>>>16)) & 0xFFFFFFFF; + return (((z&0xFFFF)<<16) | (w&0xFFFF)) & 0xFFFFFFFF; +}; + +this.nextDouble = function() { + var i = nextInt() / 4294967296; + return i < 0 ? 1 + i : i; +}; +this.nextInt = nextInt; +} +Marsaglia.createRandomized = function() { +var now = new Date(); +return new Marsaglia((now / 60000) & 0xFFFFFFFF, now & 0xFFFFFFFF); +}; + +// Noise functions and helpers +function PerlinNoise(seed) { +var rnd = seed !== undefined ? new Marsaglia(seed) : Marsaglia.createRandomized(); +var i, j; +// http://www.noisemachine.com/talk1/17b.html +// http://mrl.nyu.edu/~perlin/noise/ +// generate permutation +var p = new Array(512); +for(i=0;i<256;++i) { p[i] = i; } +for(i=0;i<256;++i) { var t = p[j = rnd.nextInt() & 0xFF]; p[j] = p[i]; p[i] = t; } +// copy to avoid taking mod in p[0]; +for(i=0;i<256;++i) { p[i + 256] = p[i]; } + +function grad3d(i,x,y,z) { + var h = i & 15; // convert into 12 gradient directions + var u = h<8 ? x : y, + v = h<4 ? y : h===12||h===14 ? x : z; + return ((h&1) === 0 ? u : -u) + ((h&2) === 0 ? v : -v); +} + +function grad2d(i,x,y) { + var v = (i & 1) === 0 ? x : y; + return (i&2) === 0 ? -v : v; +} + +function grad1d(i,x) { + return (i&1) === 0 ? -x : x; +} + +function lerp(t,a,b) { return a + t * (b - a); } + +this.noise3d = function(x, y, z) { + var X = Math.floor(x)&255, Y = Math.floor(y)&255, Z = Math.floor(z)&255; + x -= Math.floor(x); y -= Math.floor(y); z -= Math.floor(z); + var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y, fz = (3-2*z)*z*z; + var p0 = p[X]+Y, p00 = p[p0] + Z, p01 = p[p0 + 1] + Z, p1 = p[X + 1] + Y, p10 = p[p1] + Z, p11 = p[p1 + 1] + Z; + return lerp(fz, + lerp(fy, lerp(fx, grad3d(p[p00], x, y, z), grad3d(p[p10], x-1, y, z)), + lerp(fx, grad3d(p[p01], x, y-1, z), grad3d(p[p11], x-1, y-1,z))), + lerp(fy, lerp(fx, grad3d(p[p00 + 1], x, y, z-1), grad3d(p[p10 + 1], x-1, y, z-1)), + lerp(fx, grad3d(p[p01 + 1], x, y-1, z-1), grad3d(p[p11 + 1], x-1, y-1,z-1)))); +}; + +this.noise2d = function(x, y) { + var X = Math.floor(x)&255, Y = Math.floor(y)&255; + x -= Math.floor(x); y -= Math.floor(y); + var fx = (3-2*x)*x*x, fy = (3-2*y)*y*y; + var p0 = p[X]+Y, p1 = p[X + 1] + Y; + return lerp(fy, + lerp(fx, grad2d(p[p0], x, y), grad2d(p[p1], x-1, y)), + lerp(fx, grad2d(p[p0 + 1], x, y-1), grad2d(p[p1 + 1], x-1, y-1))); +}; + +this.noise1d = function(x) { + var X = Math.floor(x)&255; + x -= Math.floor(x); + var fx = (3-2*x)*x*x; + return lerp(fx, grad1d(p[X], x), grad1d(p[X+1], x-1)); +}; +} + +// these are lifted from Processing.js +// processing defaults +var noiseProfile = { generator: undefined, octaves: 4, fallout: 0.5, seed: undefined}; + +function noise(x, y, z) { +if(noiseProfile.generator === undefined) { + // caching + noiseProfile.generator = new PerlinNoise(noiseProfile.seed); +} +var generator = noiseProfile.generator; +var effect = 1, k = 1, sum = 0; +for(var i=0; i o2) v = o2; + return v; + }; + // Called every frame + this.render = function() { + + // What color is the pixel we're sitting on top of? + var c = getColor(this.x, this.y); + + // Where should we move? + var angle = noise(this.x / noiseScale, this.y / noiseScale) * _this.noiseStrength; + + // Are we within the boundaries of the image? + var onScreen = this.x > 0 && this.x < width && + this.y > 0 && this.y < height; + + var isBlack = c != "rgb(255,255,255)" && onScreen; + + // If we're on top of a black pixel, grow. + // If not, shrink. + if (isBlack) { + this.r += _this.growthSpeed; + } else { + this.r -= _this.growthSpeed; + } + + // This velocity is used by the explode function. + this.vx *= 0.5; + this.vy *= 0.5; + + // Change our position based on the flow field and our + // explode velocity. + this.x += Math.cos(angle) * _this.speed + this.vx; + this.y += -Math.sin(angle) * _this.speed + this.vy; + + this.r = this.constrain(this.r, 0, _this.maxSize); + + // If we're tiny, keep moving around until we find a black + // pixel. + if (this.r <= 0) { + this.x = Math.random() * width; + this.y = Math.random() * height; + return; // Don't draw! + } + + // Draw the circle. + g.beginPath(); + g.arc(this.x, this.y, this.r, 0, Math.PI * 2, false); + g.fill(); + + } + + } + +} \ No newline at end of file diff --git a/public/img/368-3683311_larry-the-lobster-clipart.png b/public/img/368-3683311_larry-the-lobster-clipart.png new file mode 100644 index 00000000..89d79651 Binary files /dev/null and b/public/img/368-3683311_larry-the-lobster-clipart.png differ diff --git a/public/img/612-rI+JybL._SY450_.jpg b/public/img/612-rI+JybL._SY450_.jpg new file mode 100644 index 00000000..cd0daeb7 Binary files /dev/null and b/public/img/612-rI+JybL._SY450_.jpg differ diff --git a/public/img/Sandy_Cheeks.jpg b/public/img/Sandy_Cheeks.jpg new file mode 100644 index 00000000..dfca01c2 Binary files /dev/null and b/public/img/Sandy_Cheeks.jpg differ diff --git a/public/img/SpongeBob_SquarePants_-_Pearl_Krabs_promo_art.png b/public/img/SpongeBob_SquarePants_-_Pearl_Krabs_promo_art.png new file mode 100644 index 00000000..b579b1d2 Binary files /dev/null and b/public/img/SpongeBob_SquarePants_-_Pearl_Krabs_promo_art.png differ diff --git a/public/img/YH2qZaMaBk6xp9S5tz8kcC-320-80.png b/public/img/YH2qZaMaBk6xp9S5tz8kcC-320-80.png new file mode 100644 index 00000000..3ac24b59 Binary files /dev/null and b/public/img/YH2qZaMaBk6xp9S5tz8kcC-320-80.png differ diff --git a/public/img/avatar-alt.png b/public/img/avatar-alt.png new file mode 100644 index 00000000..b0bd484b Binary files /dev/null and b/public/img/avatar-alt.png differ diff --git a/public/img/avatar.png b/public/img/avatar.png new file mode 100644 index 00000000..c9e28616 Binary files /dev/null and b/public/img/avatar.png differ diff --git a/public/img/backgroundLeft.jpg b/public/img/backgroundLeft.jpg new file mode 100644 index 00000000..bf1dfc30 Binary files /dev/null and b/public/img/backgroundLeft.jpg differ diff --git a/public/img/backgroundRight.jpg b/public/img/backgroundRight.jpg new file mode 100644 index 00000000..e16bb505 Binary files /dev/null and b/public/img/backgroundRight.jpg differ diff --git a/public/img/bg.jpg b/public/img/bg.jpg new file mode 100644 index 00000000..ff7274e8 Binary files /dev/null and b/public/img/bg.jpg differ diff --git a/public/img/favicon.png b/public/img/favicon.png new file mode 100644 index 00000000..163bb613 Binary files /dev/null and b/public/img/favicon.png differ diff --git a/public/img/login-bg.jpg b/public/img/login-bg.jpg new file mode 100644 index 00000000..624dec6c Binary files /dev/null and b/public/img/login-bg.jpg differ diff --git a/public/img/loginBG.jpg b/public/img/loginBG.jpg new file mode 100644 index 00000000..a85dc369 Binary files /dev/null and b/public/img/loginBG.jpg differ diff --git a/public/img/profPic.jpg b/public/img/profPic.jpg new file mode 100644 index 00000000..5ccf9f15 Binary files /dev/null and b/public/img/profPic.jpg differ diff --git a/public/img/sea.jpg b/public/img/sea.jpg new file mode 100644 index 00000000..885f4e8b Binary files /dev/null and b/public/img/sea.jpg differ diff --git a/public/img/squidward_tentacles_91.png b/public/img/squidward_tentacles_91.png new file mode 100644 index 00000000..dac072c3 Binary files /dev/null and b/public/img/squidward_tentacles_91.png differ diff --git a/public/img/tableBG.jpg b/public/img/tableBG.jpg new file mode 100644 index 00000000..4bdaa67f Binary files /dev/null and b/public/img/tableBG.jpg differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 00000000..bf5fa3ba --- /dev/null +++ b/public/index.html @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + User Login + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/main.css b/public/main.css new file mode 100644 index 00000000..6190093c --- /dev/null +++ b/public/main.css @@ -0,0 +1,62 @@ +body { + font-family: 'Roboto', sans-serif; + background: url("img/loginBG.jpg") no-repeat; +} +.main-section { + margin-top: 140px; + margin-left: 80px; + padding: 0; +} +.modal-content { + background-color: aliceblue; + opacity: .95; + padding: 0 18px; + box-shadow: 0 0 3px #848484; +} +.user-img { + margin-top: -50px; + margin-bottom: 35px; +} +.user-img img { + height: 100px; + width: 100px; + border-radius: 5px; + box-shadow: 0 0 2px #848484; +} +.form-group { + margin-bottom: 25px; +} +.form-group input { + height: 42px; + border-radius: 5px; + border: 0; + font-size: 18px; + padding-left: 54px; +} +.form-group::before { + font-family: 'Font Awesome\ 5 free', serif; + content: "\f007"; + position: absolute; + font-size: 22px; + color: #555e60; + left: 28px; + padding-top: 4px; +} +.form-group:last-of-type::before { + content: "\f023"; +} +button { + width: 42%; + margin: 5px 0 25px; +} +.btn { + background-color: #008CBA; + color: azure; + font-size: 17px; + padding: 7px 14px; + border-radius: 5px; +} +.btn:hover, .btn:focus { + background-color: #008CFF; + color: azure; +} \ No newline at end of file diff --git a/public/main.js b/public/main.js new file mode 100644 index 00000000..eeb1781a --- /dev/null +++ b/public/main.js @@ -0,0 +1,27 @@ +document.getElementById('loginBtn').onclick = function(e) { + fetch('/login', { + method: 'POST', + body: JSON.stringify({ + username: document.querySelector('#username').value, + password: document.querySelector('#password').value + }), + headers: { 'Content-Type': 'application/json' } + }) + .then(res => res.json()) + .then(res => { + console.log(res) + location.href = 'table/table.html' + }) + .catch(err => { + console.log(err) + document.getElementById('hint').innerHTML = 'Invalid Input' + }) + e.preventDefault(); + return false; +}; + +document.getElementById('registerBtn').onclick = function(e) { + location.href = 'register/register.html'; + e.preventDefault(); + return false; +}; \ No newline at end of file diff --git a/public/modify/modify.css b/public/modify/modify.css new file mode 100644 index 00000000..f89fb04b --- /dev/null +++ b/public/modify/modify.css @@ -0,0 +1,58 @@ +#helvetica-demo { + display: flex; + justify-content: end; + margin-bottom: -80px; + margin-left: -30px; +} + +body { + background: #eaeaea +} + +#register-box { + width: 580px; + margin: 0 auto; + margin-top: 5%; + border-radius: 25px; + background: rgba(255, 255, 255, 0.9); + padding: 40px; + box-shadow: 0px 0px 20px #aaaaaa; +} + +#register-form { + width: 500px; +} + +#background { + /* BOX MODEL */ + width: 100%; + height: 450px; + position: absolute; + top: 0; + left: 0; + /* ENHANCEMENT */ + background-image: url('/img/login-bg.jpg'); + background-position: center center; + background-size: cover; + background-repeat: no-repeat; + mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0)); + -webkit-mask-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 1)), to(rgba(0, 0, 0, 0))); + z-index: -1; +} + +.image-preview { + width: 300px; + min-height: 100px; + border: 2px solid #dddddd; + margin-top: 10px; + display: flex; + align-items: center; + justify-content: center; + font-weight: bold; + color: #cccccc; +} + +.image-preview__image { + display: none; + width: 100%; +} \ No newline at end of file diff --git a/public/modify/modify.html b/public/modify/modify.html new file mode 100644 index 00000000..e9d1f3ba --- /dev/null +++ b/public/modify/modify.html @@ -0,0 +1,115 @@ + + + + + + Modify Profile + + + + + + + + +
+
+
+
+
+
+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ Image Preview + Image Preview +
+
+
+ + + +
+

© CS4241 - Rui & Percy & Zonglin

+
+
+ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/modify/modify.js b/public/modify/modify.js new file mode 100644 index 00000000..1a12418d --- /dev/null +++ b/public/modify/modify.js @@ -0,0 +1,130 @@ +window.onload = function() { + let fizzyText = new FizzyText('Modify Profile') +} + +document.getElementById('cancel-btn').onclick = function(e) { + location.href = '../table/table.html' + e.preventDefault() + return false +} + +document.getElementById('delete-btn').onclick = function(e) { + swal({ + title: "Are you sure?", + text: "Once deleted, you will not be able to recover your account!", + icon: "warning", + buttons: true, + dangerMode: true, + }).then((willDelete) => { + if (willDelete) { + swal("Goodbye!", { + icon: "success", + }).then(() => { + fetch('/delete', { + method: 'POST' + }).then(function(response) { + console.log("Post sent to server: " + response) + location.href = '../index.html' + }) + }) + } else swal("Great!") + }) + e.preventDefault() + return false +} + +fetch('/getYou', { + method: 'GET', + headers: { 'Content-Type': 'application/json' } +}).then(function(response) { + return response.json() +}).then(function(you) { + const name = document.querySelector('#input-nickname') + const age = document.querySelector('#input-age') + const whatsUp = document.querySelector('#input-whatsup') + const genderS = document.getElementsByName('customRadio') + const hobbyS = document.getElementById('select-hobby') + const salary = document.querySelector('#input-salary') + const loc = document.querySelector('#input-location') + const selfIntro = document.querySelector('#input-selfintro') + const img = document.querySelector('img') + + name.defaultValue = you.name + age.defaultValue = you.age + if (you.whatsUp) whatsUp.defaultValue = you.whatsUp + for (let i = 0; i < genderS.length; i++) { + if (genderS[i].value === you.gender) { + genderS[i].checked = true + break + } + } + for (let j = 0; j < hobbyS.options.length; j++) { + if (hobbyS.options[j].value === you.hobby) { + hobbyS.selectedIndex = j + break + } + } + if (you.salary) salary.defaultValue = you.salary + if (you.location) loc.defaultValue = you.location + if (you.selfIntro) selfIntro.defaultValue = you.selfIntro + + let imgSrc + const previewContainer = document.getElementById('imagePreview') + const previewImage = previewContainer.querySelector('.image-preview__image') + const previewDefaultText = previewContainer.querySelector('.image-preview__default-text') + if (you.pic) { + previewDefaultText.style.display = 'none' + previewImage.style.display = 'block' + imgSrc = you.pic + previewImage.setAttribute('src', you.pic) + } + document.getElementById('inpFile').addEventListener('change', function(e) { + const file = this.files[0] + if (file) { + const reader = new FileReader() + previewDefaultText.style.display = 'none' + previewImage.style.display = 'block' + reader.addEventListener('load', function() { + imgSrc = this.result + previewImage.setAttribute('src', this.result) + }) + reader.readAsDataURL(file) + } else { + previewDefaultText.style.display = null + previewImage.style.display = null + previewImage.setAttribute('src', '') + } + e.preventDefault() + return false + }) + + document.getElementById('update-btn').onclick = function(e) { + let gender + for (let i = 0; i < genderS.length; i++) { + if (genderS[i].checked) gender = genderS[i].value + } + let hobby = hobbyS.options[hobbyS.selectedIndex].value + const json = { + name: name.value, + age: parseInt(age.value), + whatsUp: whatsUp.value, + gender: gender, + hobby: hobby, + pic: imgSrc, + salary: salary.value, + location: loc.value, + selfIntro: selfIntro.value + } + fetch(`/updateInfo`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(json) + }).then(function(response) { + swal(`Hello ${name.value}!`, `You have successfully updated your profile!`, "success") + .then(() => location.href = '../profile/profile.html') + console.log("Post sent to server: " + response); + }) + e.preventDefault() + return false + } +}) \ No newline at end of file diff --git a/public/profile/profile.css b/public/profile/profile.css new file mode 100644 index 00000000..b8245d44 --- /dev/null +++ b/public/profile/profile.css @@ -0,0 +1,196 @@ +#background { + /* BOX MODEL */ + width: 100%; + height: 450px; + /* ENHANCEMENT */ + background-image: url('/img/bg.jpg'); + background-position: center center; + background-size: cover; + background-repeat: no-repeat; + mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0)); + -webkit-mask-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 1)), to(rgba(0, 0, 0, 0))); + z-index: -1; + position: relative; +} + +#namebox { + width: 400px; + height: 50px; + background: black; + color: white; + margin: 0 auto; + text-align: center; + font-size: 24pt; + margin-bottom: 20px; +} + +#handwritten { + margin: 0 auto; + width: 500px; + text-align: center; + font-style: bold; + font-size: 18pt; + font-family: 'Great Vibes', cursive; + margin-bottom: 100px; +} + +body { + font-family: 'Montserrat', sans-serif; + background: #eeeeee; +} + +.content { + margin: -220px 5% 100px; +} + +.to-top { + position: fixed; + bottom: 90px; + right: 20px; +} + +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} + +#footer { + display: flex; + justify-content: space-around; + background-color: #3c3d41; + color: #fff; + padding-top: 20px; + padding-bottom: 10px; +} + +#footer a { + color: #d2d1d1c0; + text-decoration: none; +} + +#footer a:hover { + color: #fff; +} + +nav .btn { + margin-left: 10px; +} + +#profile-box { + margin: 0 auto; + /* margin-top: 5%; */ + width: 90%; + border-radius: 25px; + background: rgba(255, 255, 255, 0.9); + padding: 40px; + box-shadow: 0px 0px 20px #aaaaaa; +} + +#avatar { + position: absolute; + width: 200px; + height: 200px; + /* background-image: url('/img/avatar.png'); */ + background-position: center center; + background-size: 200px; + border-radius: 20px; + box-shadow: 0px 0px 15px black; + background-color: white; + margin-left: 100px; + margin-top: -120px; +} + +#personal-msg { + font-style: bold; + font-size: 20pt; + font-family: 'Nothing You Could Do', cursive; + margin-left: 380px; +} + +#name { + position: absolute; + font-family: 'Lato', sans-serif; + font-size: 24pt; + margin-left: 380px; + margin-top: -100px; +} + +#profile { + font-size: 20px; + padding: 40px; +} + +#profile>div { + margin-top: 20px; +} + +#profile h2 { + margin-top: 20px; + border-bottom: solid 1px #3c3d41; + display: flex; + justify-content: space-between; +} + +#stats { + padding: 0px 100px; + display: flex; + justify-content: space-around; +} + +#basic-info { + padding: 0px 100px; +} + +.toast { + margin: 0 auto; + max-width: 500px; +} + +.toast img { + width: 40px; + height: 40px; +} + + +/* Font Awesome Icon */ + +#edit-info { + color: #5b5b5e; +} + +/* #edit-info:hover { + color: black; +} */ + +#comment-ico { + color: #5b5b5e; +} + +/* #comment-ico:hover { + color: black; +} */ + +#like-ico { + color: rgb(255, 67, 67); +} + +#like-ico:hover { + color: rgb(255, 0, 0); +} + +#up-ico { + color: rgb(83, 143, 255) +} + +#up-ico:hover { + color: rgb(0, 102, 255); +} + +#down-ico { + color: rgb(255, 175, 83) +} + +#down-ico:hover { + color: rgb(255, 145, 0); +} \ No newline at end of file diff --git a/public/profile/profile.html b/public/profile/profile.html new file mode 100644 index 00000000..cd7e0a13 --- /dev/null +++ b/public/profile/profile.html @@ -0,0 +1,163 @@ + + + + + + Profile + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+

+ + +

+

+ + +

+

+ + +

+
+

Basic Info + +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Gender
Age
Hobby
Salary
Location
Self-Intro
+
+

Comments + +

+
+ +
+ + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/profile/profile.js b/public/profile/profile.js new file mode 100644 index 00000000..c5f30cc3 --- /dev/null +++ b/public/profile/profile.js @@ -0,0 +1,135 @@ +window.onload = function() { + $('.toast').toast({ autohide: false }) + $('.toast').toast('show') + document.getElementById('goTop-btn').onclick = function() { + document.body.scrollTop = 0 + document.documentElement.scrollTop = 0 + } +} + +function refreshProfile() { + fetch('/getYou', { + method: 'GET', + headers: { 'Content-Type': 'application/json' } + }).then(function(response) { + return response.json(); + }).then(function(you) { + let user + fetch('/refreshAll', { + method: 'GET', + headers: { 'Content-Type': 'application/json' } + }).then(function(response) { + return response.json() + }).then(function(data) { + data.map(function(row) { + if (row._id === localStorage.id) user = row + }) + + if (user.pic) document.getElementById('avatar').style.backgroundImage = `url(${user.pic})` + document.getElementById('name').innerHTML = user.name + document.getElementById('name2').innerHTML = you.name + document.getElementById('favCount').innerHTML = user.favCount + document.getElementById('likes').innerHTML = user.likes + document.getElementById('dislikes').innerHTML = user.dislikes + document.getElementById('gender').innerHTML = user.gender + document.getElementById('age').innerHTML = user.age + document.getElementById('hobby').innerHTML = user.hobby + document.getElementById('salary').innerHTML = user.salary + document.getElementById('location').innerHTML = user.location + document.getElementById('self-intro').innerHTML = user.selfIntro + document.getElementById('personal-msg').innerHTML = user.whatsUp + document.getElementById('comment-img').src = you.pic + + // const heart = document.getElementById('like-ico') + // if (user._id === you._id) { + // heart.className = 'fa fa-heart' + // heart.style.color = 'red' + // } else { + // if (you.likedList.includes(user.name)) { + // heart.className = 'fa fa-heart' + // heart.style.color = 'red' + // } else { + // console.log('here') + // heart.className = 'fa fa-heart-o' + // heart.style.color = 'black' + // } + // } + // heart.onclick = function(e) { + // if (heart.className === 'fa fa-heart') { + // heart.className = 'fa fa-heart-o'; + // heart.style.color = 'black'; + // you.favCount -= 1 + // you.likedList = you.likedList.filter((value => { return value !== user.username })) + // } else { + // heart.className = 'fa fa-heart'; + // heart.style.color = 'red'; + // you.favCount += 1 + // you.likedList.push(user.username) + // } + // update(you) + // e.preventDefault() + // return false + // } + document.getElementById('up-ico').onclick = function(e) { + user.likes += 1 + update(user) + e.preventDefault() + return false + } + document.getElementById('down-ico').onclick = function(e) { + user.dislikes += 1 + update(user) + e.preventDefault() + return false + } + + const update = function(user) { + fetch('/updateUser', { + method: 'POST', + body: JSON.stringify(user), + headers: { 'Content-Type': 'application/json' } + }).then(function(response) { + console.log("Post sent to server: " + response) + refreshProfile() + }) + } + document.getElementById('comment-btn').onclick = function(e) { + user.comments.push([you.pic, you.name, document.getElementById('comment-text').value]) + update(user) + e.preventDefault() + return false + } + + const comments = document.getElementById('comments') + comments.innerHTML = '' + const createNode = function(element) { return document.createElement(element) } + const append = function(parent, el) { return parent.appendChild(el) } + user.comments.map(function(comment) { + let img = createNode('img') + img.src = comment[0] + img.className = 'rounded mr-2' + + let strong = createNode('strong') + strong.innerHTML = comment[1] + strong.className = 'mr-auto' + + let header = createNode('div') + header.className = 'toast-header' + append(header, img) + append(header, strong) + + let body = createNode('div') + body.innerHTML = comment[2] + body.className = 'toast-body' + + let toast = createNode('div') + toast.role = 'alert' + toast.className = 'toast fade show' + append(toast, header) + append(toast, body) + append(comments, toast) + }) + }) + }) +} +refreshProfile() \ No newline at end of file diff --git a/public/register/register.css b/public/register/register.css new file mode 100644 index 00000000..3ba212a8 --- /dev/null +++ b/public/register/register.css @@ -0,0 +1,57 @@ +body { + background: #eaeaea +} + +#register-box { + width: 580px; + margin: 0 auto; + margin-top: 5%; + border-radius: 25px; + background: rgba(255, 255, 255, 0.9); + padding: 40px; + box-shadow: 0px 0px 20px #aaaaaa; +} + +#register-form { + width: 500px; +} + +#background { + /* BOX MODEL */ + width: 100%; + height: 450px; + position: absolute; + top: 0; + left: 0; + /* ENHANCEMENT */ + background-image: url('/img/login-bg.jpg'); + background-position: center center; + background-size: cover; + background-repeat: no-repeat; + mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0)); + -webkit-mask-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 1)), to(rgba(0, 0, 0, 0))); + z-index: -1; +} + +.image-preview { + width: 300px; + min-height: 100px; + border: 2px solid #dddddd; + margin-top: 10px; + display: flex; + align-items: center; + justify-content: center; + font-weight: bold; + color: #cccccc; +} + +.image-preview__image { + display: none; + width: 100%; +} + +#helvetica-demo { + display: flex; + justify-content: end; + margin-bottom: -80px; +} \ No newline at end of file diff --git a/public/register/register.html b/public/register/register.html new file mode 100644 index 00000000..3f2d50c6 --- /dev/null +++ b/public/register/register.html @@ -0,0 +1,105 @@ + + + + + + Personal Information + + + + + + + + +
+
+
+
+
+ +
+
+ + +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ +
+ +
+ Image Preview + Image Preview +
+
+
+ + +
+

© CS4241 - Rui & Percy & Zonglin

+
+
+ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/register/register.js b/public/register/register.js new file mode 100644 index 00000000..510ed215 --- /dev/null +++ b/public/register/register.js @@ -0,0 +1,79 @@ +window.onload = function() { + let fizzyText = new FizzyText('Registration') +} + +document.getElementById('cancel-btn').onclick = function(e) { + location.href = '../index.html'; + e.preventDefault(); + return false; +} + +const username = document.querySelector('#input-username') +const password = document.querySelector('#input-password') +const name = document.querySelector('#input-nickname') +const age = document.querySelector('#input-age') +const genderS = document.getElementsByName('customRadio') +const hobbyS = document.getElementById('select-hobby') + +let imgSrc +const previewContainer = document.getElementById('imagePreview') +const previewImage = previewContainer.querySelector('.image-preview__image') +const previewDefaultText = previewContainer.querySelector('.image-preview__default-text') +document.getElementById('inpFile').addEventListener('change', function(e) { + const file = this.files[0] + if (file) { + const reader = new FileReader() + previewDefaultText.style.display = 'none' + previewImage.style.display = 'block' + reader.addEventListener('load', function() { + imgSrc = this.result + previewImage.setAttribute('src', this.result) + }) + reader.readAsDataURL(file) + } else { + previewDefaultText.style.display = null + previewImage.style.display = null + previewImage.setAttribute('src', '') + } + e.preventDefault() + return false +}) + +const registerBtn = document.getElementById('register-btn') +registerBtn.onclick = function(e) { + let gender + for (let i = 0; i < genderS.length; i++) { + if (genderS[i].checked) gender = genderS[i].value + } + let hobby = hobbyS.options[hobbyS.selectedIndex].value + const json = { + username: username.value, + password: password.value, + name: name.value, + age: parseInt(age.value), + gender: gender, + hobby: hobby, + likedList: [], + blackList: [], + comments: [], + likes: 0, + dislikes: 0, + pic: imgSrc, + favCount: 0, + whatsUp: null, + salary: null, + location: null, + selfIntro: null + } + fetch(`/add`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(json) + }).then(function(response) { + swal(`Hello ${name.value}!`, `You have successfully registered!`, "success") + .then(() => location.href = '../index.html') + console.log("Post sent to server: " + response) + }) + e.preventDefault() + return false +} \ No newline at end of file diff --git a/public/table/table.html b/public/table/table.html new file mode 100644 index 00000000..06e9e0e5 --- /dev/null +++ b/public/table/table.html @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + Overview + + + + +
+
+ +
+
+ +
+
+
+
+ +
+
+
+

+ + 123 +

+

+ + 456 +

+

+ + 789 +

+
+
+ + +
+
+
+ +
+
+
+
+ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/table/tables.css b/public/table/tables.css new file mode 100644 index 00000000..a883562e --- /dev/null +++ b/public/table/tables.css @@ -0,0 +1,185 @@ +#helvetica-demo { + display: flex; + justify-content: center; + margin-left: 530px; + margin-bottom: -80px; +} + +#background { + /* BOX MODEL */ + width: 100%; + height: 450px; + /* ENHANCEMENT */ + background-image: url('/img/sea.jpg'); + background-position: center center; + background-size: cover; + background-repeat: no-repeat; + mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1), rgba(0, 0, 0, 0)); + -webkit-mask-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 1)), to(rgba(0, 0, 0, 0))); + z-index: -1; +} + +#namebox { + width: 400px; + height: 50px; + background: black; + color: white; + margin: 0 auto; + text-align: center; + font-size: 24pt; + margin-bottom: 20px; +} + +#handwritten { + margin: 0 auto; + width: 500px; + text-align: center; + font-style: bold; + font-size: 18pt; + font-family: 'Great Vibes', cursive; + margin-bottom: 100px; +} + + +/* .avatar { + width: 200px; + height: 200px; + margin: 30px; + background-image: url('/img/avatar.png'); + background-position: center center; + background-size: 200px; +} + +.avatar:hover { + background-image: url('/img/avatar-alt.png'); +} */ + +body { + font-family: 'Montserrat', sans-serif; +} + +.content { + background: white; + margin: -220px 5% 100px; +} + +.to-top { + position: fixed; + bottom: 90px; + right: 20px; +} + +.center-block { + display: block; + margin-left: auto; + margin-right: auto; +} + +#table img { + width: 60px; + height: 60px; + background-position: center center; + background-size: 60px; + border-radius: 30px; + box-shadow: 0px 0px 10px #222222; + background-color: white; +} + +#table td { + vertical-align: middle; +} + +#footer { + display: flex; + justify-content: space-around; + background-color: #3c3d41; + color: #fff; + padding-top: 20px; + padding-bottom: 10px; +} + +#footer a { + color: #d2d1d1c0; + text-decoration: none; +} + +#footer a:hover { + color: #fff; +} + +#modify-group { + display: none; + text-align: center; +} + +#modify-btn-group { + display: flex; +} + +#people-form td { + display: flex; +} + +nav .btn { + margin-left: 10px; +} + +#info-name { + font-size: 28pt; +} + +#info-pic { + width: 80px; + height: 80px; + background-position: center center; + background-size: 80px; + border-radius: 20px; + box-shadow: 0px 0px 15px black; + background-color: white; +} + +#stats { + display: flex; + justify-content: space-around; +} + +#renderer { + width: 300px; + height: 300px; + background-position: center center; + background-size: 200px; + border-radius: 150px; + box-shadow: 0px 0px 15px black; + background-color: white; +} + +#playme { + position: absolute; + margin-left: 110px; + margin-top: 110px; + width: 80px; + height: 80px; + text-align: center; + padding: 6px 0; + line-height: 1.42; + border-radius: 80px; +} + +#player { + margin: 0 auto; + width: 300px; +} + +/* Font Awesome Icon */ + +#like-ico { + color: rgb(255, 67, 67); +} + +#up-ico { + color: rgb(83, 143, 255) +} + +#down-ico { + color: rgb(255, 175, 83) +} \ No newline at end of file diff --git a/public/table/tables.js b/public/table/tables.js new file mode 100644 index 00000000..1336ba92 --- /dev/null +++ b/public/table/tables.js @@ -0,0 +1,286 @@ +fetch('/getYou', { + method: 'GET', + headers: { 'Content-Type': 'application/json' } +}).then(function(response) { + return response.json(); +}).then(function(you) { + localStorage.id = you._id + document.getElementById('handwritten').innerHTML = 'Welcome, ' + you.name + '!' + document.getElementById('favCount').innerHTML = you.favCount + document.getElementById('likes').innerHTML = you.likes + document.getElementById('dislikes').innerHTML = you.dislikes + + document.getElementById('goTop-btn').onclick = function() { + document.body.scrollTop = 0 + document.documentElement.scrollTop = 0 + } + document.getElementById('ovLink').onclick = function() { + document.title = 'Overview' + refresh() + } + document.getElementById('favLink').onclick = function() { + document.title = 'Favorite' + refresh() + } + document.getElementById('blackLink').onclick = function() { + document.title = 'Black List' + refresh() + } + + const table = document.getElementById('table') + + const createNode = function(element) { return document.createElement(element) } + const append = function(parent, el) { return parent.appendChild(el) } + const makeHeadings = function() { + let thead = createNode('thead') + thead.className = 'thead-dark' + + let th1 = createNode('th'); + let th2 = createNode('th'); + let th3 = createNode('th'); + let th4 = createNode('th'); + let th5 = createNode('th'); + let th6 = createNode('th'); + let th7 = createNode('th'); + let th8 = createNode('th'); + let th9 = createNode('th'); + + th1.innerHTML = 'Picture' + th2.innerHTML = 'Name' + th3.innerHTML = 'Gender' + th4.innerHTML = 'Age' + th5.innerHTML = 'Hobby' + th6.innerHTML = 'Likes' + th7.innerHTML = 'Dislikes' + th8.innerHTML = 'Match Score' + th9.innerHTML = 'Operations' + + let tr = createNode('tr'); + append(tr, th1); + append(tr, th2); + append(tr, th3); + append(tr, th4); + append(tr, th5); + append(tr, th6); + append(tr, th7); + append(tr, th8); + append(tr, th9); + append(thead, tr) + append(table, thead) + } + + const makeGender = function(row) { + let gender = createNode('i') + if (row.gender === 'Male') gender.className = 'fa fa-male' + else gender.className = 'fa fa-female' + return gender + } + + const update = function(user) { + fetch('/updateUser', { + method: 'POST', + body: JSON.stringify(user), + headers: { 'Content-Type': 'application/json' } + }).then(function(response) { + console.log("Post sent to server: " + response) + }) + } + + const makeHeart = function(row) { + let heart = createNode('i'); + if (you.likedList.includes(row.username)) { + heart.className = 'fa fa-heart'; + heart.style.color = 'red'; + } else { + heart.className = 'fa fa-heart-o'; + heart.style.color = 'black'; + } + + heart.onclick = function(e) { + console.log(you) + if (heart.className === 'fa fa-heart') { + heart.className = 'fa fa-heart-o'; + heart.style.color = 'black'; + row.favCount -= 1 + update(row) + you.likedList = you.likedList.filter((value => { return value !== row.username })) + update(you) + refresh() + } else { + heart.className = 'fa fa-heart'; + heart.style.color = 'red'; + row.favCount += 1 + update(row) + you.likedList.push(row.username) + update(you) + refresh() + } + e.preventDefault() + return false + }; + return heart; + }; + const makeBomb = function(row) { + let bomb = createNode('i') + if (document.title === 'Black List') bomb.className = 'fa fa-recycle'; + else bomb.className = 'fa fa-bomb'; + + bomb.onclick = function(e) { + if (bomb.className === 'fa fa-bomb') { + you.blackList.push(row.username) + update(you) + refresh() + } else { + you.blackList = you.blackList.filter((value => { return value !== row.username })) + update(you) + refresh() + } + e.preventDefault(); + return false; + }; + return bomb + } + const makeComment = function(row) { + let comment = createNode('i') + comment.className = 'fas fa-comments' + comment.onclick = function(e) { + let comment = prompt("Please enter your comment", "You look hot!") + if (comment !== null && comment !== "") { + alert("You have made the following comment: \n" + comment) + row.comments.push([you.pic, you.name, comment]) + update(row) + } else alert('Comment canceled') + e.preventDefault() + return false + } + return comment + } + const makeLike = function(row) { + let like = createNode('i') + like.className = 'fa fa-thumbs-o-up' + like.onmouseover = function() { like.className = 'fa fa-thumbs-up' } + like.onmouseleave = function() { like.className = 'fa fa-thumbs-o-up' } + like.onclick = function(e) { + row.likes += 1 + update(row) + refresh() + e.preventDefault() + return false + } + return like + } + const makeDislike = function(row) { + let dislike = createNode('i') + dislike.className = 'fa fa-thumbs-o-down' + dislike.onmouseover = function() { dislike.className = 'fa fa-thumbs-down' } + dislike.onmouseleave = function() { dislike.className = 'fa fa-thumbs-o-down' } + dislike.onclick = function(e) { + row.dislikes += 1 + update(row) + refresh() + e.preventDefault() + return false + } + return dislike + } + const calculateScore = function(row) { + let score = 0; + if (you.gender === row.gender) score -= 30; + else score += 30; + if (you.hobby === row.hobby) score += 10; + else score -= 10; + score -= Math.abs(you.age - row.age); + return score; + } + const makeImg = function(row) { + let img = createNode('img') + if (row.pic) img.src = row.pic + img.onclick = function(e) { + let id = row._id + console.log(id) + localStorage.id = id + location.href = '../profile/profile.html' + e.preventDefault() + return false + } + return img + } + const makeRow = function(row) { + let tr = createNode('tr') + let td1 = createNode('th') + let td2 = createNode('th') + let td3 = createNode('th') + let td4 = createNode('th') + let td5 = createNode('th') + let td6 = createNode('th') + let td7 = createNode('th') + let td8 = createNode('th') + let td9 = createNode('th') + + let space1 = createNode('span') + let space2 = createNode('span') + space1.innerHTML = '\xa0\xa0' + space2.innerHTML = '\xa0\xa0' + + append(td1, makeImg(row)) + td2.innerHTML = row.name + append(td3, makeGender(row)) + td4.innerHTML = row.age + td5.innerHTML = row.hobby + td6.innerHTML = row.likes + ' ' + append(td6, makeLike(row)) + td7.innerHTML = row.dislikes + ' ' + append(td7, makeDislike(row)) + td8.innerHTML = calculateScore(row) + append(td9, makeHeart(row)) + append(td9, space1) + append(td9, makeComment(row)) + append(td9, space2) + append(td9, makeBomb(row)) + + append(tr, td1) + append(tr, td2) + append(tr, td3) + append(tr, td4) + append(tr, td5) + append(tr, td6) + append(tr, td7) + append(tr, td8) + append(tr, td9) + + return tr + } + + const refresh = function() { + fetch('/refreshAll', { + method: 'GET', + headers: { 'Content-Type': 'application/json' } + }).then(function(response) { + return response.json() + }).then(function(data) { + table.innerHTML = ""; + makeHeadings() + data.map(function(row) { + if (row.username !== you.username) { + switch (document.title) { + case 'Overview': + if (!you.blackList.includes(row.username)) append(table, makeRow(row)); + break; + case 'Favorite': + if (you.likedList.includes(row.username)) append(table, makeRow(row)); + break; + case 'Black List': + if (you.blackList.includes(row.username)) append(table, makeRow(row)); + break; + default: + console.log('default') + } + } + }) + }) + } + refresh() + + document.getElementById('info-name').innerHTML = "Hi, " + you.name + "!"; + document.getElementById('info-pic').src = you.pic; +}) \ No newline at end of file diff --git a/server.js b/server.js new file mode 100644 index 00000000..548804e7 --- /dev/null +++ b/server.js @@ -0,0 +1,171 @@ +// Setup +const express = require('express') +const app = express() + +const bodyParser = require('body-parser') +app.use(express.static('public')) +app.use(bodyParser.json({ limit: '10mb', extended: true })) +app.use(bodyParser.urlencoded({ limit: '10mb', extended: true })) + +const morgan = require('morgan') +app.use(morgan('tiny')) + +const favicon = require('serve-favicon') +app.use(favicon(__dirname + '/public/img/favicon.png')) + +const cookieParser = require('cookie-parser') +app.use(cookieParser()) + +app.listen(3000) + +// ================================================================================ + +// MongoDB +const mongodb = require('mongodb') +const uri = 'mongodb+srv://percy:percy@percy0-a4fle.mongodb.net/admin?retryWrites=true&w=majority' + +const client = new mongodb.MongoClient(uri, { useNewUrlParser: true, useUnifiedTopology: true }) +let collection = null + +let allUsers +const copyAllUsers = function() { collection.find({}).toArray().then(result => allUsers = result) } +client.connect().then(() => { + return client.db('percy-a5').createCollection('users') +}).then(__collection => { + collection = __collection + return collection.find({}).toArray() +}).then(result => allUsers = result) + +app.use((req, res, next) => { + if (collection !== null) next() + else res.status(503).send() +}) + +// ================================================================================ + +// Passport +const passport = require('passport'); +const Local = require('passport-local').Strategy; +const session = require('express-session'); + +let you = {}; + +const myLocalStrategy = function(username, password, done) { + const user = allUsers.find(__user => __user.username === username); + if (user === undefined) return done(null, false, { message: 'user not found' }); + else if (user.password === password) return done(null, { username, password }); + else return done(null, false, { message: 'incorrect password' }); +}; + +passport.use(new Local(myLocalStrategy)) +passport.initialize() + +passport.serializeUser((user, done) => done(null, user.username)) +passport.deserializeUser((username, done) => { + const user = allUsers.find(u => u.username === username) + if (user !== undefined) done(null, user) + else done(null, false, { message: 'user not found; session not restored' }) +}) +app.use(session({ secret: 'cats cats cats', resave: false, saveUninitialized: false })) +app.use(passport.initialize()) +app.use(passport.session()) +app.post('/test', function(req, res) { + console.log('authenticate with cookie?', req.user) + res.json({ status: 'success' }) +}) + +app.post('/login', passport.authenticate('local'), function(req, res) { + you = allUsers.find(__user => __user.username === req.user.username) + res.json({ status: true }) +}) + +// ================================================================================ + +// GET +app.get('/', function(request, response) { + response.sendFile(__dirname + '/index.html'); +}) +app.get('/checkDup', function(request, response) { + let usernames = [] + for (let i = 0; i < allUsers.length; i++) { + usernames.push(allUsers[i]['username']) + } + response.send(usernames) +}) +app.get('/refreshAll', function(request, response) { + collection.find({}).toArray().then(result => response.json(result)) +}) +app.get('/getYou', function(request, response) { + you = allUsers.find(__user => __user.username === you.username) + response.send(you) +}) + +// ================================================================================ + +// POST +app.post('/add', function(request, response) { + collection.insertOne(request.body).then(result => { + response.json(result) + copyAllUsers() + }) +}) + +app.post('/delete', function(request, response) { + collection.deleteOne({ _id: mongodb.ObjectID(you._id) }).then(result => { + response.json(result) + copyAllUsers() + }) +}) + +app.post('/updateInfo', function(request, response) { + collection.updateOne({ _id: mongodb.ObjectID(you._id) }, { + $set: { + name: request.body.name, + age: request.body.age, + gender: request.body.gender, + hobby: request.body.hobby, + pic: request.body.pic, + salary: request.body.salary, + whatsUp: request.body.whatsUp, + location: request.body.location, + selfIntro: request.body.selfIntro + } + }).then(result => { + response.json(result) + copyAllUsers() + }) +}) +app.post('/updateUser', function(request, response) { + // console.log(request.body) + collection.updateOne({ _id: mongodb.ObjectID(request.body._id) }, { + $set: { + likedList: request.body.likedList, + blackList: request.body.blackList, + comments: request.body.comments, + likes: request.body.likes, + dislikes: request.body.dislikes, + favCount: request.body.favCount + } + }).then(result => { + response.json(result) + copyAllUsers() + }) +}) + +// ================================================================================ + +// Response Time +const responseTime = require('response-time') +const StatsD = require('node-statsd') +const stats = new StatsD() +stats.socket.on('error', function(error) { + console.error(error.stack) +}) +app.use(responseTime(function(req, res, time) { + let stat = (req.method + req.url).toLowerCase() + .replace(/[:.]/g, '') + .replace(/\//g, '_') + stats.timing(stat, time) +})) + +// ================================================================================ \ No newline at end of file