├── .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 |
13 | 17 |
18 |
19 |
20 |

Find your Primary account

21 |

Enter your email, phone number, or username.

22 | 27 | 28 | 29 |
30 |
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 |
209 |
210 | API Tests
211 | 212 | 213 |
219 |
224 |
229 |
234 |
239 |
244 |
249 |
254 |
259 |
264 |
269 | 270 |
User profile update
271 | 272 | 273 |
274 | 275 | 285 |
286 | 287 |

Pop up Example

288 | 289 | 290 | 291 | 301 | 302 | 312 |
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 }; --------------------------------------------------------------------------------