misc: restructure contents

This commit is contained in:
Yangshun Tay
2019-07-14 18:52:41 -07:00
parent 2e84bea6ba
commit c19c9626d1
83 changed files with 373 additions and 344 deletions

View File

@ -0,0 +1,61 @@
<!doctype html>
<head>
<style>
body {
font-family: 'Helvetica', sans-serif;
}
</style>
</head>
<body>
<div>
<div id="chapters">
</div>
</div>
<script>
(() => {
const DOM = {
$chapters: document.getElementById('chapters'),
};
const URL = 'https://raw.githubusercontent.com/googlesamples/web-fundamentals/gh-pages/fundamentals/getting-started/primers';
function getJSON(url) {
return new Promise((resolve, reject) => {
const req = new XMLHttpRequest();
req.open('GET', url);
req.onload = () => {
if (req.status === 200) {
resolve(JSON.parse(req.response));
return;
}
reject(req.responseText);
}
req.onerror = (err) => {
reject(err);
}
req.send();
});
}
function init() {
getJSON(`${URL}/story.json`).then(story => {
const $heading = document.createRange().createContextualFragment(story.heading);
DOM.$chapters.before($heading);
return Promise.all(story.chapterUrls.map((url, index) => {
return getJSON(`${URL}/${url}`);
}));
}).then(chapters => {
const $chapters = document.createDocumentFragment();
chapters.forEach(chapter => {
$chapters.appendChild(document.createRange().createContextualFragment(chapter.html));
});
DOM.$chapters.appendChild($chapters);
}).catch(err => {
console.warn(err);
});
}
document.addEventListener('DOMContentLoaded', init);
})();
</script>
</body>
</html>

View File

@ -0,0 +1,7 @@
# Databases
## General
- How should you store passwords in a database?
- http://www.geeksforgeeks.org/store-password-database/
- https://nakedsecurity.sophos.com/2013/11/20/serious-security-how-to-store-your-users-passwords-safely/

View File

@ -0,0 +1,5 @@
# Networking
- Given an IPv4 IP address p and an integer n, return a list of CIDR strings that most succinctly represents the range of IP addresses from p to (p + n).
- Describe what happens when you enter a url in the web browser.
- Define UDP/TCP and give an example of both.

View File

@ -0,0 +1,677 @@
const data = [
{
"_id": "591ad7e2d1b6119887420af5",
"age": 36,
"name": "Salas Mccarthy",
"gender": "male",
"company": "MAKINGWAY",
"email": "salasmccarthy@makingway.com",
"phone": "+1 (905) 546-3931"
},
{
"_id": "591ad7e2918b0ba1231b582d",
"age": 29,
"name": "Jodi Graham",
"gender": "female",
"company": "BITREX",
"email": "jodigraham@bitrex.com",
"phone": "+1 (861) 596-2691"
},
{
"_id": "591ad7e2bc19d181dcb64d57",
"age": 34,
"name": "Hollie Hardin",
"gender": "female",
"company": "ZENSURE",
"email": "holliehardin@zensure.com",
"phone": "+1 (822) 410-2433"
},
{
"_id": "591ad7e224d2fa7216132561",
"age": 26,
"name": "Bullock Cole",
"gender": "male",
"company": "ZILPHUR",
"email": "bullockcole@zilphur.com",
"phone": "+1 (839) 563-3350"
},
{
"_id": "591ad7e206c3fb3043ccf8a8",
"age": 30,
"name": "Peterson Mosley",
"gender": "male",
"company": "MOBILDATA",
"email": "petersonmosley@mobildata.com",
"phone": "+1 (974) 547-3461"
},
{
"_id": "591ad7e20a180de908b0960e",
"age": 34,
"name": "Lucille Jackson",
"gender": "female",
"company": "KINETICUT",
"email": "lucillejackson@kineticut.com",
"phone": "+1 (845) 443-3594"
},
{
"_id": "591ad7e2768b0cf4837ecf35",
"age": 40,
"name": "Cooper Leonard",
"gender": "male",
"company": "CHORIZON",
"email": "cooperleonard@chorizon.com",
"phone": "+1 (817) 418-2290"
},
{
"_id": "591ad7e270a640cf8f8a3e44",
"age": 27,
"name": "Iris Shepherd",
"gender": "female",
"company": "IDEALIS",
"email": "irisshepherd@idealis.com",
"phone": "+1 (858) 599-3628"
},
{
"_id": "591ad7e222ddd244aaba0315",
"age": 31,
"name": "Oneil Head",
"gender": "male",
"company": "LIMOZEN",
"email": "oneilhead@limozen.com",
"phone": "+1 (894) 440-3396"
},
{
"_id": "591ad7e2d36847b5e648587b",
"age": 28,
"name": "Swanson Singleton",
"gender": "male",
"company": "NSPIRE",
"email": "swansonsingleton@nspire.com",
"phone": "+1 (817) 510-2480"
},
{
"_id": "591ad7e2b78092f9fb866779",
"age": 37,
"name": "Sutton Odom",
"gender": "male",
"company": "GLUKGLUK",
"email": "suttonodom@glukgluk.com",
"phone": "+1 (961) 424-2812"
},
{
"_id": "591ad7e222736aa66a9b35cc",
"age": 31,
"name": "Chandler Kirk",
"gender": "male",
"company": "EWAVES",
"email": "chandlerkirk@ewaves.com",
"phone": "+1 (902) 524-3712"
},
{
"_id": "591ad7e2ac3f9abecd5175a7",
"age": 28,
"name": "Gentry Stanley",
"gender": "male",
"company": "GINK",
"email": "gentrystanley@gink.com",
"phone": "+1 (878) 592-2331"
},
{
"_id": "591ad7e2f79787dc605d61fb",
"age": 28,
"name": "Paulette Guthrie",
"gender": "female",
"company": "SEQUITUR",
"email": "pauletteguthrie@sequitur.com",
"phone": "+1 (820) 473-2926"
},
{
"_id": "591ad7e2ff61553783737bc0",
"age": 37,
"name": "Lesley Nash",
"gender": "female",
"company": "MELBACOR",
"email": "lesleynash@melbacor.com",
"phone": "+1 (849) 414-2115"
},
{
"_id": "591ad7e26f9c3fc95fa8e6f1",
"age": 22,
"name": "Frye Oneil",
"gender": "male",
"company": "OVERPLEX",
"email": "fryeoneil@overplex.com",
"phone": "+1 (999) 412-2403"
},
{
"_id": "591ad7e2eee8b7cd857a29d0",
"age": 40,
"name": "Christina Zamora",
"gender": "female",
"company": "REPETWIRE",
"email": "christinazamora@repetwire.com",
"phone": "+1 (860) 504-3885"
},
{
"_id": "591ad7e2be93544c81761053",
"age": 25,
"name": "Earlene Dunn",
"gender": "female",
"company": "PHARMEX",
"email": "earlenedunn@pharmex.com",
"phone": "+1 (831) 582-2974"
},
{
"_id": "591ad7e2cb672de927940ef4",
"age": 25,
"name": "Hoffman Pittman",
"gender": "male",
"company": "ENOMEN",
"email": "hoffmanpittman@enomen.com",
"phone": "+1 (882) 420-3186"
},
{
"_id": "591ad7e2aabaae50d75c21a3",
"age": 35,
"name": "Wilson Tran",
"gender": "male",
"company": "CAXT",
"email": "wilsontran@caxt.com",
"phone": "+1 (842) 529-3085"
},
{
"_id": "591ad7e2bdd6104be85bdd87",
"age": 22,
"name": "Hensley Hawkins",
"gender": "male",
"company": "LUDAK",
"email": "hensleyhawkins@ludak.com",
"phone": "+1 (902) 566-3308"
},
{
"_id": "591ad7e24796cbdc0eacb6c0",
"age": 36,
"name": "Gray Schultz",
"gender": "male",
"company": "EBIDCO",
"email": "grayschultz@ebidco.com",
"phone": "+1 (910) 532-2845"
},
{
"_id": "591ad7e204425cc3b121279a",
"age": 25,
"name": "Odonnell Livingston",
"gender": "male",
"company": "ANOCHA",
"email": "odonnelllivingston@anocha.com",
"phone": "+1 (991) 422-2754"
},
{
"_id": "591ad7e2997ac59b446d3a30",
"age": 36,
"name": "Shelton Lindsay",
"gender": "male",
"company": "DENTREX",
"email": "sheltonlindsay@dentrex.com",
"phone": "+1 (899) 567-2928"
},
{
"_id": "591ad7e2646dad2ff456b035",
"age": 40,
"name": "Mcclain Larson",
"gender": "male",
"company": "GENMOM",
"email": "mcclainlarson@genmom.com",
"phone": "+1 (934) 508-3477"
},
{
"_id": "591ad7e21e908e847ee6d3ea",
"age": 27,
"name": "Miranda Branch",
"gender": "female",
"company": "ROCKYARD",
"email": "mirandabranch@rockyard.com",
"phone": "+1 (989) 446-2387"
},
{
"_id": "591ad7e2d7905d2fcaa1ce18",
"age": 36,
"name": "Valencia Moreno",
"gender": "male",
"company": "KIOSK",
"email": "valenciamoreno@kiosk.com",
"phone": "+1 (807) 449-2626"
},
{
"_id": "591ad7e245e3c4ce18a92054",
"age": 40,
"name": "Witt Leblanc",
"gender": "male",
"company": "ENERSAVE",
"email": "wittleblanc@enersave.com",
"phone": "+1 (809) 491-3886"
},
{
"_id": "591ad7e27f2c70583d1cf34f",
"age": 32,
"name": "Glover Mccoy",
"gender": "male",
"company": "BYTREX",
"email": "glovermccoy@bytrex.com",
"phone": "+1 (928) 470-2426"
},
{
"_id": "591ad7e22768012595c933c9",
"age": 25,
"name": "Billie Dunlap",
"gender": "female",
"company": "INQUALA",
"email": "billiedunlap@inquala.com",
"phone": "+1 (903) 415-2136"
},
{
"_id": "591ad7e22df8d07559a29904",
"age": 21,
"name": "Bridget Hill",
"gender": "female",
"company": "TRIBALOG",
"email": "bridgethill@tribalog.com",
"phone": "+1 (954) 461-3390"
},
{
"_id": "591ad7e201b2bddbc81ae05e",
"age": 30,
"name": "Dawson Clements",
"gender": "male",
"company": "EURON",
"email": "dawsonclements@euron.com",
"phone": "+1 (926) 570-3444"
},
{
"_id": "591ad7e2c2aeb820e77c1d9a",
"age": 31,
"name": "Valdez Johnson",
"gender": "male",
"company": "RETRACK",
"email": "valdezjohnson@retrack.com",
"phone": "+1 (841) 506-2428"
},
{
"_id": "591ad7e2370ff047048bd688",
"age": 31,
"name": "Nichole Dickerson",
"gender": "female",
"company": "NEOCENT",
"email": "nicholedickerson@neocent.com",
"phone": "+1 (945) 437-3591"
},
{
"_id": "591ad7e2b77affba5a8f8603",
"age": 34,
"name": "Madden Weaver",
"gender": "male",
"company": "VERBUS",
"email": "maddenweaver@verbus.com",
"phone": "+1 (810) 598-3145"
},
{
"_id": "591ad7e21e692cc8247b1d83",
"age": 26,
"name": "Larsen Benson",
"gender": "male",
"company": "ISOSURE",
"email": "larsenbenson@isosure.com",
"phone": "+1 (890) 435-2219"
},
{
"_id": "591ad7e2de06284f2d2a8d19",
"age": 37,
"name": "Barnes Gaines",
"gender": "male",
"company": "RODEOCEAN",
"email": "barnesgaines@rodeocean.com",
"phone": "+1 (824) 484-2693"
},
{
"_id": "591ad7e20d9d8224dfbd292f",
"age": 28,
"name": "Pacheco Landry",
"gender": "male",
"company": "METROZ",
"email": "pachecolandry@metroz.com",
"phone": "+1 (813) 525-3821"
},
{
"_id": "591ad7e2c5348da6a981a148",
"age": 21,
"name": "Richard Alexander",
"gender": "male",
"company": "GADTRON",
"email": "richardalexander@gadtron.com",
"phone": "+1 (911) 513-3007"
},
{
"_id": "591ad7e2ca9ef31ba3b67d4f",
"age": 22,
"name": "Stone Campos",
"gender": "male",
"company": "TETAK",
"email": "stonecampos@tetak.com",
"phone": "+1 (879) 440-3672"
},
{
"_id": "591ad7e2ff5a9796c8470151",
"age": 31,
"name": "Walter Randall",
"gender": "male",
"company": "EQUITAX",
"email": "walterrandall@equitax.com",
"phone": "+1 (927) 563-3777"
},
{
"_id": "591ad7e272c6b6c1241b3a4a",
"age": 27,
"name": "Copeland Compton",
"gender": "male",
"company": "MAXIMIND",
"email": "copelandcompton@maximind.com",
"phone": "+1 (829) 599-2574"
},
{
"_id": "591ad7e2220c6caa9e2c444d",
"age": 30,
"name": "Lorie Irwin",
"gender": "female",
"company": "ELPRO",
"email": "lorieirwin@elpro.com",
"phone": "+1 (938) 584-2368"
},
{
"_id": "591ad7e2942d603032f1ebda",
"age": 35,
"name": "Buck Roth",
"gender": "male",
"company": "COMTRACT",
"email": "buckroth@comtract.com",
"phone": "+1 (830) 585-3168"
},
{
"_id": "591ad7e2c23efccd2fcd5b2e",
"age": 31,
"name": "Mamie Robinson",
"gender": "female",
"company": "ZOMBOID",
"email": "mamierobinson@zomboid.com",
"phone": "+1 (845) 481-3222"
},
{
"_id": "591ad7e285109fadbf0f9c01",
"age": 33,
"name": "Bryan Stone",
"gender": "male",
"company": "PODUNK",
"email": "bryanstone@podunk.com",
"phone": "+1 (971) 469-3832"
},
{
"_id": "591ad7e25abc328c2cc42283",
"age": 37,
"name": "Benson Lyons",
"gender": "male",
"company": "FUTURITY",
"email": "bensonlyons@futurity.com",
"phone": "+1 (964) 536-3522"
},
{
"_id": "591ad7e28fa81611cdf5066a",
"age": 37,
"name": "Sally Finley",
"gender": "female",
"company": "ZYTRAC",
"email": "sallyfinley@zytrac.com",
"phone": "+1 (953) 506-2428"
},
{
"_id": "591ad7e271c5882aa5452601",
"age": 26,
"name": "Callahan Mckee",
"gender": "male",
"company": "ACCUPRINT",
"email": "callahanmckee@accuprint.com",
"phone": "+1 (842) 400-3570"
},
{
"_id": "591ad7e2275e787c5d9eea26",
"age": 25,
"name": "Ines Hamilton",
"gender": "female",
"company": "PROSURE",
"email": "ineshamilton@prosure.com",
"phone": "+1 (976) 485-2663"
},
{
"_id": "591ad7e2d8790e386b671ec6",
"age": 38,
"name": "Mcknight Abbott",
"gender": "male",
"company": "MAXEMIA",
"email": "mcknightabbott@maxemia.com",
"phone": "+1 (960) 600-3920"
},
{
"_id": "591ad7e2a1cae1e4f17d8db2",
"age": 28,
"name": "Tanisha Ross",
"gender": "female",
"company": "SYNKGEN",
"email": "tanishaross@synkgen.com",
"phone": "+1 (920) 502-3204"
},
{
"_id": "591ad7e2f816fd6848bc03a3",
"age": 22,
"name": "Pam Webb",
"gender": "female",
"company": "VALREDA",
"email": "pamwebb@valreda.com",
"phone": "+1 (926) 442-2683"
},
{
"_id": "591ad7e208585adb332ac653",
"age": 29,
"name": "Bobbi Mays",
"gender": "female",
"company": "PARLEYNET",
"email": "bobbimays@parleynet.com",
"phone": "+1 (940) 577-3487"
},
{
"_id": "591ad7e2f4b29cbd1744b14e",
"age": 27,
"name": "Snider Sandoval",
"gender": "male",
"company": "GRONK",
"email": "snidersandoval@gronk.com",
"phone": "+1 (903) 526-2655"
},
{
"_id": "591ad7e23a3f54209ec9deba",
"age": 33,
"name": "Fitzpatrick Weiss",
"gender": "male",
"company": "MICROLUXE",
"email": "fitzpatrickweiss@microluxe.com",
"phone": "+1 (894) 494-2135"
},
{
"_id": "591ad7e229d21e1b7723c044",
"age": 27,
"name": "Kimberly Brown",
"gender": "female",
"company": "SPACEWAX",
"email": "kimberlybrown@spacewax.com",
"phone": "+1 (832) 481-2926"
},
{
"_id": "591ad7e247e285f0c563ac94",
"age": 20,
"name": "Fisher Kent",
"gender": "male",
"company": "ORBIN",
"email": "fisherkent@orbin.com",
"phone": "+1 (962) 523-3956"
},
{
"_id": "591ad7e274439c6eb52e99df",
"age": 26,
"name": "Arlene Carroll",
"gender": "female",
"company": "ELITA",
"email": "arlenecarroll@elita.com",
"phone": "+1 (998) 497-3751"
},
{
"_id": "591ad7e271852e5766fad809",
"age": 22,
"name": "Tamra Spence",
"gender": "female",
"company": "ARTWORLDS",
"email": "tamraspence@artworlds.com",
"phone": "+1 (963) 516-2492"
},
{
"_id": "591ad7e28c9165c4372744f7",
"age": 29,
"name": "Alice Goodman",
"gender": "female",
"company": "ZILLIDIUM",
"email": "alicegoodman@zillidium.com",
"phone": "+1 (829) 577-2972"
},
{
"_id": "591ad7e2c2a9121f713f15c3",
"age": 27,
"name": "Clay Washington",
"gender": "male",
"company": "EXOSTREAM",
"email": "claywashington@exostream.com",
"phone": "+1 (926) 460-3699"
},
{
"_id": "591ad7e2c99d41ce0347ac8f",
"age": 38,
"name": "Navarro Walsh",
"gender": "male",
"company": "ILLUMITY",
"email": "navarrowalsh@illumity.com",
"phone": "+1 (896) 564-3270"
},
{
"_id": "591ad7e25e7c6ff0785d1337",
"age": 22,
"name": "Alexandra Hughes",
"gender": "female",
"company": "TASMANIA",
"email": "alexandrahughes@tasmania.com",
"phone": "+1 (977) 509-3599"
},
{
"_id": "591ad7e2acb8a5785b6fd512",
"age": 40,
"name": "Vonda Oliver",
"gender": "female",
"company": "NEPTIDE",
"email": "vondaoliver@neptide.com",
"phone": "+1 (836) 595-2260"
},
{
"_id": "591ad7e20543a9fc7bc33515",
"age": 36,
"name": "Christa Nixon",
"gender": "female",
"company": "GLASSTEP",
"email": "christanixon@glasstep.com",
"phone": "+1 (966) 551-2149"
},
{
"_id": "591ad7e21b8e73ba4dbf7c9a",
"age": 34,
"name": "Weber Hurst",
"gender": "male",
"company": "SLUMBERIA",
"email": "weberhurst@slumberia.com",
"phone": "+1 (943) 516-3449"
},
{
"_id": "591ad7e282b9cb56357b42db",
"age": 27,
"name": "Rasmussen Kramer",
"gender": "male",
"company": "MUSIX",
"email": "rasmussenkramer@musix.com",
"phone": "+1 (891) 553-3264"
},
{
"_id": "591ad7e2287c587e3a04e80c",
"age": 30,
"name": "Mayra Beasley",
"gender": "female",
"company": "BRAINCLIP",
"email": "mayrabeasley@brainclip.com",
"phone": "+1 (958) 576-2642"
},
{
"_id": "591ad7e26245135989367fa8",
"age": 20,
"name": "Nunez Ortiz",
"gender": "male",
"company": "KYAGURU",
"email": "nunezortiz@kyaguru.com",
"phone": "+1 (906) 591-3563"
},
{
"_id": "591ad7e23248cee3b84bde40",
"age": 39,
"name": "Michael Foreman",
"gender": "male",
"company": "DANCITY",
"email": "michaelforeman@dancity.com",
"phone": "+1 (848) 400-3539"
},
{
"_id": "591ad7e23ae236a1d59d07ee",
"age": 36,
"name": "Virgie Wheeler",
"gender": "female",
"company": "XIIX",
"email": "virgiewheeler@xiix.com",
"phone": "+1 (831) 400-2814"
},
{
"_id": "591ad7e2d8b9e3c0c31d770d",
"age": 31,
"name": "Levy Hoover",
"gender": "male",
"company": "CANDECOR",
"email": "levyhoover@candecor.com",
"phone": "+1 (843) 428-2780"
},
{
"_id": "591ad7e2badb7200ca06a483",
"age": 21,
"name": "Battle Stanton",
"gender": "male",
"company": "CALCULA",
"email": "battlestanton@calcula.com",
"phone": "+1 (861) 522-3951"
},
{
"_id": "591ad7e27fd1a4455ccf406c",
"age": 24,
"name": "Church Lynn",
"gender": "male",
"company": "DAISU",
"email": "churchlynn@daisu.com",
"phone": "+1 (896) 425-3854"
}
]

View File

@ -0,0 +1,144 @@
<!doctype html>
<head>
<style>
body {
font-family: 'Helvetica', sans-serif;
}
</style>
</head>
<body>
<h1>People</h1>
<table class="js-table">
<tr class="js-table-header">
<th data-field="name">Name</th>
<th data-field="age">Age</th>
<th data-field="email">Email</th>
</tr>
<tbody class="js-table-body">
</tbody>
</table>
<button class="js-page-button button-prev" data-type="prev">Prev</button>
Page <span class="js-current-page"></span> / <span class="js-total-page"></span>
<button class="js-page-button button-next" data-type="next">Next</button>
<script type="text/javascript" src="data.js"></script>
<script>
(() => {
function init() {
const DOM = {
$pageButtons: document.querySelectorAll('.js-page-button'),
$prevButton: document.querySelector('.js-page-button.button-prev'),
$nextButton: document.querySelector('.js-page-button.button-next'),
$tableHeader: document.querySelector('.js-table-header'),
$tableBody: document.querySelector('.js-table-body'),
$currentPage: document.querySelector('.js-current-page'),
$totalPages: document.querySelector('.js-total-page'),
};
const PAGE_SIZE = 10;
function initialState() {
return {
currentPage: 0,
totalPages: 0,
sortField: 'name',
sortOrder: 'asc',
data,
};
}
let state = initialState();
state.totalPages = Math.ceil(state.data.length / PAGE_SIZE);
function navigatePages(type) {
let newCurrentPage = state.currentPage + (type === 'prev' ? -1 : 1);
newCurrentPage = Math.max(0, newCurrentPage);
newCurrentPage = Math.min(newCurrentPage, state.totalPages - 1);
state.currentPage = newCurrentPage;
}
function setSortField(field) {
state.currentPage = 0;
if (state.sortField !== field) {
state.sortField = field;
state.sortOrder = 'asc';
} else {
state.sortOrder = state.sortOrder === 'asc' ? 'desc' : 'asc';
}
}
function attachEventListeners() {
// Pagination.
DOM.$pageButtons.forEach(el => {
el.addEventListener('click', function () {
navigatePages(this.getAttribute('data-type'));
render();
});
});
// Sorting.
DOM.$tableHeader.addEventListener('click', function (event) {
const el = event.target;
const field = el.getAttribute('data-field');
if (el.tagName !== 'TH' || !field) {
return;
}
setSortField(field);
render();
});
}
function render() {
DOM.$tableBody.innerHTML = '';
// Sort data.
const sortField = state.sortField;
state.data.sort((a, b) => {
switch (sortField) {
case 'name':
case 'email':
return a[sortField] > b[sortField] ? 1 : -1;
case 'age':
return a[sortField] - b[sortField];
}
});
if (state.sortOrder === 'desc') {
state.data.reverse();
}
// Create table rows.
const pageData = state.data.slice(state.currentPage * PAGE_SIZE, state.currentPage * PAGE_SIZE + PAGE_SIZE);
const $tableRowsFragment = document.createDocumentFragment();
pageData.forEach(person => {
const $tableRow = document.createElement('tr');
['name', 'age', 'email'].forEach(field => {
const $tableCell = document.createElement('td');
$tableCell.textContent = person[field];
$tableRow.appendChild($tableCell);
});
$tableRowsFragment.appendChild($tableRow);
});
DOM.$tableBody.appendChild($tableRowsFragment);
// Pagination buttons disabled states.
DOM.$currentPage.textContent = state.currentPage + 1;
DOM.$totalPages.textContent = state.totalPages;
if (state.currentPage === 0) {
DOM.$prevButton.setAttribute('disabled', true);
} else {
DOM.$prevButton.removeAttribute('disabled');
}
if (state.currentPage === state.totalPages - 1) {
DOM.$nextButton.setAttribute('disabled', true);
} else {
DOM.$nextButton.removeAttribute('disabled');
}
}
render();
attachEventListeners();
}
document.addEventListener('DOMContentLoaded', init);
})();
</script>
</body>
</html>

View File

@ -0,0 +1,66 @@
# Security
## Encryption
#### Symmetrical Encryption
- Symmetrical encryption is a type of encryption where the same key is used to encrypt plaintext messages and to decrypt ciphertext.
- Symmetrical encryption is usually much less computationally expensive as compared to asymmetric encryption.
- Often called "shared secret" encryption, or "secret key" encryption.
- To use a symmetric encryption scheme, the sender and receiver must securely share a key in advance. This sharing can be done via asymmetric encryption.
#### Asymmetric Encryption
- A pair of keys are required: a **private key** and a **public key**. Public keys can be shared with anyone while private keys should be kept secret and known only to the owner.
- A private key can be used to decrypt a message encrypted by the corresponding public key. A successful decryption verifies that the holder possesses the private key.
- Also known as public-key cryptography.
## Public Key Infrastructure
A public key infrastructure (PKI) is a system for the creation, storage, and distribution of digital certificates which are used to verify that a particular public key belongs to a certain entity. The PKI creates digital certificates which map public keys to entities, securely stores these certificates in a central repository and revokes them if needed.
###### References
- https://www.wikiwand.com/en/Public_key_infrastructure
## SSH
An SSH session consists of two stages, **Negotiating Encryption** and **User Authentication**.
#### Negotiating Encryption
The goal of this stage is for the client and server to agree upon and establish encryption to protect future communication, by generating an identical session key. One possible algorithm to generate the session key is the DiffieHellman key exchange scheme. Each party generates a public/private key pair and exchanges the public key. After obtaining an authentic copy of each other's public keys, each party can compute a shared secret offline.
The basis of this procedure for classic Diffie-Hellman is:
1. Both parties agree on a large prime number, which will serve as a seed value.
1. Both parties agree on an encryption generator (typically AES), which will be used to manipulate the values in a predefined way.
1. Independently, each party comes up with another prime number which is kept secret from the other party. This number is used as the private key for this interaction (different than the private SSH key used for authentication).
1. The generated private key, the encryption generator, and the shared prime number are used to generate a public key that is derived from the private key, but which can be shared with the other party.
1. Both participants then exchange their generated public keys.
1. The receiving entity uses their own private key, the other party's public key, and the original shared prime number to compute a shared secret key.
1. Although this is independently computed by each party, using opposite private and public keys, it will result in the same shared secret key.
1. The shared secret is then used to encrypt all communication that follows.
The purpose of the shared secret key is to wrap all further communication in an encrypted tunnel that cannot be deciphered by outsiders.
#### User Authentication
The goal of this stage is to authenticate the user and discover whether access to the server should be granted. There are two approaches for authenticating, either by using passwords, or SSH key pairs.
For password authentication, the server simply prompts the client for the password of the account they are attempting to login with. The password is sent through the negotiated encryption, so it is secure from outside parties.
Authentication using SSH key pairs begins after the symmetric encryption has been established as described in the last section. The procedure happens like this:
1. The client begins by sending an ID for the key pair it would like to authenticate with to the server.
1. The server check's the `authorized_keys` file of the account that the client is attempting to log into for the key ID.
1. If a public key with matching ID is found in the file, the server generates a random number and uses the public key to encrypt the number.
1. The server sends the client this encrypted message.
1. If the client actually has the associated private key, it will be able to decrypt the message using that key, revealing the original number.
1. The client combines the decrypted number with the shared session key that is being used to encrypt the communication, and calculates the MD5 hash of this value.
1. The client then sends this MD5 hash back to the server as an answer to the encrypted number message.
1. The server uses the same shared session key and the original number that it sent to the client to calculate the MD5 value on its own. It compares its own calculation to the one that the client sent back. If these two values match, it proves that the client was in possession of the private key and the client is authenticated.
###### References
- https://www.digitalocean.com/community/tutorials/understanding-the-ssh-encryption-and-connection-process

View File

@ -0,0 +1,83 @@
# Snake Game
Design a snake game that is to be played in web browser.
Client: React + Redux
Rendering:
Pixel-based graphics. Depending on the intended resolution, can divide the screen into N \* M pixels. Can dynamically calculate the size of each pixel.
Fruit: One pixel.
Snake body: One pixel width made up of connected pixels.
Model:
```js
{
fruit: {
x, y
},
snake: {
points: [(x, y), ...] # head is at index 0
direction: north/south/east/west
}
speed: 500,
points: 0
}
```
```js
function update() {
next_loc = points[0] + (x, y) # Depends on the direction
if (snake.points.find(next_loc) > 0) {
// die
}
let pts = snake.points;
if (!isEqual(next_loc, fruit)) {
pts = points.removeLast();
} else {
generateFruit();
points++;
}
snake.points = [next_loc, ...pts];
// Boundary checking -> die
}
```
```js
function generateFruit() {
// Cannot generate on my own body.
// First approach: while on body, generate
let next_fruit_location = random_location();
while (snake.points.find(next_fruit_location) > 0) {
next_fruit_location = random_location();
}
fruit = next_fruit_location;
// Second approach: brute force
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
let point = { x: i, y: j };
if (snake.points.find(next_fruit_location) === -1) {
fruit = point;
}
}
}
// Third approach: brute force with random
const available_points = [];
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
let point = { x: i, y: j };
if (snake.points.find(next_fruit_location) === -1) {
available_points.push(point);
}
}
}
fruit = _.sample(available_points);
}
setInterval(update, speed);
```

View File

@ -0,0 +1,21 @@
# Software Engineering
## What is the difference between an interface and abstract class?
**Abstract Class**
- For an abstract class, a method must be declared as abstract. An abstract method doesn't have an implementation.
- The Abstract methods can be declared with Access modifiers like public, internal, protected, etc. When implementing these methods in a subclass, you must define them with the same (or a less restricted) visibility.
- Abstract classes can contain variables and concrete methods.
- A class can Inherit only one Abstract class. Hence multiple inheritance is not possible for an Abstract class.
- Abstract is object-oriented. It offers the basic data an 'object' should have and/or functions it should be able to do. It is concerned with the object's basic characteristics: what it has and what it can do. Hence objects which inherit from the same abstract class share the basic characteristics (generalization).
- Abstract class establishes "is a" relation with concrete classes.
**Interface**
- For an interface, all the methods are abstract by default. So one cannot declare variables or concrete methods in interfaces.
- All methods declared in an interface must be public.
- Interfaces cannot contain variables and concrete methods except constants.
- A class can implement many interfaces. Hence multiple interface inheritance is possible.
- Interface is functionality-oriented. It defines functionalities an object should have. Regardless what object it is, as long as it can do these functionalities, which are defined in the interface, it's fine. It ignores everything else. An object/class can contain several (groups of) functionalities; hence it is possible for a class to implement multiple interfaces.
- Interface provides "has a" capability for classes.

View File

@ -0,0 +1,178 @@
<!doctype html>
<head>
<style>
body {
font-family: 'Helvetica', sans-serif;
}
.board-cell {
border: 1px solid #666;
box-sizing: border-box;
display: inline-block;
font-size: 32px;
height: 100px;
line-height: 100px;
text-align: center;
width: 100px;
}
.board-cell .content {
display: inline-block;
vertical-align: middle;
}
</style>
</head>
<body>
<h1>Tic Tac Toe</h1>
<p>Current player turn: <span class="js-current-player"></span></p>
<div class="js-board"></div>
<button class="js-reset">Reset</button>
<script>
// We will spend the next 45 minutes building a single-page web app that implements a Tic-Tac-Toe game. jQuery has been included for you. We'll implement the following features in order:
// 1. Render a 3x3 board. You can hardcode some X and O values within the cell for starters.
// 2. Implement the add symbol functionality that adds a X or O into a cell whenever the player clicks on it.
// 3. Rotate between the players whenever a move is made. Update the current player display.
// 4. Check for end game conditions after each move and display the winner if any.
// horizontally, vertically, diagonally
// 5. After a winner has been determined, disable further moves on the board.
// 6. Add a button to reset the game state.
(() => {
function init() {
const DOM = {
$currentPlayer: document.querySelector('.js-current-player'),
$board: document.querySelector('.js-board'),
$resetButton: document.querySelector('.js-reset'),
};
const SIZE = 3;
function initialState() {
return {
boardModel: Array(SIZE).fill(null).map(_ => Array(SIZE).fill(null)),
players: ['X', 'O'],
currentPlayer: 0,
gameEnded: false,
turn: 0,
};
}
let state = initialState();
function renderBoard() {
DOM.$currentPlayer.textContent = state.players[state.currentPlayer];
// Assuming SIZE > 0.
DOM.$board.innerHTML = '';
for (let i = 0; i < SIZE; i++) {
const $row = document.createElement('div');
$row.classList.add('board-row');
for (let j = 0; j < SIZE; j++) {
const $cell = document.createElement('div');
$cell.classList.add('board-cell');
$cell.setAttribute('data-i', i);
$cell.setAttribute('data-j', j);
const $content = document.createElement('span');
$content.classList.add('content');
$content.textContent = state.boardModel[i][j];
$cell.appendChild($content);
$row.appendChild($cell);
}
DOM.$board.appendChild($row);
}
}
function checkWinning(board, player) {
// Check horizontal.
for (let i = 0; i < SIZE; i++) {
if (board[i].every(cell => cell === player)) {
return true;
}
}
// Check vertical.
for (let j = 0; j < SIZE; j++) {
let verticalAllPlayer = true;
for (let i = 0; i < SIZE; i++) {
if (board[i][j] !== player) {
verticalAllPlayer = false;
break;
}
}
if (verticalAllPlayer) {
return verticalAllPlayer;
}
}
// Check diagonal South-East.
let diagonalAllPlayer = true;
for (let i = 0; i < SIZE; i++) {
if (board[i][i] !== player) {
diagonalAllPlayer = false;
break;
}
}
if (diagonalAllPlayer) {
return diagonalAllPlayer;
}
// Check diagonal North-East.
diagonalAllPlayer = true;
for (let i = SIZE - 1, j = 0; i >= 0; i--, j++) {
if (board[i][j] !== player) {
diagonalAllPlayer = false;
break;
}
}
if (diagonalAllPlayer) {
return diagonalAllPlayer;
}
return false;
}
function attachEventListeners() {
DOM.$board.addEventListener('click', (event) => {
if (state.gameEnded) {
return;
}
if (!event.target.classList.contains('board-cell')) {
return;
}
const $cell = event.target;
const i = parseInt($cell.getAttribute('data-i'), 10);
const j = parseInt($cell.getAttribute('data-j'), 10);
if (state.boardModel[i][j] !== null) {
alert('Cell has already been taken!');
return;
}
const player = state.players[state.currentPlayer];
state.boardModel[i][j] = player;
const winningMove = checkWinning(state.boardModel, player);
state.turn++;
if (!winningMove) {
state.currentPlayer = (state.currentPlayer + 1) % 2;
renderBoard();
if (state.turn === SIZE * SIZE) {
alert('It\'s a draw!');
}
} else {
renderBoard();
state.gameEnded = true;
alert(`Player ${player} wins!`);
}
});
DOM.$resetButton.addEventListener('click', () => {
if (confirm('Start a new game?')) {
state = initialState();
renderBoard();
}
});
}
renderBoard();
attachEventListeners();
}
document.addEventListener('DOMContentLoaded', init);
})();
</script>
</body>
</html>