diff --git a/Week1/index.html b/Week1/index.html new file mode 100644 index 000000000..9c8f80c1a --- /dev/null +++ b/Week1/index.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + + HYF-GITHUB + + + + + +
+ + + + \ No newline at end of file diff --git a/Week1/index.js b/Week1/index.js new file mode 100644 index 000000000..0b08cce3b --- /dev/null +++ b/Week1/index.js @@ -0,0 +1,163 @@ +'use strict'; + +{ + function fetchJSON(url, cb) { + const xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.responseType = 'json'; + xhr.onload = () => { + if (xhr.status < 400) { + cb(null, xhr.response); + } else { + cb(new Error(`Network error: ${xhr.status} - ${xhr.statusText}`)); + } + }; + xhr.onerror = () => cb(new Error('Network request failed')); + xhr.send(); + } + + function createAndAppend(name, parent, options = {}) { + const elem = document.createElement(name); + parent.appendChild(elem); + Object.keys(options).forEach(key => { + const value = options[key]; + if (key === 'text') { + elem.textContent = value; + } else { + elem.setAttribute(key, value); + } + }); + return elem; + } + function loadDetail(element) { + const leftID = document.getElementById('leftID'); + createAndAppend('ul', leftID, { id: 'leftDetailID', class: 'leftDetailClass' }); + + const leftDetailID = document.getElementById('leftDetailID'); + createAndAppend('li', leftDetailID, { + id: 'leftListID', + class: 'leftListClass', + text: 'Repository: ', + }); + + const leftListID = document.getElementById('leftListID'); + createAndAppend('a', leftListID, { + text: element.name, + href: element.html_url, + id: 'repositoryNameID', + class: 'repositoryNameClass', + }); + + createAndAppend('li', leftDetailID, { + id: 'descList', + text: `Description: ${element.description}`, + }); + createAndAppend('li', leftDetailID, { text: `Forks: ${element.forks}` }); + const updatedAt = new Date(element.updated_at).toLocaleString(); + createAndAppend('li', leftDetailID, { text: `Updated: ${updatedAt}` }); + } + function loadContributor(element) { + fetchJSON(element.contributors_url, (err, data) => { + const root = document.getElementById('root'); + if (err) { + createAndAppend('div', root, { text: err.message, class: 'alert-error' }); + } else { + const rightID = document.getElementById('rightID'); + createAndAppend('p', rightID, { id: 'contTextID', text: 'Contributors' }); + createAndAppend('ul', rightID, { id: 'contListID', class: 'contListClass' }); + + let contURL; + let contData; + let contDetail; + const contListID = document.getElementById('contListID'); + for (let i = 0; i < data.length; i++) { + contURL = createAndAppend('a', contListID, { + href: data[i].html_url, + target: '_blank', + id: 'contUrlID', + class: 'contUrlClass', + }); + contData = createAndAppend('li', contURL, { + id: 'contDataID', + class: 'contDataClass', + }); + + createAndAppend('img', contData, { + src: data[i].avatar_url, + id: 'contImgID', + class: 'contImgClass', + }); + + contDetail = createAndAppend('div', contData, { + id: 'contDetailID', + class: 'contDetailClass', + }); + createAndAppend('div', contDetail, { text: data[i].login }); + createAndAppend('div', contDetail, { + text: data[i].contributions, + id: 'contBadgeID', + class: 'contBadgeClass', + }); + } + } + }); + } + + function main(url) { + fetchJSON(url, (err, data) => { + const root = document.getElementById('root'); + if (err) { + createAndAppend('div', root, { text: err.message, class: 'alert-error' }); + } else { + createAndAppend('header', root, { id: 'topBoxID', class: 'topBoxClass' }); + const topBoxID = document.getElementById('topBoxID'); + createAndAppend('p', topBoxID, { + id: 'topTextID', + class: 'topTextClass', + text: 'HYF Repositories', + }); + + createAndAppend('div', root, { id: 'mainContainerID', class: 'mainContainerClass' }); + + const mainContainerID = document.getElementById('mainContainerID'); + createAndAppend('div', mainContainerID, { id: 'leftID', class: 'leftClass' }); + createAndAppend('div', mainContainerID, { id: 'rightID', class: 'rightClass' }); + createAndAppend('select', topBoxID, { id: 'repsSelectID', Class: 'repsSelectClass' }); + data.sort((a, b) => a.name.localeCompare(b.name)); + + const repsSelectID = document.getElementById('repsSelectID'); + for (let index = 0; index < data.length; index++) { + createAndAppend('option', repsSelectID, { + value: index, + text: data[index].name, + id: 'optionID', + class: 'optionClass', + }); + } + const selector = document.getElementById('repsSelectID'); + + loadDetail(data[0]); + loadContributor(data[0]); + + selector.onchange = () => { + const leftDetail = document.getElementById('leftID'); + leftDetail.parentNode.removeChild(leftDetail); + + const rightDetail = document.getElementById('rightID'); + rightDetail.parentNode.removeChild(rightDetail); + + const i = selector.value; + + createAndAppend('div', mainContainerID, { id: 'leftID', class: 'leftClass' }); + createAndAppend('div', mainContainerID, { id: 'rightID', class: 'rightClass' }); + loadDetail(data[i]); + loadContributor(data[i]); + }; + } + }); + } + + const HYF_REPS_URL = 'https://api.github.com/orgs/HackYourFuture/repos?per_page=100'; + + window.onload = () => main(HYF_REPS_URL); +} diff --git a/Week1/style.css b/Week1/style.css new file mode 100644 index 000000000..c9040ac64 --- /dev/null +++ b/Week1/style.css @@ -0,0 +1,119 @@ +.alert-error { + color: #721c24; + background-color: #f8d7da; +} + +body { + margin-left: auto; + margin-right: auto; + font-family: Arial, Helvetica, sans-serif; + color: black; + margin-top: 0; + background-color: #f8f8f8; + width: 900px; +} + +.mainContainerClass { + display: flex; + flex-direction: row; + align-items: flex-start; +} + +@media (max-width: 767px) { + body { + width: 100%; + } + #mainContainerClass { + margin: 0; + flex-direction: column; + align-items: stretch; + } +} + +.topBoxClass { + color: white; + background-color: #3f51b5; + padding: 8px 16px; + margin-bottom: 16px; + display: flex; + flex-direction: row; + align-items: center; +} + +.topTextClass { + margin-left: 40px; + font-size: 30px; + font-family: 'Trebuchet MS'; + width: 350px; + height: 32px; + padding: 2px; +} + +.repsSelectClass { + margin-left: 30px; + font-size: 18px; + width: 350px; + height: 32px; + padding: 2px; +} + +.leftClass, .rightClass { + background-color: white; + flex: 1; + padding: 16px; + margin-right: 16px; + margin-bottom: 8px; + border: none; + border-radius: 2px; + background-color: white; + box-shadow: 0 1px 8px 0 rgba(0, 0, 0, 0.2), 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.12) +} + +.leftDetailClass { + table-layout: fixed; + color: rgb(0, 0, 0, 81%); +} + +@media (max-width: 767px) { + .leftClass { + margin: 0; + } +} + +#contTextID { + font-size: 0.9rem; + color: rgb(0, 0, 0, 54%); + padding: 8px 16px 4px 16px; +} + +.contDataClass { + border-bottom: solid 1px rgb(0, 0, 0, 12%); + padding: 16px; + display: flex; + flex-direction: row; + align-items: center; + cursor: pointer; +} + +.contImgClass { + border-radius: 3px; + margin-right: 16px; + height: 48px; +} + +.contDetailClass { + flex: 1; + display: flex; + flex-direction: row; + justify-content: space-between; + align-content: center; +} + +.contBadgeClass { + font-size: 12px; + padding: 2px 8px; + line-height: 1rem; + background-color: gray; + color: white; + border-radius: 4px; +} \ No newline at end of file diff --git a/Week2/index.html b/Week2/index.html new file mode 100644 index 000000000..9c8f80c1a --- /dev/null +++ b/Week2/index.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + + HYF-GITHUB + + + + + +
+ + + + \ No newline at end of file diff --git a/Week2/index.js b/Week2/index.js new file mode 100644 index 000000000..aa67fb636 --- /dev/null +++ b/Week2/index.js @@ -0,0 +1,164 @@ +'use strict'; + +{ + function fetchJSON(url) { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.responseType = 'json'; + xhr.onload = () => { + if (xhr.status < 400) { + resolve(xhr.response); + } else { + reject(new Error(`Network error: ${xhr.status} - ${xhr.statusText}`)); + } + }; + xhr.send(); + }); + } + + function createAndAppend(name, parent, options = {}) { + const elem = document.createElement(name); + parent.appendChild(elem); + Object.keys(options).forEach(key => { + const value = options[key]; + if (key === 'text') { + elem.textContent = value; + } else { + elem.setAttribute(key, value); + } + }); + return elem; + } + function loadDetail(element) { + const leftID = document.getElementById('leftID'); + createAndAppend('ul', leftID, { id: 'leftDetailID', class: 'leftDetailClass' }); + + const leftDetailID = document.getElementById('leftDetailID'); + createAndAppend('li', leftDetailID, { + id: 'leftListID', + class: 'leftListClass', + text: 'Repository: ', + }); + + const leftListID = document.getElementById('leftListID'); + createAndAppend('a', leftListID, { + text: element.name, + href: element.html_url, + id: 'repositoryNameID', + class: 'repositoryNameClass', + }); + + createAndAppend('li', leftDetailID, { + id: 'descList', + text: `Description: ${element.description}`, + }); + createAndAppend('li', leftDetailID, { text: `Forks: ${element.forks}` }); + const updatedAt = new Date(element.updated_at).toLocaleString(); + createAndAppend('li', leftDetailID, { text: `Updated: ${updatedAt}` }); + } + function loadContributor(element) { + const root = document.getElementById('root'); + fetchJSON(element.contributors_url) + .catch(reject => { + createAndAppend('div', root, { text: reject.message, class: 'alert-error' }); + }) + .then(data => { + const rightID = document.getElementById('rightID'); + createAndAppend('p', rightID, { id: 'contTextID', text: 'Contributors' }); + createAndAppend('ul', rightID, { id: 'contListID', class: 'contListClass' }); + + let contURL; + let contData; + let contDetail; + const contListID = document.getElementById('contListID'); + for (let i = 0; i < data.length; i++) { + contURL = createAndAppend('a', contListID, { + href: data[i].html_url, + target: '_blank', + id: 'contUrlID', + class: 'contUrlClass', + }); + contData = createAndAppend('li', contURL, { + id: 'contDataID', + class: 'contDataClass', + }); + + createAndAppend('img', contData, { + src: data[i].avatar_url, + id: 'contImgID', + class: 'contImgClass', + }); + + contDetail = createAndAppend('div', contData, { + id: 'contDetailID', + class: 'contDetailClass', + }); + createAndAppend('div', contDetail, { text: data[i].login }); + createAndAppend('div', contDetail, { + text: data[i].contributions, + id: 'contBadgeID', + class: 'contBadgeClass', + }); + } + }); + } + + function main(url) { + const root = document.getElementById('root'); + fetchJSON(url) + .catch(reject => { + createAndAppend('div', root, { text: reject.message, class: 'alert-error' }); + }) + .then(data => { + createAndAppend('header', root, { id: 'topBoxID', class: 'topBoxClass' }); + const topBoxID = document.getElementById('topBoxID'); + createAndAppend('p', topBoxID, { + id: 'topTextID', + class: 'topTextClass', + text: 'HYF Repositories', + }); + + createAndAppend('div', root, { id: 'mainContainerID', class: 'mainContainerClass' }); + + const mainContainerID = document.getElementById('mainContainerID'); + createAndAppend('div', mainContainerID, { id: 'leftID', class: 'leftClass' }); + createAndAppend('div', mainContainerID, { id: 'rightID', class: 'rightClass' }); + createAndAppend('select', topBoxID, { id: 'repsSelectID', Class: 'repsSelectClass' }); + data.sort((a, b) => a.name.localeCompare(b.name)); + + const repsSelectID = document.getElementById('repsSelectID'); + for (let index = 0; index < data.length; index++) { + createAndAppend('option', repsSelectID, { + value: index, + text: data[index].name, + id: 'optionID', + class: 'optionClass', + }); + } + const selector = document.getElementById('repsSelectID'); + + loadDetail(data[0]); + loadContributor(data[0]); + + selector.onchange = () => { + const leftDetail = document.getElementById('leftID'); + leftDetail.parentNode.removeChild(leftDetail); + + const rightDetail = document.getElementById('rightID'); + rightDetail.parentNode.removeChild(rightDetail); + + const i = selector.value; + + createAndAppend('div', mainContainerID, { id: 'leftID', class: 'leftClass' }); + createAndAppend('div', mainContainerID, { id: 'rightID', class: 'rightClass' }); + loadDetail(data[i]); + loadContributor(data[i]); + }; + }); + } + + const HYF_REPS_URL = 'https://api.github.com/orgs/HackYourFuture/repos?per_page=100'; + + window.onload = () => main(HYF_REPS_URL); +} diff --git a/Week2/style.css b/Week2/style.css new file mode 100644 index 000000000..c9040ac64 --- /dev/null +++ b/Week2/style.css @@ -0,0 +1,119 @@ +.alert-error { + color: #721c24; + background-color: #f8d7da; +} + +body { + margin-left: auto; + margin-right: auto; + font-family: Arial, Helvetica, sans-serif; + color: black; + margin-top: 0; + background-color: #f8f8f8; + width: 900px; +} + +.mainContainerClass { + display: flex; + flex-direction: row; + align-items: flex-start; +} + +@media (max-width: 767px) { + body { + width: 100%; + } + #mainContainerClass { + margin: 0; + flex-direction: column; + align-items: stretch; + } +} + +.topBoxClass { + color: white; + background-color: #3f51b5; + padding: 8px 16px; + margin-bottom: 16px; + display: flex; + flex-direction: row; + align-items: center; +} + +.topTextClass { + margin-left: 40px; + font-size: 30px; + font-family: 'Trebuchet MS'; + width: 350px; + height: 32px; + padding: 2px; +} + +.repsSelectClass { + margin-left: 30px; + font-size: 18px; + width: 350px; + height: 32px; + padding: 2px; +} + +.leftClass, .rightClass { + background-color: white; + flex: 1; + padding: 16px; + margin-right: 16px; + margin-bottom: 8px; + border: none; + border-radius: 2px; + background-color: white; + box-shadow: 0 1px 8px 0 rgba(0, 0, 0, 0.2), 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.12) +} + +.leftDetailClass { + table-layout: fixed; + color: rgb(0, 0, 0, 81%); +} + +@media (max-width: 767px) { + .leftClass { + margin: 0; + } +} + +#contTextID { + font-size: 0.9rem; + color: rgb(0, 0, 0, 54%); + padding: 8px 16px 4px 16px; +} + +.contDataClass { + border-bottom: solid 1px rgb(0, 0, 0, 12%); + padding: 16px; + display: flex; + flex-direction: row; + align-items: center; + cursor: pointer; +} + +.contImgClass { + border-radius: 3px; + margin-right: 16px; + height: 48px; +} + +.contDetailClass { + flex: 1; + display: flex; + flex-direction: row; + justify-content: space-between; + align-content: center; +} + +.contBadgeClass { + font-size: 12px; + padding: 2px 8px; + line-height: 1rem; + background-color: gray; + color: white; + border-radius: 4px; +} \ No newline at end of file diff --git a/Week3/index.html b/Week3/index.html new file mode 100644 index 000000000..9c8f80c1a --- /dev/null +++ b/Week3/index.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + + HYF-GITHUB + + + + + +
+ + + + \ No newline at end of file diff --git a/Week3/index.js b/Week3/index.js new file mode 100644 index 000000000..5d80712f8 --- /dev/null +++ b/Week3/index.js @@ -0,0 +1,168 @@ +'use strict'; + +{ + function fetchJSON(URL) { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.open('GET', URL); + xhr.responseType = 'json'; + xhr.onload = () => { + if (xhr.status < 400) { + resolve(xhr.response); + } else { + reject(new Error(`Network error: ${xhr.status} - ${xhr.statusText}`)); + } + }; + xhr.send(); + }); + } + + function createAndAppend(name, parent, options = {}) { + const elem = document.createElement(name); + parent.appendChild(elem); + Object.keys(options).forEach(key => { + const value = options[key]; + if (key === 'text') { + elem.textContent = value; + } else { + elem.setAttribute(key, value); + } + }); + return elem; + } + function loadDetail(element) { + const leftID = document.getElementById('leftID'); + createAndAppend('ul', leftID, { id: 'leftDetailID', class: 'leftDetailClass' }); + + const leftDetailID = document.getElementById('leftDetailID'); + createAndAppend('li', leftDetailID, { + id: 'leftListID', + class: 'leftListClass', + text: 'Repository: ', + }); + + const leftListID = document.getElementById('leftListID'); + createAndAppend('a', leftListID, { + text: element.name, + href: element.html_url, + id: 'repositoryNameID', + class: 'repositoryNameClass', + }); + + createAndAppend('li', leftDetailID, { + id: 'descList', + text: `Description: ${element.description}`, + }); + createAndAppend('li', leftDetailID, { text: `Forks: ${element.forks}` }); + const updatedAt = new Date(element.updated_at).toLocaleString(); + createAndAppend('li', leftDetailID, { text: `Updated: ${updatedAt}` }); + } + async function loadContributor(element) { + const root = document.getElementById('root'); + try { + const data = await fetchJSON(element.contributors_url); + const rightID = document.getElementById('rightID'); + createAndAppend('p', rightID, { id: 'contTextID', text: 'Contributors' }); + createAndAppend('ul', rightID, { id: 'contListID', class: 'contListClass' }); + + let contURL; + let contData; + let contDetail; + const contListID = document.getElementById('contListID'); + for (let i = 0; i < data.length; i++) { + contURL = createAndAppend('a', contListID, { + href: data[i].html_url, + target: '_blank', + id: 'contUrlID', + class: 'contUrlClass', + }); + contData = createAndAppend('li', contURL, { + id: 'contDataID', + class: 'contDataClass', + }); + + createAndAppend('img', contData, { + src: data[i].avatar_url, + id: 'contImgID', + class: 'contImgClass', + }); + + contDetail = createAndAppend('div', contData, { + id: 'contDetailID', + class: 'contDetailClass', + }); + createAndAppend('div', contDetail, { text: data[i].login }); + createAndAppend('div', contDetail, { + text: data[i].contributions, + id: 'contBadgeID', + class: 'contBadgeClass', + }); + } + } catch (err) { + createAndAppend('div', root, { + id: 'err.message', + class: 'alert-error', + }); + } + } + + async function main(url) { + const root = document.getElementById('root'); + try { + const data = await fetchJSON(url); + createAndAppend('header', root, { + id: 'topBoxID', + class: 'topBoxClass', + }); + const topBoxID = document.getElementById('topBoxID'); + createAndAppend('p', topBoxID, { + id: 'topTextID', + class: 'topTextClass', + text: 'HYF Repositories', + }); + + createAndAppend('div', root, { id: 'mainContainerID', class: 'mainContainerClass' }); + + const mainContainerID = document.getElementById('mainContainerID'); + createAndAppend('div', mainContainerID, { id: 'leftID', class: 'leftClass' }); + createAndAppend('div', mainContainerID, { id: 'rightID', class: 'rightClass' }); + createAndAppend('select', topBoxID, { id: 'repsSelectID', Class: 'repsSelectClass' }); + data.sort((a, b) => a.name.localeCompare(b.name)); + + const repsSelectID = document.getElementById('repsSelectID'); + for (let index = 0; index < data.length; index++) { + createAndAppend('option', repsSelectID, { + value: index, + text: data[index].name, + id: 'optionID', + class: 'optionClass', + }); + } + const selector = document.getElementById('repsSelectID'); + + loadDetail(data[0]); + loadContributor(data[0]); + + selector.onchange = () => { + const leftID = document.getElementById('leftID'); + leftID.parentNode.removeChild(leftID); + + const rightID = document.getElementById('rightID'); + rightID.parentNode.removeChild(rightID); + + const i = selector.value; + + createAndAppend('div', mainContainerID, { id: 'leftID', class: 'leftClass' }); + createAndAppend('div', mainContainerID, { id: 'rightID', class: 'rightClass' }); + loadDetail(data[i]); + loadContributor(data[i]); + }; + } catch (error) { + createAndAppend('div', root, { text: error.message, class: 'alert-error' }); + } + } + + const HYF_REPS_URL = 'https://api.github.com/orgs/HackYourFuture/repos?per_page=100'; + + window.onload = () => main(HYF_REPS_URL); +} diff --git a/Week3/style.css b/Week3/style.css new file mode 100644 index 000000000..c9040ac64 --- /dev/null +++ b/Week3/style.css @@ -0,0 +1,119 @@ +.alert-error { + color: #721c24; + background-color: #f8d7da; +} + +body { + margin-left: auto; + margin-right: auto; + font-family: Arial, Helvetica, sans-serif; + color: black; + margin-top: 0; + background-color: #f8f8f8; + width: 900px; +} + +.mainContainerClass { + display: flex; + flex-direction: row; + align-items: flex-start; +} + +@media (max-width: 767px) { + body { + width: 100%; + } + #mainContainerClass { + margin: 0; + flex-direction: column; + align-items: stretch; + } +} + +.topBoxClass { + color: white; + background-color: #3f51b5; + padding: 8px 16px; + margin-bottom: 16px; + display: flex; + flex-direction: row; + align-items: center; +} + +.topTextClass { + margin-left: 40px; + font-size: 30px; + font-family: 'Trebuchet MS'; + width: 350px; + height: 32px; + padding: 2px; +} + +.repsSelectClass { + margin-left: 30px; + font-size: 18px; + width: 350px; + height: 32px; + padding: 2px; +} + +.leftClass, .rightClass { + background-color: white; + flex: 1; + padding: 16px; + margin-right: 16px; + margin-bottom: 8px; + border: none; + border-radius: 2px; + background-color: white; + box-shadow: 0 1px 8px 0 rgba(0, 0, 0, 0.2), 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.12) +} + +.leftDetailClass { + table-layout: fixed; + color: rgb(0, 0, 0, 81%); +} + +@media (max-width: 767px) { + .leftClass { + margin: 0; + } +} + +#contTextID { + font-size: 0.9rem; + color: rgb(0, 0, 0, 54%); + padding: 8px 16px 4px 16px; +} + +.contDataClass { + border-bottom: solid 1px rgb(0, 0, 0, 12%); + padding: 16px; + display: flex; + flex-direction: row; + align-items: center; + cursor: pointer; +} + +.contImgClass { + border-radius: 3px; + margin-right: 16px; + height: 48px; +} + +.contDetailClass { + flex: 1; + display: flex; + flex-direction: row; + justify-content: space-between; + align-content: center; +} + +.contBadgeClass { + font-size: 12px; + padding: 2px 8px; + line-height: 1rem; + background-color: gray; + color: white; + border-radius: 4px; +} \ No newline at end of file diff --git a/homework/App.js b/homework/App.js index 32b71e34b..914da3df5 100644 --- a/homework/App.js +++ b/homework/App.js @@ -72,6 +72,7 @@ class App { * @param {Error} error An Error object describing the error. */ renderError(error) { + // eslint-disable-next-line no-console console.log(error); // TODO: replace with your own code } } diff --git a/homework/index.js b/homework/index.js index d3a97645e..5d80712f8 100644 --- a/homework/index.js +++ b/homework/index.js @@ -1,19 +1,20 @@ 'use strict'; { - function fetchJSON(url, cb) { - const xhr = new XMLHttpRequest(); - xhr.open('GET', url); - xhr.responseType = 'json'; - xhr.onload = () => { - if (xhr.status < 400) { - cb(null, xhr.response); - } else { - cb(new Error(`Network error: ${xhr.status} - ${xhr.statusText}`)); - } - }; - xhr.onerror = () => cb(new Error('Network request failed')); - xhr.send(); + function fetchJSON(URL) { + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.open('GET', URL); + xhr.responseType = 'json'; + xhr.onload = () => { + if (xhr.status < 400) { + resolve(xhr.response); + } else { + reject(new Error(`Network error: ${xhr.status} - ${xhr.statusText}`)); + } + }; + xhr.send(); + }); } function createAndAppend(name, parent, options = {}) { @@ -29,19 +30,139 @@ }); return elem; } + function loadDetail(element) { + const leftID = document.getElementById('leftID'); + createAndAppend('ul', leftID, { id: 'leftDetailID', class: 'leftDetailClass' }); - function main(url) { - fetchJSON(url, (err, data) => { - const root = document.getElementById('root'); - if (err) { - createAndAppend('div', root, { text: err.message, class: 'alert-error' }); - } else { - createAndAppend('pre', root, { text: JSON.stringify(data, null, 2) }); - } + const leftDetailID = document.getElementById('leftDetailID'); + createAndAppend('li', leftDetailID, { + id: 'leftListID', + class: 'leftListClass', + text: 'Repository: ', + }); + + const leftListID = document.getElementById('leftListID'); + createAndAppend('a', leftListID, { + text: element.name, + href: element.html_url, + id: 'repositoryNameID', + class: 'repositoryNameClass', }); + + createAndAppend('li', leftDetailID, { + id: 'descList', + text: `Description: ${element.description}`, + }); + createAndAppend('li', leftDetailID, { text: `Forks: ${element.forks}` }); + const updatedAt = new Date(element.updated_at).toLocaleString(); + createAndAppend('li', leftDetailID, { text: `Updated: ${updatedAt}` }); + } + async function loadContributor(element) { + const root = document.getElementById('root'); + try { + const data = await fetchJSON(element.contributors_url); + const rightID = document.getElementById('rightID'); + createAndAppend('p', rightID, { id: 'contTextID', text: 'Contributors' }); + createAndAppend('ul', rightID, { id: 'contListID', class: 'contListClass' }); + + let contURL; + let contData; + let contDetail; + const contListID = document.getElementById('contListID'); + for (let i = 0; i < data.length; i++) { + contURL = createAndAppend('a', contListID, { + href: data[i].html_url, + target: '_blank', + id: 'contUrlID', + class: 'contUrlClass', + }); + contData = createAndAppend('li', contURL, { + id: 'contDataID', + class: 'contDataClass', + }); + + createAndAppend('img', contData, { + src: data[i].avatar_url, + id: 'contImgID', + class: 'contImgClass', + }); + + contDetail = createAndAppend('div', contData, { + id: 'contDetailID', + class: 'contDetailClass', + }); + createAndAppend('div', contDetail, { text: data[i].login }); + createAndAppend('div', contDetail, { + text: data[i].contributions, + id: 'contBadgeID', + class: 'contBadgeClass', + }); + } + } catch (err) { + createAndAppend('div', root, { + id: 'err.message', + class: 'alert-error', + }); + } + } + + async function main(url) { + const root = document.getElementById('root'); + try { + const data = await fetchJSON(url); + createAndAppend('header', root, { + id: 'topBoxID', + class: 'topBoxClass', + }); + const topBoxID = document.getElementById('topBoxID'); + createAndAppend('p', topBoxID, { + id: 'topTextID', + class: 'topTextClass', + text: 'HYF Repositories', + }); + + createAndAppend('div', root, { id: 'mainContainerID', class: 'mainContainerClass' }); + + const mainContainerID = document.getElementById('mainContainerID'); + createAndAppend('div', mainContainerID, { id: 'leftID', class: 'leftClass' }); + createAndAppend('div', mainContainerID, { id: 'rightID', class: 'rightClass' }); + createAndAppend('select', topBoxID, { id: 'repsSelectID', Class: 'repsSelectClass' }); + data.sort((a, b) => a.name.localeCompare(b.name)); + + const repsSelectID = document.getElementById('repsSelectID'); + for (let index = 0; index < data.length; index++) { + createAndAppend('option', repsSelectID, { + value: index, + text: data[index].name, + id: 'optionID', + class: 'optionClass', + }); + } + const selector = document.getElementById('repsSelectID'); + + loadDetail(data[0]); + loadContributor(data[0]); + + selector.onchange = () => { + const leftID = document.getElementById('leftID'); + leftID.parentNode.removeChild(leftID); + + const rightID = document.getElementById('rightID'); + rightID.parentNode.removeChild(rightID); + + const i = selector.value; + + createAndAppend('div', mainContainerID, { id: 'leftID', class: 'leftClass' }); + createAndAppend('div', mainContainerID, { id: 'rightID', class: 'rightClass' }); + loadDetail(data[i]); + loadContributor(data[i]); + }; + } catch (error) { + createAndAppend('div', root, { text: error.message, class: 'alert-error' }); + } } - const HYF_REPOS_URL = 'https://api.github.com/orgs/HackYourFuture/repos?per_page=100'; + const HYF_REPS_URL = 'https://api.github.com/orgs/HackYourFuture/repos?per_page=100'; - window.onload = () => main(HYF_REPOS_URL); + window.onload = () => main(HYF_REPS_URL); } diff --git a/homework/style.css b/homework/style.css index a8985a8a5..c9040ac64 100644 --- a/homework/style.css +++ b/homework/style.css @@ -1,3 +1,119 @@ .alert-error { - color: red; + color: #721c24; + background-color: #f8d7da; +} + +body { + margin-left: auto; + margin-right: auto; + font-family: Arial, Helvetica, sans-serif; + color: black; + margin-top: 0; + background-color: #f8f8f8; + width: 900px; +} + +.mainContainerClass { + display: flex; + flex-direction: row; + align-items: flex-start; +} + +@media (max-width: 767px) { + body { + width: 100%; + } + #mainContainerClass { + margin: 0; + flex-direction: column; + align-items: stretch; + } +} + +.topBoxClass { + color: white; + background-color: #3f51b5; + padding: 8px 16px; + margin-bottom: 16px; + display: flex; + flex-direction: row; + align-items: center; +} + +.topTextClass { + margin-left: 40px; + font-size: 30px; + font-family: 'Trebuchet MS'; + width: 350px; + height: 32px; + padding: 2px; +} + +.repsSelectClass { + margin-left: 30px; + font-size: 18px; + width: 350px; + height: 32px; + padding: 2px; +} + +.leftClass, .rightClass { + background-color: white; + flex: 1; + padding: 16px; + margin-right: 16px; + margin-bottom: 8px; + border: none; + border-radius: 2px; + background-color: white; + box-shadow: 0 1px 8px 0 rgba(0, 0, 0, 0.2), 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 3px 3px -2px rgba(0, 0, 0, 0.12) +} + +.leftDetailClass { + table-layout: fixed; + color: rgb(0, 0, 0, 81%); +} + +@media (max-width: 767px) { + .leftClass { + margin: 0; + } +} + +#contTextID { + font-size: 0.9rem; + color: rgb(0, 0, 0, 54%); + padding: 8px 16px 4px 16px; +} + +.contDataClass { + border-bottom: solid 1px rgb(0, 0, 0, 12%); + padding: 16px; + display: flex; + flex-direction: row; + align-items: center; + cursor: pointer; +} + +.contImgClass { + border-radius: 3px; + margin-right: 16px; + height: 48px; +} + +.contDetailClass { + flex: 1; + display: flex; + flex-direction: row; + justify-content: space-between; + align-content: center; +} + +.contBadgeClass { + font-size: 12px; + padding: 2px 8px; + line-height: 1rem; + background-color: gray; + color: white; + border-radius: 4px; } \ No newline at end of file