Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fbc8d69

Browse files
committed
js3 w3 part1 Enwer
1 parent 8f35677 commit fbc8d69

File tree

14 files changed

+563
-0
lines changed

14 files changed

+563
-0
lines changed
6 KB
Binary file not shown.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
'use strict';
2+
3+
{
4+
const accounts = {
5+
hyf: {
6+
name: 'HackYourFuture',
7+
type: 'org',
8+
},
9+
microsoft: {
10+
name: 'Microsoft',
11+
type: 'org',
12+
},
13+
jim: {
14+
name: 'remarcmij',
15+
type: 'user',
16+
},
17+
};
18+
19+
const { Model, HeaderView, RepoView, ContributorsView, ErrorView } = window;
20+
const { createAndAppend } = window.Util;
21+
22+
class App {
23+
constructor(account) {
24+
const containers = App.renderContainers();
25+
26+
const model = new Model(account);
27+
const fetchData = model.fetchData.bind(model);
28+
29+
model.subscribe(new HeaderView(account, containers.header, fetchData));
30+
model.subscribe(new RepoView(containers.repo));
31+
model.subscribe(new ContributorsView(containers.contributors));
32+
model.subscribe(new ErrorView(containers.error));
33+
34+
fetchData();
35+
}
36+
37+
static renderContainers() {
38+
const root = document.getElementById('root');
39+
const header = createAndAppend('header', root, { class: 'header' });
40+
const error = createAndAppend('div', root);
41+
const main = createAndAppend('main', root, {
42+
class: 'main-container',
43+
});
44+
const repo = createAndAppend('section', main, {
45+
class: 'repo-container whiteframe',
46+
});
47+
const contributors = createAndAppend('section', main, {
48+
class: 'contributors-container whiteframe',
49+
});
50+
return { header, error, main, repo, contributors };
51+
}
52+
}
53+
54+
const ACCOUNT_KEY = 'hyf';
55+
window.onload = () => new App(accounts[ACCOUNT_KEY]);
56+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use strict';
2+
3+
{
4+
const { createAndAppend } = window.Util;
5+
6+
class ContributorsView {
7+
constructor(container) {
8+
this.container = container;
9+
}
10+
11+
update(state) {
12+
if (!state.error) {
13+
this.render(state.contributors);
14+
}
15+
}
16+
17+
/**
18+
* Renders the list of contributors
19+
* @param {Object[]} contributors An array of contributor objects
20+
*/
21+
render(contributors) {
22+
// TODO: replace this comment and the console.log with your own code
23+
console.log('ContributorsView', contributors);
24+
}
25+
}
26+
27+
window.ContributorsView = ContributorsView;
28+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
3+
{
4+
const { createAndAppend } = window.Util;
5+
6+
class ErrorView {
7+
constructor(container) {
8+
this.container = container;
9+
}
10+
11+
update(state) {
12+
this.render(state.error);
13+
}
14+
15+
/**
16+
* Renders an error for the 'error' message type.
17+
* @param {Error} error An Error object
18+
*/
19+
render(error) {
20+
this.container.innerHTML = '';
21+
if (error) {
22+
createAndAppend('div', this.container, {
23+
text: error.message,
24+
class: 'alert alert-error',
25+
});
26+
}
27+
}
28+
}
29+
30+
window.ErrorView = ErrorView;
31+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
'use strict';
2+
3+
{
4+
const { createAndAppend } = window.Util;
5+
6+
class HeaderView {
7+
constructor(account, header, fetchData) {
8+
this.account = account;
9+
this.header = header;
10+
this.fetchData = fetchData;
11+
this.select = null;
12+
}
13+
14+
update(state) {
15+
if (!this.select && !state.error) {
16+
this.render(state.repos);
17+
}
18+
}
19+
20+
/**
21+
* Renders the data for the 'select' message type. Create a <select> element
22+
* and its <option> children.
23+
* @param {Object[]} repos An array of repository objects.
24+
*/
25+
render(repos) {
26+
createAndAppend('div', this.header, { text: this.account.name });
27+
this.select = createAndAppend('select', this.header, {
28+
class: 'repo-select',
29+
autofocus: 'autofocus',
30+
});
31+
32+
repos.forEach(repo =>
33+
createAndAppend('option', this.select, {
34+
text: repo.name,
35+
value: repo.id,
36+
}),
37+
);
38+
39+
this.select.addEventListener('change', () =>
40+
this.fetchData(this.select.value),
41+
);
42+
}
43+
}
44+
45+
window.HeaderView = HeaderView;
46+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict';
2+
3+
{
4+
const { Observable } = window;
5+
6+
const makeUrl = ({ name, type }) =>
7+
`https://api.github.com/${type}s/${name}/repos?per_page=100`;
8+
9+
class Model extends Observable {
10+
constructor(account) {
11+
super();
12+
this.account = account;
13+
this.state = {
14+
repos: [],
15+
selectedRepo: null,
16+
contributors: [],
17+
error: null,
18+
};
19+
}
20+
21+
async fetchData(id) {
22+
const repoId = parseInt(id, 10);
23+
this.state.error = null;
24+
try {
25+
if (this.state.repos.length === 0) {
26+
const repos = await Model.fetchJSON(makeUrl(this.account));
27+
this.state.repos = repos.sort((a, b) => a.name.localeCompare(b.name));
28+
}
29+
const index = id
30+
? this.state.repos.findIndex(repo => repo.id === repoId)
31+
: 0;
32+
this.state.selectedRepo = this.state.repos[index];
33+
this.state.contributors = await Model.fetchJSON(
34+
this.state.selectedRepo.contributors_url,
35+
);
36+
} catch (err) {
37+
this.state.error = err;
38+
}
39+
this.notify(this.state);
40+
}
41+
42+
static fetchJSON(url) {
43+
return fetch(url).then(res => {
44+
if (!res.ok) {
45+
return new Error(`HTTP ${res.status} - ${res.statusText}`);
46+
}
47+
return res.status === 200 ? res.json() : null;
48+
});
49+
}
50+
}
51+
52+
window.Model = Model;
53+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
'use strict';
2+
3+
{
4+
class Observable {
5+
constructor() {
6+
this.observers = new Set();
7+
}
8+
9+
subscribe(observer = {}) {
10+
this.observers.add(observer);
11+
return () => this.observers.delete(observer);
12+
}
13+
14+
notify(data) {
15+
this.observers.forEach(observer => observer.update(data));
16+
}
17+
}
18+
19+
window.Observable = Observable;
20+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use strict';
2+
3+
{
4+
const { createAndAppend } = window.Util;
5+
6+
class RepoView {
7+
constructor(container) {
8+
this.container = container;
9+
}
10+
11+
update(state) {
12+
if (!state.error) {
13+
this.render(state.selectedRepo);
14+
}
15+
}
16+
17+
/**
18+
* Renders the repository details.
19+
* @param {Object} repo A repository object.
20+
*/
21+
render(repo) {
22+
// TODO: replace this comment and the console.log with your own code
23+
console.log('RepoView', repo);
24+
}
25+
}
26+
27+
window.RepoView = RepoView;
28+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use strict';
2+
3+
{
4+
class Util {
5+
/**
6+
* Creates an element, optionally setting its attributes, and appends
7+
* the element to a parent.
8+
* @param {string} name The tag name of the element to create.
9+
* @param {HTMLElement} parent The parent element.
10+
* @param {Object} options An object with attribute names and values.
11+
*/
12+
static createAndAppend(name, parent, options = {}) {
13+
const elem = document.createElement(name);
14+
parent.appendChild(elem);
15+
Object.entries(options).forEach(([key, value]) => {
16+
if (key === 'text') {
17+
elem.textContent = value;
18+
} else {
19+
elem.setAttribute(key, value);
20+
}
21+
});
22+
return elem;
23+
}
24+
}
25+
26+
window.Util = Util;
27+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta
6+
name="viewport"
7+
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
8+
/>
9+
<meta name="theme-color" content="#000000" />
10+
<meta name="apple-mobile-web-app-capable" content="yes" />
11+
<meta name="mobile-web-app-capable" content="yes" />
12+
<meta name="format-detection" content="telephone=no" />
13+
<link rel="apple-touch-icon" href="./hyf.png" />
14+
<link rel="shortcut icon" type="image/png" href="./hyf.png" />
15+
<title>HYF-GITHUB</title>
16+
<link
17+
href="https://fonts.googleapis.com/css?family=Roboto:400,700"
18+
rel="stylesheet"
19+
/>
20+
<link rel="stylesheet" href="style.css" />
21+
</head>
22+
<body>
23+
<div id="root"></div>
24+
<script src="./Util.js"></script>
25+
<script src="./Observable.js"></script>
26+
<script src="./Model.js"></script>
27+
<script src="./HeaderView.js"></script>
28+
<script src="./RepoView.js"></script>
29+
<script src="./ContributorsView.js"></script>
30+
<script src="./ErrorView.js"></script>
31+
<script src="./App.js"></script>
32+
</body>
33+
</html>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
* {
2+
margin: 0;
3+
padding: 0;
4+
}
5+
6+
body {
7+
background-color: #eee;
8+
}
9+
main {
10+
width: 96%;
11+
margin: 1%;
12+
}
13+
header {
14+
color: #fff;
15+
background-color: #3f51b5;
16+
font-size: 25px;
17+
padding: 20px;
18+
}
19+
20+
ul {
21+
list-style: none;
22+
}
23+
select {
24+
margin-left: 50px;
25+
}
26+
27+
section {
28+
width: 100%;
29+
padding: 10px;
30+
margin: 2%;
31+
background-color: #fff;
32+
padding: 10px;
33+
}
34+
35+
.contriDiv {
36+
padding: 5px;
37+
border-bottom: 2px solid #eee;
38+
margin: 10px 0;
39+
display: flex;
40+
justify-content: space-between;
41+
}

0 commit comments

Comments
 (0)