├── .vscode
└── settings.json
├── .gitignore
├── module
├── config
│ └── db_config.example.json
├── md5
│ └── md5.js
└── api
│ └── api.js
├── .idea
├── misc.xml
├── vcs.xml
└── modules.xml
├── node.iml
├── popup.css
├── package.json
├── popup.js
├── app.js
├── library
├── checkbox
│ └── checkbox.js
├── avatar
│ └── avatar.js
├── mouse
│ └── mouse.js
├── common
│ └── common-styles.js
└── slider
│ └── slider.js
├── password-reset.html
├── index.js
├── README.md
├── ui.css
├── primary.sql
└── index.html
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "liveServer.settings.port": 8082
3 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | module/config/db_config.json
2 | node_modules/
3 | .idea/
4 | .DS_Store
5 |
6 |
--------------------------------------------------------------------------------
/module/config/db_config.example.json:
--------------------------------------------------------------------------------
1 | {
2 | "host": "localhost",
3 | "user": "root",
4 | "password": "",
5 | "database": "primary_social"
6 | }
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/node.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/popup.css:
--------------------------------------------------------------------------------
1 | .popup {
2 | display: none;
3 | position: fixed;
4 | z-index: 1;
5 | padding-top: 150px;
6 | left: 0;
7 | top: 0;
8 | width: 100%;
9 | height: 100%;
10 | overflow: auto;
11 | background-color: rgba(0,0,0,0.4);
12 | }
13 |
14 | .popup-tweet {
15 | background-color: #fefefe;
16 | margin: auto;
17 | padding:20px;
18 | border: 1px solid #888;
19 | width: 80%;
20 | }
21 |
22 |
23 | .close {
24 | color: #aaaaaa;
25 | float: right;
26 | font-size: 28px;
27 | font-weight: bold;
28 | }
29 |
30 | .close:hover,
31 | .close:focus {
32 | color: #000;
33 | text-decoration: none;
34 | cursor: pointer;
35 | }
36 |
37 | .popup-save{
38 | background-color: #fefefe;
39 | margin:auto;
40 | margin-top: 10px;
41 | padding: 10px 30px;
42 | border: 1px solid #888;
43 | width: 60%;
44 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "primary",
3 | "version": "1.0.0",
4 | "description": "Build out a relatively simple (based on the pattern formed by the 3 already-existing files: **index.html**, **index.js**, **api.js** / mysql.js) API endpoint server. Our **first goal** is to figure out what should all commonplace API endpoints be (login,logout,updatebio,tweet,comment,like,retweet,etc...)",
5 | "main": "index.js",
6 | "dependencies": {
7 | "ip": "^1.1.5",
8 | "mysql": "^2.17.1",
9 | "sha3": "^2.0.4"
10 | },
11 | "devDependencies": {},
12 | "scripts": {
13 | "test": "echo \"Error: no test specified\" && exit 1"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/javascriptteacher/primary.git"
18 | },
19 | "author": "Hussain",
20 | "license": "ISC",
21 | "bugs": {
22 | "url": "https://github.com/javascriptteacher/primary/issues"
23 | },
24 | "homepage": "https://github.com/javascriptteacher/primary#readme"
25 | }
26 |
--------------------------------------------------------------------------------
/popup.js:
--------------------------------------------------------------------------------
1 | const firstPopup = document.getElementById("firstPopup");
2 | const secondPopup = document.getElementById("secondPopup");
3 |
4 | //buttons
5 | const openPopup = document.getElementById("openPopup");
6 | const tweet = document.getElementById('tweet');
7 | const closeSpan = document.getElementsByClassName("close")[0];
8 | const yesButton = document.getElementsByClassName('yes')[0];
9 | const noButton = document.getElementsByClassName('no')[0];
10 |
11 | openPopup.addEventListener('click', ()=>{
12 | firstPopup.style.display = "block";
13 | })
14 |
15 | tweet.addEventListener('click', ()=>{
16 | firstPopup.style.display = 'none';
17 | });
18 |
19 | closeSpan.addEventListener('click', ()=>{
20 | secondPopup.style.display = "block";
21 | })
22 |
23 | yesButton.addEventListener('click', ()=>{
24 | secondPopup.style.display ='none';
25 | firstPopup.style.display = 'none';
26 | })
27 |
28 | noButton.addEventListener('click', ()=>{
29 | secondPopup.style.display ="none";
30 | })
31 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | const popUp1 = document.querySelector("#pop-up1"); // href handling the click event
2 | const popUp2 = document.querySelector("#pop-up2"); // href handling the click event
3 |
4 | const POP1 = document.querySelector("#POP1"); // Popup to be displayed
5 | const POP2 = document.querySelector("#POP2"); // Popup2 to be displayed
6 |
7 | const pop1Close = document.querySelector("button#pop1Close");
8 | const pop2Close = document.querySelector("button#pop2Close");
9 |
10 | popUp1.addEventListener('click', (e) => {
11 | e.preventDefault();
12 | POP1.style.display="block";
13 | })
14 |
15 | popUp2.addEventListener('click', (e) => {
16 | e.preventDefault();
17 | POP2.style.visibility="visible";
18 | })
19 |
20 | pop1Close.addEventListener('click', () => {
21 | POP1.style.display="none";
22 | })
23 | pop2Close.addEventListener('click', () => {
24 | POP2.style.visibility="hidden";
25 | })
--------------------------------------------------------------------------------
/library/checkbox/checkbox.js:
--------------------------------------------------------------------------------
1 | export class Checkbox {
2 | constructor() { }
3 | // Add event listeners to all checkboxes on the page
4 | static initialize() {
5 | console.log("Initializing checkbox UI (convert all '.checkbox' to a slider UI control)");
6 | // Attach event listener to the checkbox
7 | document.querySelectorAll(".checkbox").forEach(function(obj) {
8 | obj.addEventListener("click", function(event) {
9 | let state = this.getAttribute("data-value");
10 | state === "off" ? this.setAttribute("data-value", "on") : this.setAttribute("data-value", "off");
11 | });
12 | });
13 | // Attach event listener to the checkbox label
14 | document.querySelectorAll(".checkbox-label").forEach(function(obj) {
15 | let labelAttribute = obj.getAttribute("for");
16 | if (labelAttribute) {
17 | obj.addEventListener("click", function(event) {
18 | let checkbox = document.getElementById(labelAttribute);
19 | if (checkbox) {
20 | let state = checkbox.getAttribute("data-value");
21 | state === "off" ? checkbox.setAttribute("data-value", "on") : checkbox.setAttribute("data-value", "off");
22 | }
23 | });
24 | }
25 | });
26 | }
27 | }
--------------------------------------------------------------------------------
/password-reset.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Password Reset
10 |
11 |
12 |
18 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | let http = require('http');
2 | let fs = require('fs');
3 | let path = require('path');
4 |
5 | /* note, mysql must be installed (npm install mysql) and mysql server running on localhost or elsewhere*/
6 | let { API, database } = require('./module/api/api.js');
7 |
8 | database.create();
9 |
10 | process.env.node_env = "localhost";
11 |
12 | // replace xx.xx.xx.xxx with your own remote IP address or localhost (127.0.0.1)
13 | const ip = '127.0.0.1'; // const ip = 'xx.xx.xx.xxx';
14 | const port = process.env.PORT || (process.env.node_env === 'production' ? 80 : 3000);
15 |
16 | http.createServer(function(request, response) {
17 |
18 | console.log('request ', request.url);
19 |
20 | let filename = '.' + request.url;
21 | if (filename == './')
22 | filename = './index.html';
23 |
24 | let extension = String(path.extname(filename)).toLowerCase();
25 | let mime = { '.html': 'text/html',
26 | '.js': 'text/javascript',
27 | '.css': 'text/css',
28 | '.json': 'application/json',
29 | '.png': 'image/png',
30 | '.jpg': 'image/jpg',
31 | '.gif': 'image/gif',
32 | };
33 | let contentType = mime[extension] || 'application/octet-stream';
34 |
35 | fs.readFile(filename, function (error, content) {
36 | if (error) {
37 | if (error.code == 'ENOENT') {
38 | if (API.catchAPIrequest( request.url ))
39 | API.exec(request, response);
40 | else
41 | fs.readFile('./404.html', function (error, content) {
42 | response.writeHead(200, { 'Content-Type': contentType });
43 | response.end(content, 'utf-8');
44 | });
45 | } else {
46 | response.writeHead(500)
47 | response.end('Server error: ' + error.code + ' ..\n');
48 | response.end();
49 | }
50 | }
51 | console.log("API request detecting...");
52 | response.writeHead(200, { 'Content-Type': contentType });
53 | response.end(content, 'utf-8');
54 |
55 | });
56 | }).listen(port, ip);
57 |
58 | process.on('exit', function () { database.connection.end(); console.log('process.exit'); });
59 | process.on('SIGINT', function () { console.log('Ctrl-C...'); database.connection.end(); process.exit(2) });
60 | process.on('uncaughtException', function(e) { console.log(e.stack); database.connection.end(); process.exit(99); });
61 |
62 | console.log('Server running at http://' + ip + ':' + port + '/');
--------------------------------------------------------------------------------
/library/avatar/avatar.js:
--------------------------------------------------------------------------------
1 | const Gender = { Female: 0, Male: 1, Undefined: 2 };
2 |
3 | export class Avatar {
4 | constructor(container_id_name_without_hash) {
5 | this.id = container_id_name_without_hash;
6 | this.target = document.getElementById(container_id_name_without_hash);
7 | this.gender = Gender.UNDEFINED; // not used yet
8 | this.shape = 0; // head shape
9 | this.eyes = 0; //
10 | this.mouth = 0;
11 | this.haircut = 0;
12 | this.glasses = -1;
13 | this.collar = 0;
14 | }
15 | static start_blinking() {
16 | let eye1 = document.getElementById("left_eye");
17 | let eye2 = document.getElementById("right_eye");
18 | eye1.style.height = "16px";
19 | eye2.style.height = "16px";
20 | let direction = 1;
21 | this.timer = setInterval(() => {
22 | let h1 = parseInt(eye1.style.height) - 1;
23 | let h2 = parseInt(eye2.style.height) - 1;
24 | if (direction == -1) {
25 | h1 = parseInt(eye1.style.height) + 1;
26 | h2 = parseInt(eye2.style.height) + 1;
27 | }
28 | eye1.style.height = h1 + "px";
29 | eye2.style.height = h2 + "px";
30 | if (h1 <= 2)
31 | direction = -direction;
32 | if (h1 >= 17) {
33 | clearInterval(this.timer);
34 | this.timer = null;
35 | setTimeout(()=>{Avatar.start_blinking()}, 1000 + 1000 * Math.floor(Math.random() * 5));
36 | }
37 | }, 1);
38 | }
39 | // set_random_color(); this function here is just for fun, to show how to change avatar background color
40 | static set_random_color() {
41 | let r = Math.floor(Math.random() * 255);
42 | let g = Math.floor(Math.random() * 255);
43 | let b = Math.floor(Math.random() * 255);
44 | let rgb = 'rgb(' + r + ', ' + g + ' , ' + b + ')';
45 | let avatar = document.querySelector("#svgavatar"); // this.id
46 | avatar.style.fill = rgb;
47 |
48 | r = Math.floor(Math.random() * 255);
49 | g = Math.floor(Math.random() * 255);
50 | b = Math.floor(Math.random() * 255);
51 | rgb = 'rgb(' + r + ', ' + g + ' , ' + b + ')';
52 | let foxy_mouth = document.querySelector("#foxy-mouth");
53 | foxy_mouth.style.backgroundColor = rgb;
54 |
55 | r = Math.floor(Math.random() * 255);
56 | g = Math.floor(Math.random() * 255);
57 | b = Math.floor(Math.random() * 255);
58 | rgb = 'rgb(' + r + ', ' + g + ' , ' + b + ')';
59 | let bg = document.querySelector("#avatar-background");
60 | bg.style.backgroundColor = rgb;
61 | }
62 | static set = {
63 | eyes: (index) => console.log("Setting eyes to " + index),
64 | shape: (index) => console.log("Setting shape to " + index),
65 | mouth: (index) => console.log("Setting mouth to " + index),
66 | haircut: (index) => console.log("Setting haircut to " + index),
67 | glasses: (index) => console.log("Setting glasses to " + index),
68 | };
69 | }
70 |
--------------------------------------------------------------------------------
/library/mouse/mouse.js:
--------------------------------------------------------------------------------
1 | export class Mouse {
2 | constructor() {
3 | this.current = {x: 0, y: 0}; // current mouse position on the screen, regardless of state
4 | this.memory = {x: 0, y: 0}; // memorized mouse position (for measuring dragging distance)
5 | this.difference = {x: 0, y: 0}; // difference between last click and current mouse position
6 | this.velocity = {x: 0, y: 0, vx: 0, vy: 0}; // same as above, except not reset after dragging stops
7 | this.inverse = {x: 0, y: 0}; // swapped
8 | this.dragging = false;
9 | this.trackVelocity = false;
10 | this.timer = null;
11 | document.body.addEventListener("mousedown", (e) => {
12 | if (this.dragging == false) {
13 | this.dragging = true;
14 | this.memory.x = this.current.x;
15 | this.memory.y = this.current.y;
16 | this.inverse.x = this.memory.x;
17 | this.inverse.y = this.memory.y;
18 | this.startTrackingVelocity();
19 | }
20 | });
21 | document.body.addEventListener("mouseup", (e) => {
22 | this.dragging = false;
23 | this.current.x = 0;
24 | this.current.y = 0;
25 | this.memory.x = 0;
26 | this.memory.y = 0;
27 | this.difference.x = 0;
28 | this.difference.y = 0;
29 | this.inverse.x = 0;
30 | this.inverse.y = 0;
31 | this.stopTrackingVelocity();
32 | });
33 | document.body.addEventListener("mousemove", (e) => {
34 | this.current.x = e.pageX;
35 | this.current.y = e.pageY;
36 | // this.velocity.x = this.current.x;
37 | // this.velocity.y = this.current.y;
38 | if (this.dragging) {
39 | this.difference.x = this.current.x - this.memory.x;
40 | this.difference.y = this.current.y - this.memory.y;
41 | if (this.current.x < this.memory.x) this.inverse.x = this.current.x;
42 | if (this.current.y < this.memory.y) this.inverse.y = this.current.y;
43 | }
44 | });
45 | }
46 | startTrackingVelocity() {
47 | this.trackVelocity = true;
48 | if (this.timer == null) {
49 | this.timer = setInterval(() => {
50 | let oldx = this.velocity.x;
51 | // this.velocity.x = this.current.x;
52 | // this.velocity.xv = (this.velocity.x - oldx) * 0.1;
53 | }, 35); // 35 is an ideal value, tried others doesn't work so well.
54 | }
55 | }
56 | stopTrackingVelocity() {
57 | if (this.timer != null) {
58 | // this.velocity = {x: 0, y: 0, vx: 0, vy: 0};
59 | clearInterval( this.timer );
60 | this.timer = null;
61 | }
62 | this.trackVelocity = false;
63 | }
64 | static initialize() {
65 | console.log("Initializing global mouse object");
66 | window.mouse = new Mouse();
67 | }
68 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Current API Endpoint Goal
2 | Build out a relatively simple (based on the pattern formed by the 3 already-existing files: **index.html**, **index.js**, **api.js** / mysql.js) API endpoint server. Our **first goal** is to figure out what should all commonplace API endpoints be (login,logout,updatebio,tweet,comment,like,retweet,etc...)
3 |
4 | **Each endpoint has a Trello card associated with it.** So if you need to find something to do, go to **Trello** and look for an empty card :) We have 500 members on our team, but sometimes even if someone is assigned to a card, doesn't mean they're working on it. Whoever posts comments on cards usually gets attention and assignment.
5 |
6 | **Trello board link:** https://trello.com/b/9s3pL5fD/primary
7 |
8 | **Trello invite link:** https://trello.com/invite/b/9s3pL5fD/0abbfbcc4f96a770fba8cb4839783d1e/primary
9 |
10 | An API endpoint is a key feature to the software we're building -- we don't need to have many, just choose a few that we really do need.
11 |
12 | Endpoints are considered completed when they can be tested via buttons on the homepage (index.html file.) To make an endpoint, you have to add it to api.js -- following the same pattern already set by other end points we already created. Make sure to come up proper names, nothing complicated, no camel case.
13 |
14 | ## Current UI Goal
15 | We need to create a vanilla page router first (there is a Trello card for it.)
16 |
17 | ## Primary Social Network
18 | Primary is a social network for every kind of person. Based on the preferences, each user gets its own view and layout of the app, perfectly adjusted to their needs.
19 |
20 | ## Motivation / Vision
21 | Our current vision is to create an API end point server.
22 | Our primary goal right now is to develop a complete API end point server.
23 | General use API end points (login, register, post comment, retweet, etc) are our main goal at this time.
24 |
25 | ## Code style
26 | We have not declared a specific code style yet.
27 | Ask JavaScript Teacher for more information.
28 |
29 | ## Tech/framework used
30 | This project is completely build with vanilla JavaScript and without any framework. We are looking to simplicity.
31 |
32 | ## Features
33 | People from different areas can use the network for its own purpose. A musician for example has another view than a artist or athlete.
34 |
35 | ## Code Example
36 | Coming soon ...
37 |
38 | ## Installation
39 | Install dependencies using **`npm install`**.
40 | Then, **`database`** must be configured. To do that, go to **`module/config/`** folder and create duplicate **`db_config.example.json`** as **`db_config.json`**.
41 | Finally, you can enter your database credentials into **`db_config.json`**.
42 |
43 | ## API Reference
44 | We are publishing some useful links to API references soon ...
45 |
46 | ## Tests
47 | Coming soon ...
48 |
49 | ## Contribute
50 | We are currently working on a contributing file where you can find all information about contributing to this project.
51 |
52 | ## Credits
53 | Thank you to everybody who is involved in this project.
54 | Special thanks to JavaScript Teacher for making this project happen.
55 |
56 | ## License
57 | Every team member can use the code for its own purpose.
58 | However, it is not an open-source project.
59 |
60 | Official license will follow soon.
61 |
--------------------------------------------------------------------------------
/ui.css:
--------------------------------------------------------------------------------
1 | * {
2 | /* margin: 0;
3 | padding: 0; */
4 | box-sizing: border-box;
5 | /* Adding browser prefixes */
6 | -moz-box-sizing: border-box;
7 | -o-box-sizing: border-box;
8 | -webkit-box-sizing: border-box;
9 | -ms-box-sizing: border-box;
10 | }
11 | .mb-5{
12 | margin-bottom: 1em;
13 | }
14 | /* Styling for pop up 1 */
15 | a.button{
16 |
17 | font-size: 13px;
18 | padding: 5px 10px;
19 | border: 1px solid #aaa;
20 | background-color: #eee;
21 | background-image: linear-gradient(top, #fff, #f0f0f0);
22 | border-radius: 2px;
23 | box-shadow: 0 1px 3px rgba(0,0,0,0.15);
24 | color: #666;
25 | text-decoration: none;
26 | text-shadow: 0 1px 0 #fff;
27 | cursor: pointer;
28 | transition: all 0.2s ease-out;
29 | }
30 | .overlay{
31 | position: absolute;
32 | top:0;
33 | right:0;
34 | left:0;
35 | bottom:0;
36 | background: rgba(0, 0, 0, .6);
37 | transition: opacity 200ms;
38 | display: none;
39 | opacity: 1;
40 | }
41 |
42 | .popup1{
43 | width: 500px;
44 | height: 200px;
45 | background: white;
46 | position: relative;
47 | color: black;
48 | margin: auto;
49 | margin-top: 2em;
50 | padding: 1em;
51 | }
52 | .popup1 a{
53 | color: black;
54 | }
55 |
56 | .overlay2{
57 | position: absolute;
58 | top:0;
59 | right:0;
60 | left:0;
61 | bottom:0;
62 | background: rgba(166, 166, 166, 0.6);
63 | display: flex;
64 | transition: opacity 200ms;
65 | visibility: hidden;
66 | opacity: 1;
67 | }
68 |
69 | .popup2{
70 | width: 300px;
71 | height: 200px;
72 | background: white;
73 | position: relative;
74 | color: black;
75 | margin: auto;
76 | padding: 1em;
77 | }
78 | .popup1 a{
79 | color: black;
80 | }
81 |
82 | /* Styling for password Reset here */
83 | body#p-body{
84 | padding: 0;
85 | margin: 0;
86 | }
87 | .p-header{
88 | width: 100%;
89 | min-height: 30px;
90 | box-shadow: 0 0 3px #aaa;
91 | border-bottom: 0 solid rgba(0,0,0,0.25);
92 | padding: 10px;
93 | }
94 | .p-header nav{
95 | margin: auto;
96 | width: 50%;
97 | display: flex;
98 | justify-content: space-between;
99 | align-content: flex-start;
100 | }
101 | .p-header nav.logo img{
102 | border-radius: 50%;
103 | }
104 |
105 | .p-header nav .lang,.lang{
106 | display: flex;
107 | }
108 | .p-header nav .lang span{
109 | margin: auto;
110 | }
111 | .mr{
112 | margin-right: .5em;
113 | }
114 | .reset-password{
115 | height: 80vh;
116 | width: 100%;
117 | }
118 | .reset-password form{
119 | padding: 36px 0;
120 | max-width: 450px;
121 | margin: auto;
122 | }
123 | .reset-password form input{
124 | display: block;
125 | width: fit-content;
126 | border-radius: 18px;
127 | padding: 7px 15px 5px;
128 | line-height: 20px;
129 | border: 1px solid#ccd6dd;
130 | outline: 0;
131 | font-size: 16px;
132 | margin: 5px 10px 5px 0;
133 | }
134 | .reset-password form input:focus{
135 | outline: #0000FF;
136 | }
137 | .reset-password button.btn-primary{
138 | border: none;
139 | cursor: pointer;
140 | color: white;
141 | border-radius: 100px;
142 | box-shadow: none;
143 | cursor: pointer;
144 | font-size: 14px;
145 | font-weight: bold;
146 | line-height: 20px;
147 | padding: 6px 16px;
148 | position: relative;
149 | text-align: center;
150 | white-space: nowrap;
151 | margin-top: 20px;
152 | background: #0000FF;
153 | font-size: 15px;
154 | }
155 |
156 |
157 |
158 | /* End of Password reset styling */
159 |
160 |
--------------------------------------------------------------------------------
/library/common/common-styles.js:
--------------------------------------------------------------------------------
1 | export const id = i => document.getElementById(i);
2 | export const style = i => id(i).style;
3 |
4 | // creates a generic block with absolute position
5 | export const absolute = (id, left, top, w, h, z, right, bottom) => {
6 | // defaults - used to replace missing arguments
7 | const POSITION = 0;
8 | const SIZE = 10;
9 | const Z = 1;
10 |
11 | // create a element dynamically
12 | const div = document.createElement('div');
13 |
14 | // Set ID of the element
15 | div.setAttribute('id', id);
16 |
17 | // Set absolute behavior
18 | div.style.position = 'absolute';
19 | div.style.display = 'block';
20 |
21 | if (right)
22 | // If right is provided, reposition element
23 | div.style.right = right || `${POSITION}px`;
24 | else div.style.left = left || `${POSITION}px`;
25 |
26 | if (bottom)
27 | // If bottom is provided, reposition element
28 | div.style.bottom = bottom || `${POSITION}px`;
29 | else div.style.top = top || `${POSITION}px`;
30 |
31 | // If w,h and z were supplied, use them, otherwise use default values
32 | div.style.width = w ? `${w}px` : `${SIZE}px`;
33 | div.style.height = h ? `${h}px` : `${SIZE}px`;
34 | div.style.zindex = z || Z;
35 |
36 | // Return the object associated with this element
37 | return div;
38 | };
39 |
40 | // creates input element
41 | export const input = (id, left, top, w, h, z, right, bottom) => {
42 | // defaults - used to replace missing arguments
43 | const POSITION = 0;
44 | const SIZE = 10;
45 | const Z = 1;
46 |
47 | // create a
element dynamically
48 | const div = document.createElement('input');
49 |
50 | // Set ID of the element
51 | div.setAttribute('id', id);
52 |
53 | // Set absolute behavior
54 | div.style.position = 'absolute';
55 | div.style.display = 'block';
56 |
57 | if (right)
58 | // If right is provided, reposition element
59 | div.style.right = right || `${POSITION}px`;
60 | else div.style.left = left || `${POSITION}px`;
61 |
62 | if (bottom)
63 | // If bottom is provided, reposition element
64 | div.style.bottom = bottom || `${POSITION}px`;
65 | else div.style.top = top || `${POSITION}px`;
66 |
67 | // If w,h and z were supplied, use them, otherwise use default values
68 | div.style.width = w ? `${w}px` : `${SIZE}px`;
69 | div.style.height = h ? `${h}px` : `${SIZE}px`;
70 | div.style.zindex = z || Z;
71 |
72 | // Return the object associated with this element
73 | return div;
74 | };
75 |
76 | // creates a column-based CSS grid
77 | export const grid = (id, columns, x, y, w, h, z) => {
78 | const DEFAULTPOSITION = 0;
79 | const DEFAULTSIZE = 10;
80 | const DEFAULTZ = 1;
81 | const div = document.createElement('div');
82 | div.setAttribute('id', id);
83 | div.style.position = 'absolute';
84 | div.style.display = 'grid';
85 | let frs = '';
86 | let hei = '';
87 | for (let i = 0; i < columns; i++) {
88 | frs += '1fr';
89 | hei += '32px';
90 | if (i < columns - 1) {
91 | frs += ' ';
92 | hei += ' ';
93 | }
94 | }
95 | div.style.gridTemplateColumns = frs;
96 | div.style.gridTemplateRows = hei;
97 | div.style.left = x || `${DEFAULTPOSITION}px`;
98 | div.style.top = y || `${DEFAULTPOSITION}px`;
99 | div.style.width = w || `${DEFAULTSIZE}px`;
100 | div.style.height = h || `${DEFAULTSIZE}px`;
101 | div.style.zindex = z || DEFAULTZ;
102 | return div;
103 | };
104 |
105 | // creates a grid item
106 | export const gridItem = (id, _class, image, z) => {
107 | const DEFAULTZ = 'unset';
108 | const div = document.createElement('div');
109 | div.setAttribute('id', id);
110 | div.setAttribute('class', _class);
111 | div.style.zindex = z || DEFAULTZ;
112 | return div;
113 | };
114 |
--------------------------------------------------------------------------------
/module/md5/md5.js:
--------------------------------------------------------------------------------
1 | /*
2 | * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
3 | * Digest Algorithm, as defined in RFC 1321.
4 | * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
5 | * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
6 | * Distributed under the BSD License
7 | * See http://pajhome.org.uk/crypt/md5 for more info.
8 | */
9 | var hexcase=0;function md5(a){return rstr2hex(rstr_md5(str2rstr_utf8(a)))}function hex_hmac_md5(a,b){return rstr2hex(rstr_hmac_md5(str2rstr_utf8(a),str2rstr_utf8(b)))}function md5_vm_test(){return hex_md5("abc").toLowerCase()=="900150983cd24fb0d6963f7d28e17f72"}function rstr_md5(a){return binl2rstr(binl_md5(rstr2binl(a),a.length*8))}function rstr_hmac_md5(c,f){var e=rstr2binl(c);if(e.length>16){e=binl_md5(e,c.length*8)}var a=Array(16),d=Array(16);for(var b=0;b<16;b++){a[b]=e[b]^909522486;d[b]=e[b]^1549556828}var g=binl_md5(a.concat(rstr2binl(f)),512+f.length*8);return binl2rstr(binl_md5(d.concat(g),512+128))}function rstr2hex(c){try{hexcase}catch(g){hexcase=0}var f=hexcase?"0123456789ABCDEF":"0123456789abcdef";var b="";var a;for(var d=0;d
>>4)&15)+f.charAt(a&15)}return b}function str2rstr_utf8(c){var b="";var d=-1;var a,e;while(++d>>6)&31),128|(a&63))}else{if(a<=65535){b+=String.fromCharCode(224|((a>>>12)&15),128|((a>>>6)&63),128|(a&63))}else{if(a<=2097151){b+=String.fromCharCode(240|((a>>>18)&7),128|((a>>>12)&63),128|((a>>>6)&63),128|(a&63))}}}}}return b}function rstr2binl(b){var a=Array(b.length>>2);for(var c=0;c>5]|=(b.charCodeAt(c/8)&255)<<(c%32)}return a}function binl2rstr(b){var a="";for(var c=0;c>5]>>>(c%32))&255)}return a}function binl_md5(p,k){p[k>>5]|=128<<((k)%32);p[(((k+64)>>>9)<<4)+14]=k;var o=1732584193;var n=-271733879;var m=-1732584194;var l=271733878;for(var g=0;g>16)+(d>>16)+(c>>16);return(b<<16)|(c&65535)}function bit_rol(a,b){return(a<>>(32-b))};
10 | /*
11 | * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
12 | * Digest Algorithm, as defined in RFC 1321.
13 | * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
14 | * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
15 | * Distributed under the BSD License
16 | * See http://pajhome.org.uk/crypt/md5 for more info.
17 | */
18 |
19 | module.exports = md5;
--------------------------------------------------------------------------------
/primary.sql:
--------------------------------------------------------------------------------
1 | # ************************************************************
2 | # Sequel Pro SQL dump
3 | # Version 5427
4 | #
5 | # https://www.sequelpro.com/
6 | # https://github.com/sequelpro/sequelpro
7 | #
8 | # Host: 127.0.0.1 (MySQL 8.0.13)
9 | # Database: primary
10 | # Generation Time: 2019-10-30 02:38:26 +0000
11 | # ************************************************************
12 |
13 |
14 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
15 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
16 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
17 | /*!40101 SET NAMES utf8 */;
18 | SET NAMES utf8mb4;
19 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
20 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
21 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
22 |
23 |
24 | # Dump of table like
25 | # ------------------------------------------------------------
26 |
27 | DROP TABLE IF EXISTS `like`;
28 |
29 | CREATE TABLE `like` (
30 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
31 | `user_id` int(11) DEFAULT NULL,
32 | `tweet_id` int(11) DEFAULT NULL,
33 | PRIMARY KEY (`id`)
34 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
35 |
36 |
37 |
38 | # Dump of table session
39 | # ------------------------------------------------------------
40 |
41 | DROP TABLE IF EXISTS `session`;
42 |
43 | CREATE TABLE `session` (
44 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
45 | `user_id` int(11) DEFAULT NULL,
46 | `token` varchar(255) DEFAULT NULL,
47 | `timestamp` int(11) DEFAULT NULL,
48 | `expire` varchar(11) DEFAULT 'never',
49 | PRIMARY KEY (`id`)
50 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
51 |
52 | LOCK TABLES `session` WRITE;
53 | /*!40000 ALTER TABLE `session` DISABLE KEYS */;
54 |
55 | INSERT INTO `session` (`id`, `user_id`, `token`, `timestamp`, `expire`)
56 | VALUES
57 | (10,1,'c73f002e978c3cc36bd2f3f6df803ba5',1559606968,'never'),
58 | (11,2,'d866bd5f01fbf8ef508e22cbde1764f9',1559606968,'never'),
59 | (12,1,'token_test_12345',0,'never');
60 |
61 | /*!40000 ALTER TABLE `session` ENABLE KEYS */;
62 | UNLOCK TABLES;
63 |
64 |
65 | # Dump of table token
66 | # ------------------------------------------------------------
67 |
68 | DROP TABLE IF EXISTS `token`;
69 |
70 | CREATE TABLE `token` (
71 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
72 | `token` varchar(64) DEFAULT '',
73 | `user_id` int(11) DEFAULT NULL,
74 | PRIMARY KEY (`id`)
75 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
76 |
77 |
78 |
79 | # Dump of table tweet
80 | # ------------------------------------------------------------
81 |
82 | DROP TABLE IF EXISTS `tweet`;
83 |
84 | CREATE TABLE `tweet` (
85 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
86 | `user_id` int(11) DEFAULT NULL,
87 | `message` char(255) DEFAULT NULL,
88 | `timestamp` int(11) DEFAULT NULL,
89 | PRIMARY KEY (`id`)
90 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
91 |
92 |
93 |
94 | # Dump of table user
95 | # ------------------------------------------------------------
96 |
97 | DROP TABLE IF EXISTS `user`;
98 |
99 | CREATE TABLE `user` (
100 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
101 | `username` varchar(32) DEFAULT NULL,
102 | `email_address` varchar(255) DEFAULT NULL,
103 | `password_md5` varchar(255) DEFAULT NULL,
104 | `first_name` varchar(32) DEFAULT NULL,
105 | `last_name` varchar(32) DEFAULT NULL,
106 | `avatar` varchar(255) DEFAULT NULL COMMENT 'json',
107 | `country` varchar(255) DEFAULT NULL,
108 | `state` varchar(255) DEFAULT NULL,
109 | `city` varchar(255) DEFAULT NULL,
110 | `ip_address` varchar(255) DEFAULT NULL,
111 | `location` varchar(32) DEFAULT NULL,
112 | `bio` varchar(255) DEFAULT NULL,
113 | `website_url` varchar(64) DEFAULT NULL,
114 | `dob` varchar(20) DEFAULT NULL,
115 | PRIMARY KEY (`id`)
116 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
117 |
118 | LOCK TABLES `user` WRITE;
119 | /*!40000 ALTER TABLE `user` DISABLE KEYS */;
120 |
121 | INSERT INTO `user` (`id`, `username`, `email_address`, `password_md5`, `first_name`, `last_name`, `avatar`, `country`, `state`, `city`, `ip_address`, `location`, `bio`, `website_url`, `dob`)
122 | VALUES
123 | (1,'felix','felix@felix.com','482c811da5d5b4bc6d497ffa98491e38','felix','felix','{}','US','California','San Francisco','123','','','',''),
124 | (2,'luna','luna@luna.com','12345','luna','luna','{}','US','California','San Francisco','123','Lagos, Nigeria','This is the bio of the web dev','https://website.com','2019-10-16'),
125 | (3,'felix','felixx@gmail.com','haha123',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
126 | (4,'felix','felixx@gmail.com','haha123',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
127 | (5,'luna1543','luna487@lavacode.com','password',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
128 | (6,'luna5230','luna856@lavacode.com','password',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
129 | (7,'felix8842','felix367@lavacode.com','password',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
130 | (8,'luna2778','luna219@lavacode.com','password',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
131 | (9,'luna8993','luna966@lavacode.com','password','last','233906291','{\"head\":1,\"eyes\":1}',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
132 | (10,'felix3374','felix974@lavacode.com','password','last','6289507725','{\"head\":1,\"eyes\":1}',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
133 | (11,'felix4178','felix807@lavacode.com','password','last','3926883697','{\"head\":1,\"eyes\":1}',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
134 | (47,'luna6270','luna796@lavacode.com','5f4dcc3b5aa765d61d8327deb882cf99','first','last','{\"head\":1,\"eyes\":1}',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
135 | (48,'luna7480','luna603@lavacode.com','5f4dcc3b5aa765d61d8327deb882cf99','first','last','{\"head\":1,\"eyes\":1}',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
136 | (49,'luna7480','luna6t03@lavacode.com','5f4dcc3b5aa765d61d8327deb882cf99','first','last','{\"head\":1,\"eyes\":1}',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
137 | (50,'luna3298','luna382@lavacode.com','5f4dcc3b5aa765d61d8327deb882cf99','first','last','{\"head\":1,\"eyes\":1}',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
138 | (51,'felix7529','felix956@lavacode.com','5f4dcc3b5aa765d61d8327deb882cf99','first','last','{\"head\":1,\"eyes\":1}',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
139 | (52,'felix7419','felix638@lavacode.com','5f4dcc3b5aa765d61d8327deb882cf99','first','last','{\"head\":1,\"eyes\":1}',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
140 | (53,'felix7118','felix182@lavacode.com','5f4dcc3b5aa765d61d8327deb882cf99','first','last','{\"head\":1,\"eyes\":1}',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
141 | (54,'luna4266','luna381@lavacode.com','5f4dcc3b5aa765d61d8327deb882cf99','first','last','{\"head\":1,\"eyes\":1}',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
142 | (55,'','','5f4dcc3b5aa765d61d8327deb882cf99','first','last','{\"head\":1,\"eyes\":1}',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
143 | (56,'fghf','fgh','5f4dcc3b5aa765d61d8327deb882cf99','first','last','{\"head\":1,\"eyes\":1}',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
144 | (57,'tvpeter','withtvpeter@gmail.com','5f4dcc3b5aa765d61d8327deb882cf99','first','last','{\"head\":1,\"eyes\":1}',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
145 | (58,'tvpeter','withtvepter@gmail.com','5f4dcc3b5aa765d61d8327deb882cf99','first','last','{\"head\":1,\"eyes\":1}',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
146 |
147 | /*!40000 ALTER TABLE `user` ENABLE KEYS */;
148 | UNLOCK TABLES;
149 |
150 |
151 |
152 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
153 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
154 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
155 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
156 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
157 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
158 |
--------------------------------------------------------------------------------
/library/slider/slider.js:
--------------------------------------------------------------------------------
1 | import { absolute } from "./../common/common-styles.js";
2 |
3 | export let HORIZONTAL = 0;
4 | export let VERTICAL = 1;
5 |
6 | export class QuadrupleShadow {
7 | constructor() { }
8 | update() {
9 | document.getElementById("specimen_corners").style.boxShadow = code;
10 | if (window.selectedElement) window.selectedElement.style.boxShadow = code;
11 | }
12 | generate_code() { }
13 | }
14 |
15 | export class Slider {
16 |
17 | static initialize() {
18 | console.log("Initializing slider UI (convert all '.slider' to a slider UI control)");
19 | document.querySelectorAll(".slider").forEach(function(obj) {
20 | let id = obj.getAttribute("id");
21 | let width = 200;
22 | let height = 16;
23 | new Slider(window.mouse, "slider_id_" + id, id, "name" + Math.floor(Math.random() * 100), {x:0,y:0}, HORIZONTAL, width, height, 0, 200, 0, false, false);
24 | });
25 | }
26 |
27 | constructor(mouse, // globally available mouse object
28 | id, // unique id to assign to dynamically created element
29 | target, // container object
30 | name, // slider control name
31 | array_xy_pos, // slider position stored in array
32 | direction, // HORIZONTAL or VERTICAL
33 | width, // slider dimensions
34 | height,
35 | min, // min value limit
36 | max, // max value limit
37 | now, // value of the slider now
38 | show_value, // show numeric value in a box to the right of slider
39 | show_name // show name of the slider inside the slider box
40 | ) {
41 |
42 | this.id = id;
43 |
44 | this.mouse = mouse;
45 |
46 | // get element object of the container it will be embedded into
47 | this.container = document.getElementById( target );
48 |
49 | // store a reference to the object the values on this slider control will modify
50 | this.specimen = null;
51 |
52 | this.direction = direction;
53 |
54 | // width of the box where numeric value of this
55 | // slider is displayed (to the right of actual slider)
56 | // *not used if "show_value" argument is false or null
57 | this.value_width = 30;
58 |
59 | // Slider can be horizontal or vertical, horizontal is default, but flip axis if its vertical
60 | if (this.direction == VERTICAL) { let save = width; width = height; height = save; }
61 |
62 | this.width = width;
63 | this.height = height;
64 | this.min = 0;
65 | this.max = 100;
66 | this.now = now;
67 | this.background = { color : "#888" };
68 |
69 | if (this.direction == HORIZONTAL)
70 | this.scrubber = { x : 0, y : 0, width : 25, height: this.height, color : "silver" };
71 |
72 | if (this.direction == VERTICAL)
73 | this.scrubber = { x : 0, y : 0, height : 25, width: this.width, color : "silver" };
74 |
75 | this.canDrag = false;
76 | this.isDragging = false;
77 | this.memory = { x : 0, y : 0};
78 | this.name = name;
79 |
80 | this.val = null;
81 |
82 | // Main slider area
83 | // this.div = document.createElement("div");
84 |
85 | // Create slider container
86 | this.div = absolute(id,
87 | array_xy_pos == null ? 0 : array_xy_pos[0],
88 | array_xy_pos == null ? 0 : array_xy_pos[1],
89 | this.width,
90 | this.height,
91 | 1);
92 |
93 | this.div.style.marginTop = "8px";
94 | // if position is not provided, make this element "relative" (position works only with "absolute" display location)
95 | if (array_xy_pos == null) this.div.style.position = "relative";
96 | this.div.setAttribute("class", "noselect scrubber");
97 | this.div.style.background = this.background.color;
98 | this.div.style.color = "gray";
99 |
100 | if (show_name) // Print name of this control inside it
101 | this.div.innerHTML = "" + name + "
";
102 |
103 | // Determine color and font of the name displayed inside the element
104 | this.div.style.fontFamily = "Arial";
105 | this.div.style.fontSize = "14px";
106 | this.div.style.color = "white";
107 | // Match line height to the height of the element
108 | this.div.style.lineHeight = this.height + "px";
109 | // Add overflow hidden
110 | //this.div.style.overflow = "hidden";
111 |
112 | // Create the draggable knob
113 | this.knob = absolute(id + "_knob",
114 | 0,
115 | this.scrubber.x,
116 | this.scrubber.width,
117 | this.scrubber.height,
118 | 12);
119 | this.knob.setAttribute("class", "scrubber-knob");
120 | this.knob.style.background = this.scrubber.color;
121 | this.knob.style.cursor = "hand";
122 |
123 | // Create value display (if enabled)
124 | if (show_value) {
125 | this.val = absolute(id + "_value",
126 | 0,
127 | 0,
128 | this.value_width,
129 | this.height,
130 | 13,
131 | -this.value_width); // align to right side
132 | this.val.style.background = "black";
133 | this.val.style.border = "0";
134 | this.val.style.color = "#888";
135 | this.val.style.fontFamily = "Arial";
136 | this.val.style.fontSize = "12px";
137 | this.val.style.paddingLeft = "10px";
138 | this.val.style.paddingTop = "2px";
139 | this.val.innerHTML = "0";
140 | this.val.value = this.now;
141 | }
142 |
143 | if (this.val) {
144 | // //console.log("Nest value inside Slider container")
145 | this.div.appendChild( this.val );
146 | }
147 |
148 | // Nest the knob inside the container
149 | this.div.appendChild( this.knob );
150 |
151 | // console.log("this.div = ", this.div);
152 |
153 | // Add the slider to target in the DOM
154 | this.container.appendChild( this.div );
155 |
156 | // Add mouse drag events
157 | this.events();
158 | }
159 |
160 | events() {
161 |
162 | ////console.log("Slider().events() for id = " + this.id);
163 |
164 | // knob
165 | this.knob.addEventListener("mousedown", e => {
166 | if (this.isDragging == false)
167 | {
168 | this.isDragging = true;
169 | this.memory.x = parseInt(this.knob.style.left);
170 | this.memory.y = parseInt(this.knob.style.top);
171 | // //console.log(this.memory);
172 | }
173 | // e.stopPropagation();
174 | e.preventDefault();
175 | });
176 | this.knob.addEventListener("mouseup", e => {
177 | this.isDragging = false;
178 | });
179 |
180 | // main events
181 | document.body.addEventListener("mousemove", e => {
182 |
183 | if (this.isDragging == true) {
184 |
185 | // Current value of this slider
186 | let val, bound;
187 |
188 | // Horizontal
189 | if (this.direction == HORIZONTAL) {
190 | val = this.memory.x + this.mouse.difference.x;
191 | bound = this.width - this.scrubber.width;
192 | if (val < 0) val = 0;
193 | if (val > bound) val = bound;
194 | this.knob.style.left = val + "px";
195 |
196 | // Must be vertical
197 | } else {
198 | val = this.memory.y + this.mouse.difference.y;
199 | bound = this.height - this.scrubber.height;
200 | if (val < 0) val = 0;
201 | if (val > bound) val = bound;
202 | this.knob.style.top = val + "px";
203 | }
204 |
205 | // Keep lime color if mouse is outside of the knob but still dragging
206 | this.knob.style.background = "lime";
207 |
208 | // Update new slider value in slider value display
209 | // if (this.val) this.val.innerHTML = val;
210 |
211 | // Do something with target:
212 | // ...
213 | }
214 | });
215 | document.body.addEventListener("mouseup", e => {
216 | this.isDragging = false;
217 | this.knob.style.background = this.scrubber.color;
218 | });
219 | }
220 | }
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Primary
4 |
5 |
6 |
199 |
205 |
206 |
207 |
208 |
313 |
314 |
315 |
316 |
317 |
--------------------------------------------------------------------------------
/module/api/api.js:
--------------------------------------------------------------------------------
1 | const ip = require('ip');
2 | const mysql = require('mysql');
3 | const db_config = require('./../config/db_config.json');
4 | // Standard MD5 hashing algorithm
5 | const md5 = require('./../md5/md5.js');
6 | // Standard FIPS 202 SHA-3 implementation
7 | const { SHA3 } = require('sha3');
8 | // The Keccak hash function is also available
9 | const { Keccak } = require('sha3');
10 |
11 | // Generate timestamp: if full argument is false/undefined,
12 | // timestamp is divided by 1000 to generate linux-length timestamp
13 | function timestamp(full) {
14 | let date = new Date();
15 | let timestamp = date.getTime();
16 | return full ? Math.floor(timestamp) : Math.floor(timestamp / 1000);
17 | }
18 |
19 | // Generate string "1s", "2h", etc between now and "time" argument
20 | function elapsed(time) {
21 | const $SECONDS = Math.abs(timestamp() - time);
22 | const $iv_table = ["s", "min", "h", "d", "mo", "y", "s", "min", "h", "d", "mo", "y"];
23 | const $iv = [$SECONDS,
24 | ($SECONDS - ($SECONDS % 60)) / 60,
25 | ($SECONDS - ($SECONDS % 3600)) / 3600,
26 | ($SECONDS - ($SECONDS % (3600 * 24))) / (3600 * 24),
27 | ($SECONDS - ($SECONDS % (3600 * 24 * 30))) / (3600 * 24 * 30),
28 | ($SECONDS - ($SECONDS % (3600 * 24 * 30 * 12))) / (3600 * 24 * 30 * 12)];
29 | for (let $i = 5; $i >= 0; $i--) {
30 | $r = $iv[$i];
31 | if ($r > 0) {
32 | if (($r > 1 || $r == 0))
33 | $i += 6;
34 | return $r + "" + $iv_table[$i];
35 | }
36 | }
37 | }
38 |
39 | // Check if property with value exists on an object
40 | Object.prototype.exists = function (property_name, value) {
41 | for (let i = 0; i < this.length; i++) {
42 | let o = this[i];
43 | if (o[property_name] != undefined)
44 | if (o[property_name] == value)
45 | return true;
46 | }
47 | return false;
48 | }
49 |
50 | // Check if value exists in array
51 | Array.prototype.exists = function (value) {
52 |
53 | for (let i = 0; i < this.length; i++)
54 | if (this[i] == value)
55 | return true;
56 | return false;
57 |
58 | }
59 |
60 | class database {
61 | constructor() { }
62 | static create() {
63 | let message = "Creating MySQL connection...";
64 | this.connection = mysql.createConnection(db_config);
65 | this.connection.connect();
66 | console.log(message + "Ok.");
67 | }
68 | }
69 |
70 | // Requires payload.email_address =
71 | function action_register_user(request, payload) {
72 | return new Promise((resolve, reject) => {
73 | if (!request || !request.headers || !payload)
74 | reject("Error: Wrong request, missing request headers, or missing payload");
75 | let q = `SELECT email_address FROM user WHERE email_address = '${payload.email_address}' LIMIT 1`;
76 | database.connection.query(q,
77 | (error, results) => { // Check if user already exists in database
78 | if (error)
79 | throw (error);
80 | let result = results[0];
81 | if (results && results.length != 0 && result.email_address == payload.email_address)
82 | resolve(`{"success": false, "message": "user already exists"}`);
83 | else {
84 | let avatar = JSON.stringify({ "head": 1, "eyes": 1 });
85 | // Encrypt payload.password with md5 algorithm
86 | let password_md5 = md5(payload.password);
87 | let fields = "( `username`, `email_address`, `password_md5`, `first_name`, `last_name`, `avatar` )";
88 | let values = `VALUES( '${payload.username}', '${payload.email_address}', '${password_md5}', 'first', 'last', '${avatar}')`;
89 | database.connection.query("INSERT INTO user " + fields + " " + values,
90 | (error, results) => { // Create new user in database
91 | if (error)
92 | throw (error);
93 | resolve(`{"success": true, "message": "user registered"}`);
94 | });
95 | }
96 | });
97 | }).catch((error) => { console.log(error) });
98 | }
99 |
100 | // Requires payload.id =
101 | function action_get_user(request, payload) {
102 | return new Promise((resolve, reject) => {
103 | if (!request || !request.headers || !payload)
104 | reject("Error: Wrong request, missing request headers, or missing payload");
105 | database.connection.query("SELECT * FROM user WHERE id = '" + payload.id + "' LIMIT 1",
106 | (error, results) => { // Check if user already exists in database
107 | if (error) throw (error);
108 | let result = results[0];
109 | if (results && results.length != 0 && result.id == payload.id) {
110 | result.found = true;
111 | resolve(`{"found": true, "user": ${JSON.stringify(result)}, "message": "user found"}`);
112 | } else
113 | resolve(`{"found": false, "user": null, "message": "user with this id doesn't exist"}`);
114 | });
115 | }).catch(error => console.log(error));
116 | }
117 |
118 | function action_get_user_promiseless(request, payload) {
119 | return new Promise((resolve, reject) => {
120 | if (!request || !request.headers || !payload)
121 | reject("Error: Wrong request, missing request headers, or missing payload");
122 | database.connection.query("SELECT * FROM user WHERE id = '" + payload.id + "' LIMIT 1",
123 | (error, results) => { // Check if user already exists in database
124 | if (error) throw (error);
125 | let result = results[0];
126 | if (results && results.length != 0 && result.id == payload.id) {
127 | resolve(`{"found": true, "user": ${JSON.stringify(result)}, "message": "user found"}`);
128 | } else
129 | resolve(`{"found": false, "user": null, "message": "user with this id doesn't exist"}`);
130 | });
131 | }).catch(error => console.log(error));
132 | }
133 |
134 | function action_delete_user(request, payload) {
135 | return new Promise((resolve, reject) => {
136 | // Header or payload are missing
137 | if (!request || !request.headers || !payload)
138 | reject("Error: Wrong request, missing request headers, or missing payload");
139 | // Payload must specify user id
140 | if (!payload.id)
141 | reject("User id not specified!");
142 | let query = "DELETE from `user` WHERE `id` = " + payload.id;
143 | database.connection.query(query, (error, results) => {
144 | if (error)
145 | throw (error);
146 | let result = results[0];
147 | console.log("results[0] = ", results[0]);
148 | console.log("result = ", result);
149 | resolve(`{"success": true, "message": "user updated!"}`);
150 | });
151 | }).catch(error => console.log(error));
152 | }
153 |
154 | function action_update_user(request, payload) {
155 | return new Promise((resolve, reject) => {
156 | // Header or payload are missing
157 | if (!request || !request.headers || !payload)
158 | reject("Error: Wrong request, missing request headers, or missing payload");
159 | // Payload must specify user id
160 | if (!payload.id)
161 | reject("User id not specified!");
162 | // Columns allowed to be changed:
163 | let allowed = ["id", "email_address", "password_md5"];
164 | // Exclude not-allowed fields from payload
165 | Object.entries(payload).map((value, index, obj) => {
166 | let name = value[0];
167 | if (!allowed.exists(name)) delete payload[name];
168 | });
169 | // Start MySQL query
170 | let query = "UPDATE user SET ";
171 | // Build the rest of MySQL query from payload
172 | Object.entries(payload).map((item, index, object) => {
173 | let name = item[0];
174 | let value = payload[name];
175 | index != 0 ? query += ", " : null;
176 | query += "`" + name + "` = '" + value + "'";
177 | });
178 | // End query
179 | query += " WHERE `id` = '" + payload.id + "'";
180 | // Execute MySQL query we just created
181 | database.connection.query(query, (error, results) => {
182 | if (error)
183 | throw (error);
184 | let result = results[0];
185 | console.log("results[0] = ", results[0]);
186 | console.log("result = ", result);
187 | resolve(`{"success": true, "message": "user updated!"}`);
188 | });
189 |
190 | }).catch(error => null);
191 | }
192 |
193 | function action_login(request, payload) {
194 | return new Promise((resolve, reject) => {
195 | // First, get the user from database by payload.id
196 | let query = `SELECT * FROM \`user\` WHERE \`username\` = '${payload.username}'`;
197 | console.log(query);
198 | database.connection.query(query,
199 | (error, results) => { // Check if user already exists in database
200 | if (error)
201 | throw (error);
202 | let result = results[0];
203 | /* console.log("result = ", result);
204 | console.log("payload.username = ", payload.username);
205 | console.log("payload.password = ", payload.password);
206 | console.log("password 1 = ", md5(payload.password));
207 | console.log("password 2 = ", result.password_md5); */
208 | if (results && results.length != 0 && result.username == payload.username) {
209 | // result.found = true;
210 | // Check if submitted password is correct
211 | if (md5(payload.password) == result.password_md5) {
212 | delete result.email_address; // don't send email to front-end
213 | delete result.password_md5; // don't send md5 password to front-end
214 | resolve(`{"success": true, "user": ${JSON.stringify(result)}, "message": "user successfully logged in!"}`);
215 | } else
216 | resolve(`{"success": false, "user": null, "message": "incorrect username or password"}`);
217 | }
218 | // User not found
219 | resolve(`{"success": false, "user": null, "message": "user with this username(${payload.username}) doesn't exist"}`);
220 | });
221 | }).catch(error => console.log(error));
222 | }
223 |
224 | function action_logout(request, payload) {
225 | return new Promise((resolve, reject) => {
226 | /* implement */
227 | }).catch(error => console.log(error));;
228 | }
229 |
230 | function action_create_session(request, payload) {
231 | // Create unique authentication token
232 | function create_auth_token() {
233 | let token = md5(timestamp(true) + "");
234 | return token;
235 | }
236 | return new Promise((resolve, reject) => {
237 | if (!request || !request.headers || !payload)
238 | reject("Error: Wrong request, missing request headers, or missing payload");
239 | database.connection.query("SELECT * FROM session WHERE user_id = '" + payload.id + "' LIMIT 1",
240 | (error, results) => { // Check if session already exists
241 | if (error) throw (error);
242 | let result = results[0];
243 | if (results && results.length != 0 && result.user_id == payload.id) {
244 | result.found = true;
245 | resolve(`{"found": true,
246 | "token": token,
247 | "session": ${JSON.stringify(result)},
248 | "message": "session already exists"}`);
249 | } else { // This session doesn't exist, create it
250 | // Create auth token
251 | let token = create_auth_token();
252 | database.connection.query("INSERT INTO session ( `user_id`, `timestamp`, `token`) VALUES( '" + payload.id + "', '" + timestamp() + "', '" + token + "')",
253 | (error, results) => {
254 | if (error) throw (error);
255 | resolve(`{"found" : false,
256 | "token" : token,
257 | "user_id": ${payload.user_id},
258 | "message": "session was created"}`);
259 | });
260 | }
261 | });
262 | }).catch(error => { console.log(error) });
263 | }
264 |
265 | // Requirements:
266 | // - payload.user_id_to =
267 | // - payload.user_id_from =
268 | // - payload.dm_conversation_id =
269 | // - payload.message_body =
270 | function send_direct_message(request, payload) {
271 | return new Promise((resolve, reject) => {
272 | if (!request || !request.headers || !payload)
273 | reject("Error: Wrong request, missing request headers, or missing payload");
274 | let values = "VALUES( '" + payload.dm_conversation_id + "', '" + payload.user_id_from + "', '" + payload.user_id_to + "', '" + payload.message_body + "', '" + timestamp() + "' )";
275 | let q = "INSERT INTO direct_messages ( `dm_conversation_id`, `user_id_from`, `user_id_to`, `message_body`, `timestamp`) " + values + "";
276 | database.connection.query(q,
277 | (error, results) => {
278 | if (error) throw (error);
279 | resolve('{"message": "direct message was sent"}');
280 | }
281 | )
282 | }).catch((error) => { console.log(error) })
283 | }
284 |
285 | // Requirements:
286 | // - payload.user_id_to =
287 | // - payload.user_id_from =
288 | function create_dm_conversation(request, payload) {
289 | return new Promise((resolve, reject) => {
290 | if (!request || !request.headers || !payload)
291 | reject("Error: Wrong request, missing request headers, or missing payload");
292 | let values = "VALUES( '" + payload.user_id_from + "', '" + payload.user_id_to + "' )";
293 | database.connection.query("INSERT INTO dm_conversations ( `user_id_from`, `user_id_to` ) " + values + "",
294 | (error, results) => {
295 | if (error) throw (error);
296 | resolve('{"message": "direct message conversation was created"}');
297 | }
298 | )
299 | }).catch((error) => { console.log(error) })
300 | }
301 |
302 | function action_get_session(request, payload) {
303 | return new Promise((resolve, reject) => {
304 | if (!request || !request.headers || !payload)
305 | reject("Error: Wrong request, missing request headers, or missing payload");
306 | database.connection.query("SELECT * FROM session WHERE user_id = '" + payload.id + "' LIMIT 1",
307 | (error, results) => { // Return session
308 | if (error)
309 | throw (error);
310 | let result = results[0];
311 | if (results && results.length != 0 && result.user_id == payload.id) {
312 | result.found = true;
313 | resolve(`{"found": true,
314 | "session": ${JSON.stringify(result)},
315 | "message": "session found"}`);
316 | } else
317 | resolve(`{"found": false, "session": null, "message": "session found"}`);
318 | });
319 | }).catch((error) => { console.log(error) });
320 | }
321 |
322 | function action_authenticate_user(request, payload) {
323 | return new Promise((resolve, reject) => {
324 | if (!request || !request.headers || !payload)
325 | reject("Error: Wrong request, missing request headers, or missing payload");
326 | database.connection.query("SELECT * FROM session WHERE token = '" + payload.token + "' LIMIT 1",
327 | (error, results) => { // Return session
328 | if (error)
329 | throw (error);
330 | if (results.length == 0) {
331 | console.log("API.authenticate, results.length == 0 (session with token not found)");
332 | reject(`{"success": false, "message": "token not found in session"}`);
333 | } else {
334 | //console.log( results );
335 | //console.log( results[0] );
336 | let token = JSON.stringify({ token: results[0].token, type: "admin" });
337 | resolve(`{"success": true, "message": "user (id=${results[0].user_id}) was successfully authenticated", "token" : ${token}}`);
338 | }
339 | });
340 | }).catch((error) => { console.log(error) });
341 | }
342 |
343 | function action_update_user_profile(request, payload) {
344 | return new Promise((resolve, reject) => {
345 | // Header or payload are missing
346 | if (!request || !request.headers || !payload)
347 | reject("Error: Wrong request, missing request headers, or missing payload");
348 | // Payload must specify user id
349 | if (!payload.id)
350 | reject("User id not specified!");
351 | // Columns allowed to be changed:
352 | let allowed = ["id", "location", "website_url", "dob", "bio"];
353 | // Exclude not-allowed fields from payload
354 | Object.entries(payload).map((value, index, obj) => {
355 | let name = value[0];
356 | if (!allowed.exists(name)) delete payload[name];
357 | });
358 | // Start MySQL query
359 | let query = "UPDATE user SET ";
360 | // Build the rest of MySQL query from payload
361 | Object.entries(payload).map((item, index, object) => {
362 | let name = item[0];
363 | let value = payload[name];
364 | index != 0 ? query += ", " : null;
365 | query += "`" + name + "` = '" + value + "'";
366 | });
367 | // End query
368 | query += " WHERE `id` = '" + payload.id + "'";
369 | // Execute MySQL query we just created
370 | database.connection.query(query, (error, results) => {
371 | if (error)
372 | throw (error);
373 | let result = results[0];
374 | console.log("results[0] = ", results[0]);
375 | console.log("result = ", result);
376 | resolve(`{"success": true, "message": "user profile updated successfully!"}`);
377 | });
378 |
379 | }).catch(error => null);
380 | }
381 |
382 |
383 | // Check if API.parts match a URL pattern, example: "api/user/get"
384 | function identify(a, b) {
385 | return API.parts[0] == "api" && API.parts[1] == a && API.parts[2] == b;
386 | }
387 |
388 | // General use respond function -- send json object back to the browser in response to a request
389 | function respond(response, content) {
390 | console.log("responding = ", [content]);
391 | const jsontype = "{ 'Content-Type': 'application/json' }";
392 | response.writeHead(200, jsontype);
393 | response.end(content, 'utf-8');
394 | }
395 |
396 | // Convert buffer to JSON object
397 | function json(chunks) {
398 | return JSON.parse(Buffer.concat(chunks).toString());
399 | }
400 |
401 | class Action { }
402 |
403 | Action.register_user = action_register_user;
404 | Action.login = action_login;
405 | Action.logout = action_logout;
406 | Action.get_user = action_get_user;
407 | Action.delete_user = action_delete_user;
408 | Action.update_user = action_update_user;
409 | Action.authenticate_user = action_authenticate_user;
410 | Action.create_session = action_create_session;
411 | Action.get_session = action_get_session;
412 | Action.create_dm_conversation = create_dm_conversation;
413 | Action.send_direct_message = send_direct_message;
414 | Action.update_profile= action_update_user_profile;
415 |
416 | const resp = response => content => respond(response, content);
417 |
418 | class API {
419 |
420 | constructor() { }
421 |
422 | static exec(request, response) {
423 |
424 | console.log("API.exec(), parts = ", API.parts);
425 |
426 | if (request.method == 'POST') {
427 |
428 | request.url[0] == "/" ? request.url = request.url.substring(1, request.url.length) : null;
429 | request.parts = request.url.split("/");
430 | request.chunks = [];
431 |
432 | // Start reading POST data chunks
433 | request.on('data', segment => {
434 | if (segment.length > 1e6) // 413 = "Request Entity Too Large"
435 | response.writeHead(413, { 'Content-Type': 'text/plain' }).end();
436 | else
437 | request.chunks.push(segment);
438 | });
439 |
440 | // Finish reading POST data chunks
441 | request.on('end', () => { // POST data fully received
442 |
443 | API.parts = request.parts;
444 |
445 | if (identify("user", "register")) // Register (create) user
446 | Action.register_user(request, json(request.chunks))
447 | .then(content => respond(response, content));
448 |
449 | if (identify("user", "login")) // Log in
450 | Action.login(request, json(request.chunks))
451 | .then(content => respond(response, content));
452 |
453 | if (identify("user", "logout")) // Log out
454 | Action.logout(request, json(request.chunks))
455 | .then(content => respond(response, content));
456 |
457 | if (identify("user", "delete")) // Delete user
458 | Action.delete_user(request, json(request.chunks))
459 | .then(content => respond(response, content));
460 |
461 | if (identify("user", "get")) // Get user data
462 | Action.get_user(request, json(request.chunks))
463 | .then(content => respond(response, content));
464 |
465 | if (identify("user", "update")) // Update user
466 | Action.update_user(request, json(request.chunks))
467 | .then(content => respond(response, content));
468 |
469 | if (identify("session", "create")) // Create session
470 | Action.create_session(request, json(request.chunks))
471 | .then(content => respond(response, content));
472 |
473 | if (identify("user", "authenticate")) // Authenticate user
474 | Action.authenticate_user(request, json(request.chunks))
475 | .then(content => respond(response, content));
476 |
477 | if (identify("password", "email")) // Send password reset link
478 | Action.send_reset_link(request, json(request.chunks))
479 | .then(content => respond(response, content));
480 |
481 | if (identify("password", "reset")) // Reset password
482 | Action.reset_password(request, json(request.chunks))
483 | .then(content => respond(response, content));
484 |
485 | if (identify("tweet", "post")) // Post a "tweet"
486 | Action.post_tweet(request, json(request.chunks))
487 | .then(content => respond(response, content));
488 |
489 | if (identify("user", "profile")) // Update user profile
490 | Action.update_profile(request, json(request.chunks))
491 | .then(content => respond(response, content));
492 | });
493 | }
494 |
495 | if (request.method == 'GET') {
496 | /* GET placeholder */
497 | }
498 | }
499 |
500 | static catchAPIrequest(request) {
501 | request[0] == "/" ? request = request.substring(1, request.length) : null;
502 | if (request.constructor === String)
503 | if (request.split("/")[0] == "api") {
504 | API.parts = request.split("/");
505 | return true;
506 | }
507 | return false;
508 | }
509 | }
510 |
511 | API.parts = null;
512 |
513 | module.exports = { API, database };
--------------------------------------------------------------------------------