UNPKG

githubjs

Version:
574 lines (501 loc) 26.8 kB
// Github.js - v0.1.3 // © 2015, Akshay Sharma Released under the MIT License. (function (root, factory) { // Set up library appropriately for the environment if (typeof define === 'function' && define.amd) { // Define for AMD define(['underscore'], factory); } else if (typeof exports === 'object') { // Export for Node, CommonJS-like modules module.exports = factory(require('underscore')); } else { // As browser global (root is window here) root.Github = factory(root._); } }(this, function (_) { var Github = {}; // Current version of the library. Keep in sync with `package.json` Github.version = '0.1.3'; // Common GitHub API URL var gitApiUrl = 'https://api.github.com/'; // userProfile - render's the github user details to selector // @param Type - JSON -> options [username, selector] Github.userProfile = function (options) { if(options = gitMethods.initialize(options, ['username','selector'], 0)){ var userUrl = gitApiUrl + 'users/' + options.username; gitMethods.getData(userUrl, options, gitMethods.getUserProfileHTML); } else{ console.error("Parameters not passed correctly"); } }; // // repoProfile - render's the github repo details to selector // @param Type - JSON -> options [username, reponame, selector] // Github.repoProfile = function (options) { if(options = gitMethods.initialize(options, ['username','selector','reponame'], 0)){ var repoUrl = gitApiUrl + 'repos/' + options.username +'/'+ options.reponame; gitMethods.getData(repoUrl, options, gitMethods.getRepoProfileHTML); } else{ console.error("Parameters not passed correctly"); } }; // // orgProfile - render's the github organization details to selector // @param Type - JSON -> options [orgname, selector] // Github.orgProfile = function (options) { if(options = gitMethods.initialize(options, ['orgname','selector'], 0)){ var orgUrl = gitApiUrl + 'orgs/' + options.orgname; gitMethods.getData(orgUrl, options, gitMethods.getOrgProfileHTML); } else{ console.error("Parameters not passed correctly"); } }; // // userActivity - render's the github user activity to selector // @param Type - JSON -> options [username, selector and limit (optional)] // Github.userActivity = function (options) { if(options = gitMethods.initialize(options, ['username','selector'], 1)){ var userUrl = gitApiUrl + 'users/' + options.username, eventsUrl = userUrl + '/events'; gitMethods.getData(userUrl, options, gitMethods.getUserProfileHTML); gitMethods.getData(eventsUrl, options, gitMethods.getPublicActivityHTML); } else{ console.error("Parameters not passed correctly"); } }; // // repoActivity - render's the github repository activity to selector // @param Type - JSON -> options [username, reponame, selector and limit (optional)] // Github.repoActivity = function (options) { if(options = gitMethods.initialize(options, ['username','selector','reponame'], 1)){ var repoUrl = gitApiUrl + 'repos/' + options.username +'/'+ options.reponame, eventsUrl = repoUrl + '/events'; gitMethods.getData(repoUrl, options, gitMethods.getRepoProfileHTML); gitMethods.getData(eventsUrl, options, gitMethods.getPublicActivityHTML); } else{ console.error("Parameters not passed correctly"); } }; // // orgActivity - render's the github organization activity to selector // @param Type - JSON -> options [orgname, selector and limit (optional)] // Github.orgActivity = function (options) { if(options = gitMethods.initialize(options, ['orgname','selector'], 1)){ var orgUrl = gitApiUrl + 'orgs/' + options.orgname, eventsUrl = orgUrl + '/events'; gitMethods.getData(orgUrl, options, gitMethods.getOrgProfileHTML); gitMethods.getData(eventsUrl, options, gitMethods.getPublicActivityHTML); } else{ console.error("Parameters not passed correctly"); } }; // Underscore templates for profiles, activities, etc. var gitTemplates = { // Parent container template parentTpl: '<div class="gt-container">'+ '<div class="gt-header gt-shadow">'+ '<div class="gt-loading-txt">Loading..</div>'+ '</div>'+ '<%if(type){%><div class="gt-activity-cnt gt-scrollbar">'+ '<div class="gt-loading-txt">Loading..</div>'+ '</div><%}%>'+ '</div>', // User profile template userProfileTpl: '<div class="gt-usr-avatar">'+ '<a target="_blank" href="<%= html_url%>">'+ '<div class="gt-usr-img" style="background-image: url(<%= avatar_url%>)"> </div>'+ '</a>'+ '</div>'+ '<div class="gt-usr-name">'+ '<span class="user-name"><%= name%></span>'+ '<a target="_blank" href="<%= html_url%>">'+ '<span class="user-login"><%= login%></span>'+ '</a>'+ '</div>'+ '<div class="gt-usr-details">'+ '<div class="gt-usr-repo">'+ '<a target="_blank" href="<%= html_url%>">'+ '<span class="gt-usr-txt"><%= public_repos%></span>'+ '<span class="gt-usr-dt">Repositories</span>'+ '</a>'+ '</div>'+ '<div class="gt-usr-folwr">'+ '<a target="_blank" href="<%= html_url + "/followers"%>">'+ '<span class="gt-usr-txt"><%= followers%></span>'+ '<span class="gt-usr-dt">Followers</span>'+ '</a>'+ '</div>'+ '<div class="gt-usr-folng">'+ '<a target="_blank" href="<%= html_url + "/following"%>">'+ '<span class="gt-usr-txt"><%= following%></span>'+ '<span class="gt-usr-dt">Following</span>'+ '</a>'+ '</div>'+ '</div>', // Repository profile template repoProfileTpl: '<div class="gt-usr-name">'+ '<span class="user-name"><%= name%></span>'+ '<a target="_blank" href="<%= owner.html_url%>">'+ '<span class="user-login"><%= owner.login%></span>'+ '</a>'+ '<p>'+ '<%= description%>'+ '</p>'+ '</div>'+ '<div class="gt-repo-details">'+ '<div class="gt-usr-repo">'+ '<a target="_blank" href="<%= html_url%>">'+ '<span class="gt-usr-txt"><%= stargazers_count%></span>'+ '<span class="gt-usr-dt">Stars</span>'+ '</a>'+ '</div>'+ '<div class="gt-usr-folwr">'+ '<a target="_blank" href="<%= html_url %>">'+ '<span class="gt-usr-txt"><%= subscribers_count%></span>'+ '<span class="gt-usr-dt">Watchers</span>'+ '</a>'+ '</div>'+ '<div class="gt-usr-folng">'+ '<a target="_blank" href="<%= html_url %>">'+ '<span class="gt-usr-txt"><%= forks_count%></span>'+ '<span class="gt-usr-dt">Forks</span>'+ '</a>'+ '</div>'+ '<div class="gt-repo-lg-stat">'+ '</div>'+ '</div>', // Organization profile template orgProfileTpl: '<div class="gt-org-avatar">'+ '<a target="_blank" href="<%= html_url%>">'+ '<div class="gt-org-img" style="background-image: url(<%= avatar_url%>)"> </div>'+ '</a>'+ '</div>'+ '<div class="gt-org-name">'+ '<span class="user-name"><%= name%></span>'+ '<%if(blog){%><a target="_blank" href="<%= blog%>" class="gt-org-link">'+ '<span class="user-login">Website</span>'+ '</a><%}%>'+ '<%if(email){%><a target="_blank" href="mailto:<%= email%>" class="gt-org-link">'+ '<span class="user-login">Email</span>'+ '</a><%}%>'+ '<br><span class="gt-org-repos"><%= public_repos%></span>'+ '<span class="gt-org-repos"> Public Repositories</span>'+ '</div>', // Parent activity container template gitActivityTpl: '<div class="gt-activity <%=type%>">'+ '<div class="gt-avatar-cnt">'+ '<a target="_blank" href="https://github.com/<%= actor.login%>">'+ '<img src="<%=actor.avatar_url%>" class="gt-usr-avatar">'+ '</a>'+ '</div>'+ '<div class="gt-act-cnt">'+ '<div title="<%= created_at%>" class="gt-time-cnt"><div class="gt-time-str"><%= timeString%></div></div>'+ '<%= userLink%>'+'<%= message%>'+ '</div>'+ '<div class="gt-clearfix"></div>'+ '</div>', // Activity templates keyed with activity type CommitCommentEvent:'<span> commented on commit <%= commentLink%> </span>'+ '<p><%= payload.comment.body%></p>', CreateEvent: '<span> created <%= payload.ref_type%> <%= branchLink%> at <%= repoLink%> </span>', DeleteEvent: '<span> deleted <%= payload.ref%> <%= payload.ref_type%> at <%= repoLink%> </span>', ForkEvent: '<span> forked <%= repoLink%> to <%= forkLink%> </span>', GollumEvent: '<span> <%= actionType%> the <%= repoLink%> wiki</span>'+ '<p><%= wikiMessage%></p>', IssueCommentEvent: '<span> commented on issue <%= commentLink%> </span>'+ '<p><%= payload.comment.body%></p>', IssuesEvent: '<span> <%= payload.action%> issue <%= issueUrl%> </span>', MemberEvent: '<span> added <%= memberLink%> to <%= repoLink%> </span>', PublicEvent: '<span> open sourced <%= repoLink%> </span>', PullRequestEvent: '<span> <%= payload.action%> pull request <%= mergeRequestUrl%> </span>'+ '<p><%= payload.pull_request.title%></p>'+ '<p class="pull-req-info"><%= payload.pull_request.commits%><%if(payload.pull_request.commits > 1){%> commits <%}else{%> commit <%}%> '+ 'with <%= payload.pull_request.changed_files%> <%if(payload.pull_request.commits > 1){%> files <%}else{%> file <%}%> changed.</p>', PullRequestReviewCommentEvent: '<span> commented on pull request <%= pullCommentUrl%> </span>'+ '<p><%= payload.comment.body%></p>', PushEvent: '<span> pushed to <%= branchLink%> at <%= repoLink%> </span>'+ '<%= commitsHtml%>', ReleaseEvent: '<span> released <%= tagLink%> at <%= repoLink%> </span>'+ '<br><%= zipLink%>', WatchEvent: '<span> starred <%= repoLink%> </span>', noActivityTpl: '<div class="gt-no-activity">'+ '<span> There are no public events for this account in past 90 days. </span>'+ '</div>', notFoundTpl: '<div class="gt-no-activity">'+ '<span> This account does not exist. </span>'+ '</div>' }; // Object for locally used methods var gitMethods = { // Check variables and render base template initialize: function(options, variables, type){ // Validate varaibles for (var i = 0; i < variables.length; i++) { if(!options[variables[i]]) return false; } // Type - 0 - only profile // Type - 1 - profile and acitivity feed gitMethods.renderContent(gitMethods.getRenderedHTML(gitTemplates['parentTpl'],{ type: type }),options.selector); // Set limit value options.limit = gitMethods.setLimit(options.limit); return options; }, // Check if value is integer checkInteger: function (value) { if (value === parseInt(value, 10)) return true; else return false; }, // Get the rendered template with data getRenderedHTML: function (template, data) { if (data) { return _.template(template)(data); } else{ return _.template(template)(); } }, // Render User profile HTML getUserProfileHTML: function (data, options){ gitMethods.renderContent(gitMethods.getRenderedHTML(gitTemplates.userProfileTpl, data), options.selector,'.gt-header'); }, // Render the repository profile HTML with language stats getRepoProfileHTML: function (data, options){ var languageUrl = gitApiUrl + 'repos/' + options.username +'/'+ options.reponame + '/languages'; // Render template gitMethods.renderContent(gitMethods.getRenderedHTML(gitTemplates.repoProfileTpl, data), options.selector,'.gt-header'); // Fetch language stat for repo gitMethods.getData(languageUrl, options, gitMethods.getLanguageHTML); }, // Render organization profile HTML getOrgProfileHTML: function (data, options){ gitMethods.renderContent(gitMethods.getRenderedHTML(gitTemplates.orgProfileTpl, data), options.selector,'.gt-header'); }, // Render recent public activities of user/repo/organization getPublicActivityHTML: function (data,options) { var html = ''; // Get min of limit of data size var length = (options.limit < data.length)? options.limit : data.length; if (length==0) { // If no activity in last 90 days html += gitMethods.getRenderedHTML(gitTemplates['noActivityTpl']); } else{ // Loop over all the activities for(var index = 0; index < length; index++){ var activity = data[index]; var payload = activity.payload; // Get attributes common to all activities activity.timeString = gitMethods.millisecondsToStr(new Date() - new Date(activity.created_at)); activity.userLink = gitMethods.getGitHubLink(activity.actor.login, activity.actor.login); activity.repoLink = gitMethods.getGitHubLink(activity.repo.name, activity.repo.name); // Get the branch name activity.branchLink = ''; if (payload.ref) { if (payload.ref.substring(0, 11) === 'refs/heads/') { activity.branch = payload.ref.substring(11); } else { activity.branch = payload.ref; } activity.branchLink = gitMethods.getGitHubLink(activity.repo.name + '/tree/' + activity.branch, activity.branch); } // Get the HTML of selected activity type switch(activity.type){ case 'CommitCommentEvent': activity.commentLink = gitMethods.getLink(payload.comment.html_url, activity.repo.name + '@' +payload.comment.commit_id.substring(0,6)); break; case 'CreateEvent': break; case 'DeleteEvent': break; case 'ForkEvent': activity.forkLink = gitMethods.getGitHubLink(payload.forkee.html_url, payload.forkee.full_name); break; case 'GollumEvent': var page = payload.pages[0]; activity.actionType = page.action; activity.wikiMessage = activity.actionType.charAt(0).toUpperCase() + activity.actionType.slice(1) + ' '; activity.wikiMessage += gitMethods.getLink(page.html_url, page.title); break; case 'IssueCommentEvent': activity.commentLink= gitMethods.getLink(payload.comment.html_url, activity.repo.name + '#' +payload.issue.number); break; case 'IssuesEvent': activity.issueUrl = gitMethods.getLink(payload.issue.html_url, activity.repo.name + '#' +payload.issue.number); break; case 'MemberEvent': activity.memberLink = gitMethods.getGitHubLink(payload.member.login, payload.member.login); break; case 'PublicEvent': break; case 'PullRequestEvent': activity.mergeRequestUrl = gitMethods.getLink(payload.pull_request.html_url, activity.repo.name + '#' +payload.pull_request.number); break; case 'PullRequestReviewCommentEvent': activity.pullCommentUrl= gitMethods.getLink(payload.comment.html_url, activity.repo.name + '#' +payload.pull_request.number); break; case 'PushEvent': activity.commitsHtml = gitMethods.getCommitsHTML(activity); break; case 'ReleaseEvent': activity.tagLink = gitMethods.getLink(payload.release.html_url, payload.release.tag_name); activity.zipLink = gitMethods.getLink(payload.release.zipball_url, 'Download Source Code (zip)') break; case 'WatchEvent': break; } // Get activity specific message activity.message = gitMethods.getRenderedHTML(gitTemplates[activity.type], activity); html += gitMethods.getRenderedHTML(gitTemplates['gitActivityTpl'],activity); } } // Render created activity HTML to DOM gitMethods.renderContent(html, options.selector, '.gt-activity-cnt'); }, // Form HTML for commits in PushEvent getCommitsHTML: function(activity){ var html = '<ul class="gt-commit-list">', liElement, shaLink, commitMessage, commit, index, compareLink = '', payload = activity.payload, length = payload.commits.length, shaDiff = payload.before + '...' + payload.head; // Get links for 2 or less commit for(index = 0; index < length; index++){ if (index>1) break; commit = payload.commits[index]; // Create commit li element liElement = '<li class="gt-commit-item" >'; shaLink = gitMethods.getGitHubLink(activity.repo.name + '/commit/' + commit.sha, commit.sha.substring(0, 6)); commitMessage = '<span class="gt-commit-msg">' + commit.message.substring(0,150) + '</span>'; liElement += shaLink liElement += commitMessage; liElement += '</li>' html += liElement; } // Get the diff link between commits if (length === 2) { compareLink = gitMethods.getGitHubLink(activity.repo.name + '/compare/' + shaDiff, 'View comparison for these 2 commits &raquo;','gt-compare-link'); } else if (length > 2) { compareLink = gitMethods.getGitHubLink(activity.repo.name + '/compare/' + shaDiff, (length-2)+' more ' + gitMethods.getPluralWord(length-2,'commit') + ' &raquo;','gt-compare-link'); } html += '</ul>' html += compareLink; return html; }, // Utility for asynchronous AJAX calls getData: function(url, options, callback){ var data, request; request = new XMLHttpRequest(); request.open('GET', url, true); // Use OAuth token if available if (options.OAuth) { request.setRequestHeader('Authorization', 'Token ' + options.OAuth); } request.onload = function(e) { if (request.status >= 200 && request.status < 400){ data = JSON.parse(request.responseText); callback(data, options); } else { // Unsuccessful request - invalid username/ lost internet connectivity/ exceeded rate limit/ API URL not found gitMethods.renderContent(gitMethods.getRenderedHTML(gitTemplates.notFoundTpl, data), options.selector,'.gt-container'); console.error('An error occurred while connecting to GitHub API.'); } }; request.onerror = function(e) { console.error('An error occurred while connecting to GitHub API.'); }; request.send(); }, // Get anchor tag HTML for URL getLink: function(url, title, cssClass) { if (!title) title = url; if (typeof(cssClass) === 'undefined') cssClass = ''; return gitMethods.getRenderedHTML('<a class="' + cssClass + '" href="<%=url%>" target="_blank"><%=title%></a>', { url: url, title: title }); }, // Get anchor tag HTML for non-github URL getGitHubLink: function(url, title, cssClass) { if (!title) title = url; if (typeof(cssClass) === 'undefined') cssClass = ''; return gitMethods.getLink('https://github.com/' + url, title, cssClass); }, // Only for plurals ending with 's'. Yeah! this sucks. getPluralWord: function (count, word) { if (count !== 1) return word + 's'; return word; }, // Get repository language stat HTML getLanguageHTML: function(data, options){ var languageData = [], sum = 0, percentage, languageHtml = ''; // Get total size and create array which can be sorted by value _.each(data, function(value, key){ var data = {}; data.language = key; data.size = value; languageData.push(data); sum += value; }); // Sort languages by usage in repo languageData = languageData.sort(function(a, b){return b.size - a.size}); // Get HTML for each language _.each(languageData, function(element){ percentage = (parseInt(element.size)/sum*100).toFixed(1); languageHtml +='<div class="gt-repo-lg-cnt" style="width: '+ percentage +'%; background: #'+ gitMethods.getRandomColor() +'; " >'+ ' <div class="gt-repo-lg-name" data-title="'+ element.language +' ('+ percentage +'%)"> </div> </div>'; }); // Render language HTML to the container gitMethods.renderContent(languageHtml, options.selector,'.gt-repo-lg-stat'); }, // Get random HEX code for color getRandomColor: function(){ return Math.random().toString(16).substring(2, 8); }, // Convert milliseconds to time string millisecondsToStr: function(milliseconds) { function numberEnding(number) { return (number > 1) ? 's ago' : ' ago'; } var temp = Math.floor(milliseconds / 1000); var years = Math.floor(temp / 31536000); if (years) return years + ' year' + numberEnding(years); var months = Math.floor((temp %= 31536000) / 2592000); if (months) return months + ' month' + numberEnding(months); var days = Math.floor((temp %= 2592000) / 86400); if (days) return days + ' day' + numberEnding(days); var hours = Math.floor((temp %= 86400) / 3600); if (hours) return 'about ' + hours + ' hour' + numberEnding(hours); var minutes = Math.floor((temp %= 3600) / 60); if (minutes) return minutes + ' minute' + numberEnding(minutes); var seconds = temp % 60; if (seconds) return seconds + ' second' + numberEnding(seconds); return 'just now'; }, // Render the content to the selector or subSelector renderContent: function(content, selector, subSelector){ var selectorDivs = document.querySelectorAll(selector); for (var i = 0; i < selectorDivs.length; i++) { // if subSelector is passed, find it if (subSelector) { selectorDiv = selectorDivs[i].querySelector(subSelector); } else{ selectorDiv = selectorDivs[i]; } selectorDiv.innerHTML = content; } }, // Set render limit for activities - default is 30 setLimit: function(value){ var limit; if (value !== 'undefined' && gitMethods.checkInteger(limit = parseInt(value, 10))) { limit = (limit>30)?30:limit; } else { limit = 30; } return limit; } }; return Github; }));