PageSourceSearch

https://harmanbhutani.github.io/assets/plugins/github-activity/src/github-activity.js

js harmanbhutani.github.io collected 2026-10-03 09:16:51 UTC 19,966 bytes, 379 lines download raw bytes

1/*!
2 * GitHub Activity Stream - v0.1.4 - 10/7/2015
3 * https://github.com/caseyscarborough/github-activity
4 *
5 * Copyright (c) 2015 Casey Scarborough
6 * MIT License
7 * http://opensource.org/licenses/MIT
8 */
9
10var GitHubActivity = (function() {
11  'use strict';
12
13  var obj = {};
14
15  var methods = {
16    renderLink: function(url, title, cssClass) {
17      if (!title) { title = url; }
18      if (typeof(cssClass) === 'undefined') cssClass = "";
19      return Mustache.render('<a class="' + cssClass + '" href="{{url}}" target="_blank">{{{title}}}</a>', { url: url, title: title });
20    },
21    renderGitHubLink: function(url, title, cssClass) {
22      if (!title) { title = url; }
23      if (typeof(cssClass) === 'undefined') cssClass = "";
24      return methods.renderLink('https://github.com/' + url, title, cssClass);
25    },
26    getMessageFor: function(data) {
27      var p = data.payload;
28      data.repoLink = methods.renderGitHubLink(data.repo.name);
29      data.userGravatar = Mustache.render('<div class="gha-gravatar-user"><img src="{{url}}" class="gha-gravatar-small"></div>', { url: data.actor.avatar_url });
30
31      // Get the branch name if it exists.
32      if (p.ref) {
33        if (p.ref.substring(0, 11) === 'refs/heads/') {
34          data.branch = p.ref.substring(11);
35        } else {
36          data.branch = p.ref;
37        }
38        data.branchLink = methods.renderGitHubLink(data.repo.name + '/tree/' + data.branch, data.branch) + ' at ';
39      }
40
41      // Only show the first 6 characters of the SHA of each commit if given.
42      if (p.commits) {
43        var shaDiff = p.before + '...' + p.head;
44        var length = p.commits.length;
45        if (length === 2) {
46          // If there are 2 commits, show message 'View comparison for these 2 commits >>'
47          data.commitsMessage = Mustache.render('<a href="https://github.com/{{repo}}/compare/{{shaDiff}}">View comparison for these 2 commits &raquo;</a>', { repo: data.repo.name, shaDiff: shaDiff });
48        } else if (length > 2) {
49          // If there are more than two, show message '(numberOfCommits - 2) more commits >>'
50          data.commitsMessage = Mustache.render('<a href="https://github.com/{{repo}}/compare/{{shaDiff}}">{{length}} more ' + pluralize('commit', length - 2) + ' &raquo;</a>', { repo: data.repo.name, shaDiff: shaDiff, length: p.size - 2 });
51        }
52
53        p.commits.forEach(function(d, i) {
54          if (d.message.length > 66) {
55            d.message = d.message.substring(0, 66) + '...';
56          }
57          if (i < 2) {
58            d.shaLink = methods.renderGitHubLink(data.repo.name + '/commit/' + d.sha, d.sha.substring(0, 6), 'gha-sha');
59            d.committerGravatar = Mustache.render('<img class="gha-gravatar-commit" src="https://gravatar.com/avatar/{{hash}}?s=30&d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png" width="16" />', { hash: md5(d.author.email) });
60          } else {
61            // Delete the rest of the commits after the first 2, and then break out of the each loop.
62            p.commits.splice(2, p.size);
63            return false;
64          }
65        });
66      }
67
68      // Get the link if this is an IssueEvent.
69      if (p.issue) {
70        var title = data.repo.name + "#" + p.issue.number;
71        data.issueLink = methods.renderLink(p.issue.html_url, title);
72        data.issueType = "issue";
73        if (p.issue.pull_request) {
74          data.issueType = "pull request";
75        }
76      }
77
78      // Retrieve the pull request link if this is a PullRequestEvent.
79      if (p.pull_request) {
80        var pr = p.pull_request;
81        data.pullRequestLink = methods.renderLink(pr.html_url, data.repo.name + "#" + pr.number);
82        data.mergeMessage = "";
83
84        // If this was a merge, set the merge message.
85        if (p.pull_request.merged) {
86          p.action = "merged";
87          var message = '{{c}} ' + pluralize('commit', pr.commits) + ' with {{a}} ' + pluralize('addition', pr.additions) + ' and {{d}} ' + pluralize('deletion', pr.deletions);
88          data.mergeMessage = Mustache.render('<br><small class="gha-message-merge">' + message + '</small>', { c: pr.commits, a: pr.additions, d: pr.deletions });
89        }
90      }
91
92      // Get the link if this is a PullRequestReviewCommentEvent
93      if (p.comment && p.comment.pull_request_url) {
94        var title = data.repo.name + "#" + p.comment.pull_request_url.split('/').pop();
95        data.pullRequestLink = methods.renderGitHubLink(p.comment.pull_request_url, title);
96      }
97
98      // Get the comment if one exists, and trim it to 150 characters.
99      if (p.comment && p.comment.body) {
100        data.comment = p.comment.body;
101        if (data.comment.length > 150) {
102          data.comment = data.comment.substring(0, 150) + '...';
103        }
104        if (p.comment.html_url && p.comment.commit_id) {
105          var title = data.repo.name + '@' + p.comment.commit_id.substring(0, 10);
106          data.commentLink = methods.renderLink(p.comment.html_url, title);
107        }
108      }
109
110      if (data.type === 'ReleaseEvent') {
111        data.tagLink = methods.renderLink(p.release.html_url, p.release.tag_name);
112        data.zipLink = methods.renderLink(p.release.zipball_url, 'Download Source Code (zip)');
113      }
114
115      // Wiki event
116      if (data.type === 'GollumEvent') {
117        var page = p.pages[0];
118        data.actionType = page.action;
119        data.message = data.actionType.charAt(0).toUpperCase() + data.actionType.slice(1) + ' ';
120        data.message += methods.renderGitHubLink(page.html_url, page.title);
121      }
122
123      if (data.type === 'FollowEvent') data.targetLink = methods.renderGitHubLink(p.target.login);
124      if (data.type === 'ForkEvent')   data.forkLink   = methods.renderGitHubLink(p.forkee.full_name);
125      if (data.type === 'MemberEvent') data.memberLink = methods.renderGitHubLink(p.member.login);
126
127      if (p.gist) {
128        data.actionType = p.action === 'fork' ? p.action + 'ed' : p.action + 'd';
129        data.gistLink = methods.renderLink(p.gist.html_url, 'gist: ' + p.gist.id);
130      }
131
132      var message = Mustache.render(templates[data.type], data);
133      var timeString = millisecondsToStr(new Date() - new Date(data.created_at));
134      var icon;
135
136      if (data.type == 'CreateEvent' && (['repository', 'branch', 'tag'].indexOf(p.ref_type) >= 0)) {
137        // Display separate icons depending on type of create event.
138        icon = icons[data.type + '_' + p.ref_type];
139      } else {
140        icon = icons[data.type]
141      }
142      var activity = { message: message, icon: icon, timeString: timeString, userLink: methods.renderGitHubLink(data.actor.login) };
143
144      if (singleLineActivities.indexOf(data.type) > -1) {
145        return Mustache.render(templates.SingleLineActivity, activity);
146      }
147      return Mustache.render(templates.Activity, activity);
148    },
149    getHeaderHTML: function(data) {
150      if (data.name) {
151        data.userNameLink = methods.renderLink(data.html_url, data.name);
152      } else {
153        data.withoutName = ' without-name';
154      }
155      data.userLink = methods.renderLink(data.html_url, data.login);
156      data.gravatarLink = methods.renderLink(data.html_url, '<img src="' + data.avatar_url + '">');
157
158      return Mustache.render(templates.UserHeader, data);
159    },
160    getActivityHTML: function(data, limit) {
161      var text = '';
162      var dataLength = data.length;
163      if (limit && limit > dataLength) {
164          limit = dataLength;
165      }
166      limit = limit ? limit : dataLength;
167
168      if (limit === 0) {
169        return Mustache.render(templates.NoActivity, {});
170      }
171      for (var i = 0; i < limit; i++) {
172        text += methods.getMessageFor(data[i]);
173      }
174
175      return text;
176    },
177    getOutputFromRequest: function(url, callback) {
178      var request = new XMLHttpRequest();
179      request.open('GET', url);
180      request.setRequestHeader('Accept', 'application/vnd.github.v3+json');
181
182      request.onreadystatechange = function() {
183        if (request.readyState === 4) {
184          if (request.status >= 200 && request.status < 300){
185            var data = JSON.parse(request.responseText);
186            callback(undefined, data);
187          } else {
188            callback('request for ' + url + ' yielded status ' + request.status);
189          }
190        }
191      };
192
193      request.onerror = function() { callback('An error occurred connecting to ' + url); };
194      request.send();
195    },
196    renderStream: function(output, div) {
197      div.innerHTML = Mustache.render(templates.Stream, { text: output, footer: templates.Footer });
198      div.style.position = 'relative';
199    },
200    writeOutput: function(selector, content) {
201      var div = selector.charAt(0) === '#' ? document.getElementById(selector.substring(1)) : document.getElementsByClassName(selector.substring(1));
202      if (div instanceof HTMLCollection) {
203        for (var i = 0; i < div.length; i++) {
204          methods.renderStream(content, div[i]);
205        }
206      } else {
207        methods.renderStream(content, div);
208      }
209    },
210    renderIfReady: function(selector, header, activity) {
211      if (header && activity) {
212        methods.writeOutput(selector, header + activity);
213      }
214    }
215  };
216
217  obj.feed = function(options) {
218    if (!options.username || !options.selector) {
219      throw "You must specify the username and selector options for the activity stream.";
220      return false;
221    }
222
223    var selector = options.selector,
224        userUrl   = 'https://api.github.com/users/' + options.username,
225        eventsUrl = userUrl + '/events',
226        header,
227        activity;
228
229    if (!!options.repository){
230      eventsUrl = 'https://api.github.com/repos/' + options.username + '/' + options.repository + '/events';
231    }
232
233    if (options.clientId && options.clientSecret) {
234      var authString = '?client_id=' + options.clientId + '&client_secret=' + options.clientSecret;
235      userUrl   += authString;
236      eventsUrl += authString;
237    }
238
239    if (!!options.eventsUrl){
240      eventsUrl = options.eventsUrl;
241    }
242
243    // Allow templates override
244    if (typeof options.templates == 'object') {
245      for (var template in templates) {
246        if (typeof options.templates[template] == 'string') {
247          templates[template] = options.templates[template];
248        }
249      }
250    }
251
252    methods.getOutputFromRequest(userUrl, function(error, output) {
253      if (error) {
254        header = Mustache.render(templates.UserNotFound, { username: options.username });
255      } else {
256        header = methods.getHeaderHTML(output)
257      }
258      methods.renderIfReady(selector, header, activity)
259    });
260
261    methods.getOutputFromRequest(eventsUrl, function(error, output) {
262      if (error) {
263        activity = Mustache.render(templates.EventsNotFound, { username: options.username });
264      } else {
265        var limit = options.limit != 'undefined' ? parseInt(options.limit, 10) : null;
266        activity = methods.getActivityHTML(output, limit);
267      }
268      methods.renderIfReady(selector, header, activity);
269    });
270  };
271
272  return obj;
273}());
274
275// Takes in milliseconds and converts it to a human readable time,
276// such as 'about 3 hours ago' or '23 days ago'
277function millisecondsToStr(milliseconds) {
278  'use strict';
279
280  function numberEnding(number) {
281    return (number > 1) ? 's ago' : ' ago';
282  }
283  var temp = Math.floor(milliseconds / 1000);
284
285  var years = Math.floor(temp / 31536000);
286  if (years) return years + ' year' + numberEnding(years);
287
288  var months = Math.floor((temp %= 31536000) / 2592000);
289  if (months) return months + ' month' + numberEnding(months);
290
291  var days = Math.floor((temp %= 2592000) / 86400);
292  if (days) return days + ' day' + numberEnding(days);
293
294  var hours = Math.floor((temp %= 86400) / 3600);
295  if (hours) return 'about ' + hours + ' hour' + numberEnding(hours);
296
297  var minutes = Math.floor((temp %= 3600) / 60);
298  if (minutes) return minutes + ' minute' + numberEnding(minutes);
299
300  var seconds = temp % 60;
301  if (seconds) return seconds + ' second' + numberEnding(seconds);
302
303  return 'just now';
304}
305
306// Pluralizes a word, but only works when the word requires
307// an 's' to be added for pluralization.
308function pluralize(word, number) {
309  // Yeah I know, this sucks.
310  if (number !== 1) return word + 's';
311  return word;
312}
313
314/** MD5 methods written by Joseph Myers. http://www.myersdaily.org/joseph/javascript/md5-text.html */
315function md5cycle(f,h){var g=f[0],e=f[1],j=f[2],i=f[3];g=ff(g,e,j,i,h[0],7,-680876936);i=ff(i,g,e,j,h[1],12,-389564586);j=ff(j,i,g,e,h[2],17,606105819);e=ff(e,j,i,g,h[3],22,-1044525330);g=ff(g,e,j,i,h[4],7,-176418897);i=ff(i,g,e,j,h[5],12,1200080426);j=ff(j,i,g,e,h[6],17,-1473231341);e=ff(e,j,i,g,h[7],22,-45705983);g=ff(g,e,j,i,h[8],7,1770035416);i=ff(i,g,e,j,h[9],12,-1958414417);j=ff(j,i,g,e,h[10],17,-42063);e=ff(e,j,i,g,h[11],22,-1990404162);g=ff(g,e,j,i,h[12],7,1804603682);i=ff(i,g,e,j,h[13],12,-40341101);j=ff(j,i,g,e,h[14],17,-1502002290);e=ff(e,j,i,g,h[15],22,1236535329);g=gg(g,e,j,i,h[1],5,-165796510);i=gg(i,g,e,j,h[6],9,-1069501632);j=gg(j,i,g,e,h[11],14,643717713);e=gg(e,j,i,g,h[0],20,-373897302);g=gg(g,e,j,i,h[5],5,-701558691);i=gg(i,g,e,j,h[10],9,38016083);j=gg(j,i,g,e,h[15],14,-660478335);e=gg(e,j,i,g,h[4],20,-405537848);g=gg(g,e,j,i,h[9],5,568446438);i=gg(i,g,e,j,h[14],9,-1019803690);j=gg(j,i,g,e,h[3],14,-187363961);e=gg(e,j,i,g,h[8],20,1163531501);g=gg(g,e,j,i,h[13],5,-1444681467);i=gg(i,g,e,j,h[2],9,-51403784);j=gg(j,i,g,e,h[7],14,1735328473);e=gg(e,j,i,g,h[12],20,-1926607734);g=hh(g,e,j,i,h[5],4,-378558);i=hh(i,g,e,j,h[8],11,-2022574463);j=hh(j,i,g,e,h[11],16,1839030562);e=hh(e,j,i,g,h[14],23,-35309556);g=hh(g,e,j,i,h[1],4,-1530992060);i=hh(i,g,e,j,h[4],11,1272893353);j=hh(j,i,g,e,h[7],16,-155497632);e=hh(e,j,i,g,h[10],23,-1094730640);g=hh(g,e,j,i,h[13],4,681279174);i=hh(i,g,e,j,h[0],11,-358537222);j=hh(j,i,g,e,h[3],16,-722521979);e=hh(e,j,i,g,h[6],23,76029189);g=hh(g,e,j,i,h[9],4,-640364487);i=hh(i,g,e,j,h[12],11,-421815835);j=hh(j,i,g,e,h[15],16,530742520);e=hh(e,j,i,g,h[2],23,-995338651);g=ii(g,e,j,i,h[0],6,-198630844);i=ii(i,g,e,j,h[7],10,1126891415);j=ii(j,i,g,e,h[14],15,-1416354905);e=ii(e,j,i,g,h[5],21,-57434055);g=ii(g,e,j,i,h[12],6,1700485571);i=ii(i,g,e,j,h[3],10,-1894986606);j=ii(j,i,g,e,h[10],15,-1051523);e=ii(e,j,i,g,h[1],21,-2054922799);g=ii(g,e,j,i,h[8],6,1873313359);i=ii(i,g,e,j,h[15],10,-30611744);j=ii(j,i,g,e,h[6],15,-1560198380);e=ii(e,j,i,g,h[13],21,1309151649);g=ii(g,e,j,i,h[4],6,-145523070);i=ii(i,g,e,j,h[11],10,-1120210379);j=ii(j,i,g,e,h[2],15,718787259);e=ii(e,j,i,g,h[9],21,-343485551);f[0]=add32(g,f[0]);f[1]=add32(e,f[1]);f[2]=add32(j,f[2]);f[3]=add32(i,f[3])}function cmn(h,e,d,c,g,f){e=add32(add32(e,h),add32(c,f));return add32((e<<g)|(e>>>(32-g)),d)}function ff(g,f,k,j,e,i,h){return cmn((f&k)|((~f)&j),g,f,e,i,h)}function gg(g,f,k,j,e,i,h){return cmn((f&j)|(k&(~j)),g,f,e,i,h)}function hh(g,f,k,j,e,i,h){return cmn(f^k^j,g,f,e,i,h)}function ii(g,f,k,j,e,i,h){return cmn(k^(f|(~j)),g,f,e,i,h)}function md51(c){txt="";var e=c.length,d=[1732584193,-271733879,-1732584194,271733878],b;for(b=64;b<=c.length;b+=64){md5cycle(d,md5blk(c.substring(b-64,b)))}c=c.substring(b-64);var a=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];for(b=0;b<c.length;b++){a[b>>2]|=c.charCodeAt(b)<<((b%4)<<3)}a[b>>2]|=128<<((b%4)<<3);if(b>55){md5cycle(d,a);for(b=0;b<16;b++){a[b]=0}}
315a[14]=e*8;md5cycle(d,a);return d}function md5blk(b){var c=[],a;for(a=0;a<64;a+=4){c[a>>2]=b.charCodeAt(a)+(b.charCodeAt(a+1)<<8)+(b.charCodeAt(a+2)<<16)+(b.charCodeAt(a+3)<<24)}return c}var hex_chr="0123456789abcdef".split("");function rhex(c){var b="",a=0;for(;a<4;a++){b+=hex_chr[(c>>(a*8+4))&15]+hex_chr[(c>>(a*8))&15]}return b}function hex(a){for(var b=0;b<a.length;b++){a[b]=rhex(a[b])}return a.join("")}function md5(a){return hex(md51(a))}function add32(d,c){return(d+c)&4294967295}if(md5("hello")!="5d41402abc4b2a76b9719d911017c592"){function add32(a,d){var c=(a&65535)+(d&65535),b=(a>>16)+(d>>16)+(c>>16);return(b<<16)|(c&65535)}};
316
317var templates = {
318  Stream: '<div class="gha-feed">{{{text}}}<div class="gha-push-small"></div>{{{footer}}}</div>',
319  Activity: '<div id="{{id}}" class="gha-activity">\
320               <div class="gha-activity-icon"><span class="octicon octicon-{{icon}}"></span></div>\
321               <div class="gha-message"><div class="gha-time">{{{timeString}}}</div>{{{userLink}}} {{{message}}}</div>\
322               <div class="gha-clear"></div>\
323             </div>',
324  SingleLineActivity: '<div class="gha-activity gha-small">\
325                         <div class="gha-activity-icon"><span class="octicon octicon-{{icon}}"></span></div>\
326                         <div class="gha-message">{{{userLink}}} {{{message}}}</div><div class="gha-time">{{{timeString}}}</div>\
327                         <div class="gha-clear"></div>\
328                       </div>',
329  UserHeader: '<div class="gha-header">\
330                 <div class="gha-github-icon"><span class="octicon octicon-mark-github"></span></div>\
331                 <div class="gha-user-info{{withoutName}}">{{{userNameLink}}}<p>{{{userLink}}}</p></div>\
332                 <div class="gha-gravatar">{{{gravatarLink}}}</div>\
333               </div><div class="gha-push"></div>',
334  Footer: '<div class="gha-footer">Public Activity <a href="https://github.com/caseyscarborough/github-activity" target="_blank">GitHub Activity Stream</a>',
335  NoActivity: '<div class="gha-info">This user does not have any public activity yet.</div>',
336  UserNotFound: '<div class="gha-info">User {{username}} wasn\'t found.</div>',
337  EventsNotFound: '<div class="gha-info">Events for user {{username}} not found.</div>',
338  CommitCommentEvent: 'commented on commit {{{commentLink}}}<br>{{{userGravatar}}}<small>{{comment}}</small>',
339  CreateEvent: 'created {{payload.ref_type}} {{{branchLink}}}{{{repoLink}}}',
340  DeleteEvent: 'deleted {{payload.ref_type}} {{payload.ref}} at {{{repoLink}}}',
341  FollowEvent: 'started following {{{targetLink}}}',
342  ForkEvent: 'forked {{{repoLink}}} to {{{forkLink}}}',
343  GistEvent: '{{actionType}} {{{gistLink}}}',
344  GollumEvent: '{{actionType}} the {{{repoLink}}} wiki<br>{{{userGravatar}}}<small>{{{message}}}</small>',
345  IssueCommentEvent: 'commented on {{issueType}} {{{issueLink}}}<br>{{{userGravatar}}}<small>{{comment}}</small>',
346  IssuesEvent: '{{payload.action}} issue {{{issueLink}}}<br>{{{userGravatar}}}<small>{{payload.issue.title}}</small>',
347  MemberEvent: 'added {{{memberLink}}} to {{{repoLink}}}',
348  PublicEvent: 'open sourced {{{repoLink}}}',
349  PullRequestEvent: '{{payload.action}} pull request {{{pullRequestLink}}}<br>{{{userGravatar}}}<small>{{payload.pull_request.title}}</small>{{{mergeMessage}}}',
350  PullRequestReviewCommentEvent: 'commented on pull request {{{pullRequestLink}}}<br>{{{userGravatar}}}<small>{{comment}}</small>',
351  PushEvent: 'pushed to {{{branchLink}}}{{{repoLink}}}<br>\
352                <ul class="gha-commits">{{#payload.commits}}<li><small>{{{committerGravatar}}} {{{shaLink}}} {{message}}</small></li>{{/payload.commits}}</ul>\
353                <small class="gha-message-commits">{{{commitsMessage}}}</small>',
354  ReleaseEvent: 'released {{{tagLink}}} at {{{repoLink}}}<br>{{{userGravatar}}}<small><span class="octicon octicon-cloud-download"></span>  {{{zipLink}}}</small>',
355  WatchEvent: 'starred {{{repoLink}}}'
356},
357
358icons = {
359  CommitCommentEvent: 'comment-discussion',
360  CreateEvent_repository: 'repo-create',
361  CreateEvent_tag: 'tag-add',
362  CreateEvent_branch: 'git-branch-create',
363  DeleteEvent: 'repo-delete',
364  FollowEvent: 'person-follow',
365  ForkEvent: 'repo-forked',
366  GistEvent: 'gist',
367  GollumEvent: 'repo',
368  IssuesEvent: 'issue-opened',
369  IssueCommentEvent: 'comment-discussion',
370  MemberEvent: 'person',
371  PublicEvent: 'globe',
372  PullRequestEvent: 'git-pull-request',
373  PullRequestReviewCommentEvent: 'comment-discussion',
374  PushEvent: 'git-commit',
375  ReleaseEvent: 'tag-add',
376  WatchEvent: 'star'
377},
378
379singleLineActivities = [ 'CreateEvent', 'DeleteEvent', 'FollowEvent', 'ForkEvent', 'GistEvent', 'Member
379Event', 'WatchEvent' ];

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.