window.addEventListener('load', () => { initDT(); // Initialize the DatatTable and window.columnNames variables addDarkmodeWidget(); const repo = getRepoFromUrl(); if (repo) { document.getElementById('q').value = repo; fetchData(); } }); document.getElementById('form').addEventListener('submit', e => { e.preventDefault(); fetchData(); }); function addDarkmodeWidget() { new Darkmode( { label: '🌓' } ).showWidget(); } function fetchData() { const repo = document.getElementById('q').value; const re = /[-_\w]+\/[-_.\w]+/; const urlRepo = getRepoFromUrl(); if (!urlRepo || urlRepo !== repo) { window.history.pushState('', '', `#${repo}`); } if (re.test(repo)) { fetchAndShow(repo); } else { showMsg( 'Invalid GitHub repository! Format is <username>/<repo>', 'danger' ); } } function updateDT(data) { // Remove any alerts, if any: if ($('.alert')) $('.alert').remove(); // Format dataset and redraw DataTable. Use second index for key name const forks = []; for (let fork of data) { fork.repoLink = `Link`; fork.ownerName = `${fork.owner ? fork.owner.login : 'Unknown'}`; forks.push(fork); } const dataSet = forks.map(fork => window.columnNamesMap.map(colNM => fork[colNM[1]]) ); window.forkTable .clear() .rows.add(dataSet) .draw(); } function initDT() { // Create ordered Object with column name and mapped display name window.columnNamesMap = [ // [ 'Repository', 'full_name' ], ['Link', 'repoLink'], // custom key ['Owner', 'ownerName'], // custom key ['Name', 'name'], ['Branch', 'default_branch'], ['Stars', 'stargazers_count'], ['Forks', 'forks'], ['Open Issues', 'open_issues_count'], ['Size', 'size'], ['Last Push', 'pushed_at'], ]; // Sort by stars: const sortColName = 'Stars'; const sortColumnIdx = window.columnNamesMap .map(pair => pair[0]) .indexOf(sortColName); // Use first index for readable column name // we use moment's fromNow() if we are rendering for `pushed_at`; better solution welcome window.forkTable = $('#forkTable').DataTable({ columns: window.columnNamesMap.map(colNM => { return { title: colNM[0], render: colNM[1] === 'pushed_at' ? (data, type, _row) => { if (type === 'display') { return moment(data).fromNow(); } return data; } : null, }; }), order: [[sortColumnIdx, 'desc']], // paging: false, searchBuilder:{ // all options at default } }); let table = window.forkTable; new $.fn.dataTable.SearchBuilder(table, {}); table.searchBuilder.container().prependTo(table.table().container()); } function fetchAndShow(repo) { repo = repo.replace('https://github.com/', ''); repo = repo.replace('http://github.com/', ''); repo = repo.replace(/\.git$/, ''); fetch( `https://api.github.com/repos/${repo}/forks?sort=stargazers&per_page=100` ) .then(response => { if (!response.ok) throw Error(response.statusText); return response.json(); }) .then(data => { updateDT(data); }) .catch(error => { const msg = error.toString().indexOf('Forbidden') >= 0 ? 'Error: API Rate Limit Exceeded' : error; showMsg(`${msg}. Additional info in console`, 'danger'); console.error(error); }); } function showMsg(msg, type) { let alert_type = 'alert-info'; if (type === 'danger') { alert_type = 'alert-danger'; } document.getElementById('footer').innerHTML = ''; document.getElementById('data-body').innerHTML = ` `; } function getRepoFromUrl() { const urlRepo = location.hash && location.hash.slice(1); return urlRepo && decodeURIComponent(urlRepo); }