├── views
├── user.html
├── repo.html
├── org.html
├── opt_out.html
├── job.html
├── error.html
├── resumeOrgs.html
├── index.html
└── resume.html
├── images
├── srt.png
├── loader.gif
├── ttab.png
└── low_contrast_linen.png
├── config.ru
├── README.markdown
├── index.html
├── css
├── print.css
└── resume.css
└── js
├── mustache.js
└── githubresume.js
/views/user.html:
--------------------------------------------------------------------------------
1 |
2 |
My GitHub Résumé
3 |
4 |
5 |
6 | As a software startup owner I really enjoy when people send us their
7 | résumés and they include their github account so we can see tangible work they have done.
8 |
9 |
10 |
11 | After a tweet by John Resig
12 | I imagined that it may be nice for people to be able to generate their GitHub résumés.
13 |
14 |
15 |
16 |
22 |
23 |
24 |
25 |
26 |
27 |
See some popular users
28 |
38 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
Notes, Information and Future features
54 |
55 | This is the first version. I am planning on adding
56 | things as such as your most committed forks, most committed repositories and make the "My Popular Repositories" be built from
57 | your complete list of repositories. Feel free to fork the page if you want to help :-)
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
76 |
--------------------------------------------------------------------------------
/views/resume.html:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
{{name}}
14 | {{userStatus}}
15 |
16 |
17 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
GitHub Profile
35 |
36 |
37 |
38 | On GitHub {{#earlyAdopter}}as an early adopter {{/earlyAdopter}} since {{since}}, {{name}} is a developer
39 | {{#location}}
40 | based in {{location}}
41 | {{/location}}
42 | {{#repos}}with {{repos}} public {{reposLabel}}{{/repos}}{{^repos}}without any public repository for now{{/repos}}{{#followers}} and {{followers}} {{followersLabel}}{{/followers}}.
43 |
44 |
45 |
46 | {{#website}}
47 |
48 |
49 |
Website
50 |
51 |
54 |
55 | {{/website}}
56 |
57 |
58 |
Languages
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
Popular Repositories
67 |
68 |
69 |
70 |
Loading information...
71 |
72 |
73 |
74 |
75 |
76 |
Organizations
77 |
78 |
79 |
80 |
Loading information...
81 |
82 |
83 |
84 |
85 |
86 |
About This Résumé
87 |
88 |
89 |
90 | This résumé is generated automatically using public information from the developer's GitHub account. The repositories are
91 | ordered by popularity based on a very simple popularity heuristic that defines the popularity of a repository
92 | by its sum of watchers and forks. Do not hesitate to visit {{name}}'s GitHub page
93 | for a complete work history.
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/css/resume.css:
--------------------------------------------------------------------------------
1 | /*
2 | ---------------------------------------------------------------------------------
3 | STRIPPED DOWN RESUME TEMPLATE
4 | html resume
5 |
6 | v0.9: 5/28/09
7 |
8 | design and code by: thingsthatarebrown.com
9 | (matt brown)
10 | ---------------------------------------------------------------------------------
11 | */
12 |
13 | .msg {
14 | padding: 10px;
15 | background: #222;
16 | position: relative;
17 | }
18 |
19 | .msg h1 { color: #fff }
20 |
21 | .msg a {
22 | margin-left: 20px;
23 | background: #408814;
24 | color: white;
25 | padding: 4px 8px;
26 | text-decoration: none;
27 | }
28 |
29 | .msg a:hover { background: #266400 }
30 |
31 | html {
32 | height: 100% ! important;
33 | background: url('../images/low_contrast_linen.png');
34 | }
35 |
36 | /* //-- yui-grids style overrides -- */
37 |
38 | body {
39 | font-family: Georgia, Garamond, "Times New Roman", Times, serif;
40 | color: #444;
41 | }
42 |
43 | .yui-gf {
44 | margin-bottom: 2em;
45 | padding-bottom: 2em;
46 | border-bottom: 1px solid #ccc;
47 | }
48 |
49 | .yui-gf div.first { width: 12.3% }
50 |
51 | .yui-gf .yui-u { width: 80.2% }
52 |
53 | /* //-- header, body, footer -- */
54 |
55 | #hd {
56 | margin: 2.5em 0 3em 0;
57 | padding-bottom: 1.5em;
58 | border-bottom: 1px solid #ccc;
59 | }
60 |
61 | #hd h1 {
62 | font-size: 48px;
63 | text-transform: uppercase;
64 | letter-spacing: 3px;
65 | }
66 |
67 | #hd h2 {
68 | text-transform: uppercase;
69 | letter-spacing: 2px;
70 | }
71 |
72 | #bd,
73 | #ft { margin-bottom: 2em }
74 |
75 | #ft p {
76 | margin-bottom: 0;
77 | text-align: center;
78 | }
79 |
80 | /* //-- footer -- */
81 |
82 | #ft {
83 | padding: 1em 0 5em 0;
84 | font-size: 92%;
85 | border-top: 1px solid #ccc;
86 | text-align: center;
87 | }
88 |
89 | /* //-- core typography and style -- */
90 |
91 | h2 { font-size: 152% }
92 |
93 | h3,
94 | h4 { font-size: 122% }
95 |
96 | h1,
97 | h2,
98 | h3,
99 | h4 { color: #333 }
100 |
101 | p {
102 | font-size: 100%;
103 | line-height: 18px;
104 | }
105 |
106 | a { color: #990003 }
107 |
108 | a:hover { text-decoration: none }
109 |
110 | strong { font-weight: bold }
111 |
112 | li {
113 | line-height: 24px;
114 | border-bottom: 1px solid #ccc;
115 | }
116 |
117 | p.enlarge {
118 | font-size: 144%;
119 | line-height: 24px;
120 | }
121 |
122 | .contact-info {
123 | margin-top: 7px;
124 | text-align: right;
125 | font-size: 12px;
126 | position: relative;
127 | float: left;
128 | width: 100%;
129 | }
130 |
131 | .contact-info img {
132 | float: right;
133 | border: 1px solid #ccc;
134 | width: 140px;
135 | height: 140px;
136 | margin: -24px 0 14px;
137 | border-radius: 3px;
138 | }
139 |
140 | .contact-info a {
141 | position : relative;
142 | float: left;
143 | width: 100%;
144 | }
145 |
146 | .first h2 { font-style: italic }
147 |
148 | .last { border-bottom: 0 }
149 |
150 | /* //-- section styles -- */
151 |
152 | #pdf {
153 | display: block;
154 | float: left;
155 | background: #666;
156 | color: #fff;
157 | padding: 6px 50px 6px 12px;
158 | margin-bottom: 6px;
159 | text-decoration: none;
160 | }
161 |
162 | #pdf:hover { background: #222 }
163 |
164 | .org,
165 | .job {
166 | position: relative;
167 | margin-bottom: 1em;
168 | padding-bottom: 1em;
169 | border-bottom: 1px solid #ccc;
170 | }
171 |
172 | .org h3,
173 | .job h3 {
174 | font-size: 100%;
175 | }
176 |
177 | .org h4,
178 | .job h4 {
179 | position: absolute;
180 | top: 0.35em;
181 | right: 0;
182 | }
183 |
184 | .org a,
185 | .job a {
186 | border: none;
187 | text-decoration: none;
188 | }
189 |
190 | .org p,
191 | .job p {
192 | margin: 0.75em 0 3em 0;
193 | font-size: 122%;
194 | }
195 |
196 | .last { border: none }
197 |
198 | .talent {
199 | width: 32%;
200 | float: left;
201 | }
202 |
203 | .talent h2 { margin-bottom: 6px }
204 |
205 | #srt-ttab {
206 | margin-bottom: 100px;
207 | text-align: center;
208 | }
209 |
210 | #srt-ttab img.last { margin-top: 20px }
211 |
212 | /* --// override to force 1/8th width grids -- */
213 |
214 | #username {
215 | position: relative;
216 | float: left;
217 | height: 30px;
218 | width: 75%;
219 | border: 2px solid #444;
220 | -webkit-border-radius: 4px;
221 | -moz-border-radius: 4px;
222 | border-radius: 4px;
223 | font-family: Georgia, Garamond, "Times New Roman", Times, serif;
224 | color: #444;
225 | font-size: 18px;
226 | padding: 5px;
227 | }
228 |
229 | #gen {
230 | position: relative;
231 | float: left;
232 | margin-left: 10px;
233 | width: 20%;
234 | height: 44px;
235 | font-family: Georgia, Garamond, "Times New Roman", Times, serif;
236 | font-size: 18px;
237 | color: #fff;
238 | background-image: -webkit-gradient( linear, left bottom, left top, color-stop(0.21, rgb(31,31,31)), color-stop(0.61, rgb(51,51,51)) );
239 | background-image: -moz-linear-gradient( center bottom, rgb(31,31,31) 21%, rgb(51,51,51) 61% );
240 | border: 1px solid #444;
241 | -webkit-border-radius: 4px;
242 | -moz-border-radius: 4px;
243 | border-radius: 4px;
244 | }
245 |
246 | #gen:hover { cursor: pointer }
247 |
248 | .yui-gb {
249 | margin-top: 10px;
250 | margin-bottom: 10px;
251 | padding-bottom: 10px;
252 | }
253 |
254 | .enlarge-medium { font-size: 15px }
255 |
256 | .yui-gb ul { width: 80% }
257 |
258 | noscript {
259 | display: block;
260 | margin-top: 30px;
261 | font-size: 152%;
262 | color: #990003;
263 | }
264 |
265 | #actions {
266 | padding: 30px 0 40px 0;
267 | margin: 0 auto 0 auto;
268 | width: 73.076em;
269 | }
270 |
271 | #actions a {
272 | color: #ccc;
273 | text-decoration: none;
274 | text-shadow: 0 0 5px #000;
275 | margin-left: 1em;
276 | }
277 |
278 | #actions * { float: right }
279 |
280 | #doc {
281 | margin-top: 70px;
282 | background: #f5f5f5;
283 | padding: 25px 107px 0px 107px;
284 | }
285 |
286 | #doc2 { padding-bottom: 70px }
287 |
288 | @media screen and (max-width: 59.9em) {
289 | #inner {
290 | padding: 5 5 5 5;
291 | margin: 0 auto;
292 | background: #f5f5f5;
293 | border: solid #666;
294 | border-width: 8px 0 2px 0;
295 | box-shadow: 0 1px 3px #000;
296 | }
297 | #hd h1 {
298 | font-size: 2.2em;
299 | font-weight: bold;
300 | text-transform: uppercase;
301 | letter-spacing: 0.2em;
302 | }
303 |
304 | #hd h2 {
305 | font-size: 1.5em;
306 | text-transform: uppercase;
307 | letter-spacing: 0.2em;
308 | }
309 |
310 | #doc2 {
311 | width: 100%;
312 | padding-bottom: 1em;
313 | }
314 | #actions{
315 | display: none;
316 | }
317 |
318 | h1{
319 | font-size: 2em;
320 | }
321 | h2{
322 | font-size: 1.5em;
323 | }
324 | p.enlarge {
325 | font-size: 1.4em;
326 | line-height: 1.2em;
327 | }
328 | li {
329 | line-height: 1.6em;
330 | }
331 | h3, h4{
332 | font-size: 1.3em;
333 | }
334 | p{
335 | font-size: 1.2em;
336 | line-height: 1.1em;
337 | }
338 | .org p, .job p {
339 | margin: 0.75em 0 1.5em 0;
340 | }
341 | .first h2, .first h1{
342 | padding-left: 0.5em;
343 | }
344 |
345 | .yui-gf div.first {
346 | width: 20%;
347 | }
348 |
349 | .yui-gf .yui-u { width: 80% }
350 |
351 | }
352 |
353 | @media screen and (min-width: 60em) {
354 | #inner {
355 | padding: 10px 80px;
356 | margin: 0 auto;
357 | background: #f5f5f5;
358 | border: solid #666;
359 | border-width: 8px 0 2px 0;
360 | box-shadow: 0 1px 3px #000;
361 | }
362 | }
--------------------------------------------------------------------------------
/js/mustache.js:
--------------------------------------------------------------------------------
1 | /*
2 | mustache.js — Logic-less templates in JavaScript
3 |
4 | See http://mustache.github.com/ for more info.
5 | */
6 |
7 | var Mustache = function() {
8 | var Renderer = function() {};
9 |
10 | Renderer.prototype = {
11 | otag: "{{",
12 | ctag: "}}",
13 | pragmas: {},
14 | buffer: [],
15 | pragmas_implemented: {
16 | "IMPLICIT-ITERATOR": true
17 | },
18 | context: {},
19 |
20 | render: function(template, context, partials, in_recursion) {
21 | // reset buffer & set context
22 | if(!in_recursion) {
23 | this.context = context;
24 | this.buffer = []; // TODO: make this non-lazy
25 | }
26 |
27 | // fail fast
28 | if(!this.includes("", template)) {
29 | if(in_recursion) {
30 | return template;
31 | } else {
32 | this.send(template);
33 | return;
34 | }
35 | }
36 |
37 | template = this.render_pragmas(template);
38 | var html = this.render_section(template, context, partials);
39 | if(in_recursion) {
40 | return this.render_tags(html, context, partials, in_recursion);
41 | }
42 |
43 | this.render_tags(html, context, partials, in_recursion);
44 | },
45 |
46 | /*
47 | Sends parsed lines
48 | */
49 | send: function(line) {
50 | if(line != "") {
51 | this.buffer.push(line);
52 | }
53 | },
54 |
55 | /*
56 | Looks for %PRAGMAS
57 | */
58 | render_pragmas: function(template) {
59 | // no pragmas
60 | if(!this.includes("%", template)) {
61 | return template;
62 | }
63 |
64 | var that = this;
65 | var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
66 | this.ctag);
67 | return template.replace(regex, function(match, pragma, options) {
68 | if(!that.pragmas_implemented[pragma]) {
69 | throw({message:
70 | "This implementation of mustache doesn't understand the '" +
71 | pragma + "' pragma"});
72 | }
73 | that.pragmas[pragma] = {};
74 | if(options) {
75 | var opts = options.split("=");
76 | that.pragmas[pragma][opts[0]] = opts[1];
77 | }
78 | return "";
79 | // ignore unknown pragmas silently
80 | });
81 | },
82 |
83 | /*
84 | Tries to find a partial in the curent scope and render it
85 | */
86 | render_partial: function(name, context, partials) {
87 | name = this.trim(name);
88 | if(!partials || partials[name] === undefined) {
89 | throw({message: "unknown_partial '" + name + "'"});
90 | }
91 | if(typeof(context[name]) != "object") {
92 | return this.render(partials[name], context, partials, true);
93 | }
94 | return this.render(partials[name], context[name], partials, true);
95 | },
96 |
97 | /*
98 | Renders inverted (^) and normal (#) sections
99 | */
100 | render_section: function(template, context, partials) {
101 | if(!this.includes("#", template) && !this.includes("^", template)) {
102 | return template;
103 | }
104 |
105 | var that = this;
106 | // CSW - Added "+?" so it finds the tighest bound, not the widest
107 | var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag +
108 | "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag +
109 | "\\s*", "mg");
110 |
111 | // for each {{#foo}}{{/foo}} section do...
112 | return template.replace(regex, function(match, type, name, content) {
113 | var value = that.find(name, context);
114 | if(type == "^") { // inverted section
115 | if(!value || that.is_array(value) && value.length === 0) {
116 | // false or empty list, render it
117 | return that.render(content, context, partials, true);
118 | } else {
119 | return "";
120 | }
121 | } else if(type == "#") { // normal section
122 | if(that.is_array(value)) { // Enumerable, Let's loop!
123 | return that.map(value, function(row) {
124 | return that.render(content, that.create_context(row),
125 | partials, true);
126 | }).join("");
127 | } else if(that.is_object(value)) { // Object, Use it as subcontext!
128 | return that.render(content, that.create_context(value),
129 | partials, true);
130 | } else if(typeof value === "function") {
131 | // higher order section
132 | return value.call(context, content, function(text) {
133 | return that.render(text, context, partials, true);
134 | });
135 | } else if(value) { // boolean section
136 | return that.render(content, context, partials, true);
137 | } else {
138 | return "";
139 | }
140 | }
141 | });
142 | },
143 |
144 | /*
145 | Replace {{foo}} and friends with values from our view
146 | */
147 | render_tags: function(template, context, partials, in_recursion) {
148 | // tit for tat
149 | var that = this;
150 |
151 | var new_regex = function() {
152 | return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
153 | that.ctag + "+", "g");
154 | };
155 |
156 | var regex = new_regex();
157 | var tag_replace_callback = function(match, operator, name) {
158 | switch(operator) {
159 | case "!": // ignore comments
160 | return "";
161 | case "=": // set new delimiters, rebuild the replace regexp
162 | that.set_delimiters(name);
163 | regex = new_regex();
164 | return "";
165 | case ">": // render partial
166 | return that.render_partial(name, context, partials);
167 | case "{": // the triple mustache is unescaped
168 | return that.find(name, context);
169 | default: // escape the value
170 | return that.escape(that.find(name, context));
171 | }
172 | };
173 | var lines = template.split("\n");
174 | for(var i = 0; i < lines.length; i++) {
175 | lines[i] = lines[i].replace(regex, tag_replace_callback, this);
176 | if(!in_recursion) {
177 | this.send(lines[i]);
178 | }
179 | }
180 |
181 | if(in_recursion) {
182 | return lines.join("\n");
183 | }
184 | },
185 |
186 | set_delimiters: function(delimiters) {
187 | var dels = delimiters.split(" ");
188 | this.otag = this.escape_regex(dels[0]);
189 | this.ctag = this.escape_regex(dels[1]);
190 | },
191 |
192 | escape_regex: function(text) {
193 | // thank you Simon Willison
194 | if(!arguments.callee.sRE) {
195 | var specials = [
196 | '/', '.', '*', '+', '?', '|',
197 | '(', ')', '[', ']', '{', '}', '\\'
198 | ];
199 | arguments.callee.sRE = new RegExp(
200 | '(\\' + specials.join('|\\') + ')', 'g'
201 | );
202 | }
203 | return text.replace(arguments.callee.sRE, '\\$1');
204 | },
205 |
206 | /*
207 | find `name` in current `context`. That is find me a value
208 | from the view object
209 | */
210 | find: function(name, context) {
211 | name = this.trim(name);
212 |
213 | // Checks whether a value is thruthy or false or 0
214 | function is_kinda_truthy(bool) {
215 | return bool === false || bool === 0 || bool;
216 | }
217 |
218 | var value;
219 | if(is_kinda_truthy(context[name])) {
220 | value = context[name];
221 | } else if(is_kinda_truthy(this.context[name])) {
222 | value = this.context[name];
223 | }
224 |
225 | if(typeof value === "function") {
226 | return value.apply(context);
227 | }
228 | if(value !== undefined) {
229 | return value;
230 | }
231 | // silently ignore unkown variables
232 | return "";
233 | },
234 |
235 | // Utility methods
236 |
237 | /* includes tag */
238 | includes: function(needle, haystack) {
239 | return haystack.indexOf(this.otag + needle) != -1;
240 | },
241 |
242 | /*
243 | Does away with nasty characters
244 | */
245 | escape: function(s) {
246 | s = String(s === null ? "" : s);
247 | return s.replace(/&(?!\w+;)|["<>\\]/g, function(s) {
248 | switch(s) {
249 | case "&": return "&";
250 | case "\\": return "\\\\";
251 | case '"': return '\"';
252 | case "<": return "<";
253 | case ">": return ">";
254 | default: return s;
255 | }
256 | });
257 | },
258 |
259 | // by @langalex, support for arrays of strings
260 | create_context: function(_context) {
261 | if(this.is_object(_context)) {
262 | return _context;
263 | } else {
264 | var iterator = ".";
265 | if(this.pragmas["IMPLICIT-ITERATOR"]) {
266 | iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
267 | }
268 | var ctx = {};
269 | ctx[iterator] = _context;
270 | return ctx;
271 | }
272 | },
273 |
274 | is_object: function(a) {
275 | return a && typeof a == "object";
276 | },
277 |
278 | is_array: function(a) {
279 | return Object.prototype.toString.call(a) === '[object Array]';
280 | },
281 |
282 | /*
283 | Gets rid of leading and trailing whitespace
284 | */
285 | trim: function(s) {
286 | return s.replace(/^\s*|\s*$/g, "");
287 | },
288 |
289 | /*
290 | Why, why, why? Because IE. Cry, cry cry.
291 | */
292 | map: function(array, fn) {
293 | if (typeof array.map == "function") {
294 | return array.map(fn);
295 | } else {
296 | var r = [];
297 | var l = array.length;
298 | for(var i = 0; i < l; i++) {
299 | r.push(fn(array[i]));
300 | }
301 | return r;
302 | }
303 | }
304 | };
305 |
306 | return({
307 | name: "mustache.js",
308 | version: "0.3.0",
309 |
310 | /*
311 | Turns a template and view into HTML
312 | */
313 | to_html: function(template, view, partials, send_fun) {
314 | var renderer = new Renderer();
315 | if(send_fun) {
316 | renderer.send = send_fun;
317 | }
318 | renderer.render(template, view, partials);
319 | if(!send_fun) {
320 | return renderer.buffer.join("\n");
321 | }
322 | }
323 | });
324 | }();
--------------------------------------------------------------------------------
/js/githubresume.js:
--------------------------------------------------------------------------------
1 | var urlParams = {};
2 | var username;
3 | var trackerId = 'UA-21222559-1';
4 |
5 | (function () {
6 | var e,
7 | a = /\+/g, // Regex for replacing addition symbol with a space
8 | r = /([^&=]+)=?([^&]*)/g,
9 | d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
10 | q = window.location.search.substring(1);
11 |
12 | while (e = r.exec(q)) {
13 | urlParams[0] = d(e[1]);
14 | }
15 | })();
16 |
17 | $(document).ready(function() {
18 | try {
19 | if (urlParams[0] !== undefined) {
20 | username = urlParams[0];
21 | run();
22 | } else {
23 | home();
24 | }
25 | } catch (err) {
26 | try {
27 | console.log(err);
28 | } catch (e) {
29 | /*fail silently*/
30 | }
31 | }
32 | });
33 |
34 | var error = function() {
35 | $.ajax({
36 | url: 'views/error.html',
37 | dataType: 'html',
38 | success: function(data) {
39 | var template = data;
40 | $('#resume').html(data);
41 | }
42 | });
43 | };
44 |
45 | var home = function() {
46 | $.ajax({
47 | url: 'views/index.html',
48 | dataType: 'html',
49 | success: function(data) {
50 | var template = data;
51 | $('#resume').html(data);
52 | }
53 | });
54 | };
55 |
56 | var github_user = function(username, callback) {
57 | $.getJSON('https://api.github.com/users/' + username + '?callback=?', callback);
58 | }
59 |
60 | var github_user_repos = function(username, callback, page_number, prev_data) {
61 | var page = (page_number ? page_number : 1),
62 | url = 'https://api.github.com/users/' + username + '/repos?callback=?',
63 | data = (prev_data ? prev_data : []);
64 |
65 | if (page_number > 1) {
66 | url += '&page=' + page_number;
67 | }
68 | $.getJSON(url, function(repos) {
69 | data = data.concat(repos.data);
70 | if (repos.data.length > 0) {
71 | github_user_repos(username, callback, page + 1, data);
72 | } else {
73 | callback(data);
74 | }
75 | });
76 | }
77 |
78 | var github_user_orgs = function(username, callback) {
79 | $.getJSON('https://api.github.com/users/' + username + '/orgs?callback=?', callback);
80 | }
81 |
82 | // Check to see if the user has starred the resume.github.com repo.
83 | //
84 | // Returns true/false.
85 | var github_user_starred_resume = function(username, page) {
86 | var star = false;
87 | var repos = [];
88 | var page = (page ? page : 1);
89 | var url = 'https://api.github.com/users/' + username + '/starred?page=' + page;
90 |
91 | $.ajax({
92 | url: url,
93 | async: false,
94 | dataType: 'json',
95 | success: function(data) {
96 | repos = data;
97 | }
98 | });
99 |
100 | $.each(repos, function(i, repo) {
101 | if (repo.full_name == "resume/resume.github.com") {
102 | star = true;
103 | return false; // stop iterating
104 | }
105 | });
106 |
107 | if (star) {
108 | return star;
109 | }
110 |
111 | if (repos.length > 0) {
112 | star = github_user_starred_resume(username, page + 1);
113 | }
114 |
115 | return star;
116 | }
117 |
118 | var run = function() {
119 | var itemCount = 0,
120 | maxItems = 5,
121 | maxLanguages = 9;
122 |
123 | if (! github_user_starred_resume(username)) {
124 | $.ajax({
125 | url: 'views/opt_out.html',
126 | dataType: 'html',
127 | success: function(data) {
128 | var template = data;
129 | $('#resume').html(data);
130 | }
131 | });
132 | return;
133 | }
134 |
135 | var res = github_user(username, function(data) {
136 | data = data.data;
137 | var sinceDate = new Date(data.created_at);
138 | var sinceMonth = sinceDate.getMonth();
139 | var since = sinceDate.getFullYear();
140 | var sinceMonth = sinceDate.getMonth();
141 | var currentYear = (new Date).getFullYear();
142 | switch (since) {
143 | case currentYear-1:
144 | since = 'last year';
145 | break;
146 | case currentYear:
147 | since = 'this year';
148 | break;
149 | }
150 |
151 | var addHttp = '';
152 | if (data.blog && data.blog.indexOf('http') < 0) {
153 | addHttp = 'http://';
154 | }
155 |
156 | // set view.name to the "friendly" name e.g. "John Doe". If not defined
157 | // (in which case data.name is empty), fall back to the login
158 | // name e.g. "johndoe"
159 | var name = username;
160 | if (data.name !== null && data.name !== undefined && data.name.length) {
161 | name = data.name;
162 | }
163 |
164 | var avatar = '';
165 | if (data.type == 'Organization'){
166 | avatar = data.avatar_url.match(/https:\/\/secure.gravatar.com\/avatar\/[0-9a-z]+/)[0];
167 | avatar += '?s=140&d=https://github.com/images/gravatars/gravatar-140.png';
168 | }
169 |
170 | var view = {
171 | name: name,
172 | type: data.type,
173 | email: data.email,
174 | created_at: data.created_at,
175 | earlyAdopter: 0,
176 | location: data.location,
177 | gravatar_id: data.gravatar_id,
178 | avatar_url: avatar,
179 | repos: data.public_repos,
180 | reposLabel: data.public_repos > 1 ? 'repositories' : 'repository',
181 | followers: data.followers,
182 | followersLabel: data.followers > 1 ? 'followers' : 'follower',
183 | username: username,
184 | userStatus: 'GitHub user',
185 | since: since,
186 | resume_url: window.location
187 | };
188 |
189 | // We consider a limit of 4 months since the GitHub opening (Feb 2008) to be considered as an early adopter
190 | if ((since == '2008' && sinceMonth <= 5) || since <= '2007') {
191 | view.earlyAdopter = 1;
192 | }
193 |
194 | view.userStatus = getUserStatus();
195 | function getUserStatus() {
196 | var COEF_REPOS = 2;
197 | var COEF_GISTS = 0.25;
198 | var COEF_FOLLOWERS = 0.5;
199 | var COEF_FOLLOWING = 0.25;
200 | var FIRST_STEP = 0;
201 | var SECOND_STEP = 5;
202 | var THIRD_STEP = 20;
203 | var FOURTH_STEP = 50;
204 | var FIFTH_STEP = 150;
205 | var EXTRA_POINT_GAIN = 1;
206 |
207 | var statusScore = data.public_repos * COEF_REPOS
208 | + data.public_gists * COEF_GISTS
209 | + data.followers * COEF_FOLLOWERS
210 | + data.following * COEF_FOLLOWING;
211 |
212 | // Extra points
213 | // - Early adopter
214 | if (view.earlyAdopter == 1) {
215 | statusScore += EXTRA_POINT_GAIN;
216 | }
217 | // - Blog & Email & Location
218 | if (view.location && view.location != '' && view.email && view.email != '' && data.blog && data.blog != '') {
219 | statusScore += EXTRA_POINT_GAIN;
220 | }
221 |
222 | if (statusScore == FIRST_STEP) {
223 | return 'Inactive GitHub user';
224 | }
225 | else if (statusScore > FIRST_STEP && statusScore <= SECOND_STEP) {
226 | return 'Newbie GitHub user';
227 | }
228 | else if (statusScore > SECOND_STEP && statusScore <= THIRD_STEP) {
229 | return 'Regular GitHub user';
230 | }
231 | else if (statusScore > THIRD_STEP && statusScore <= FOURTH_STEP) {
232 | return 'Advanced GitHub user';
233 | }
234 | else if (statusScore > FOURTH_STEP && statusScore <= FIFTH_STEP) {
235 | return 'Enthusiastic GitHub user';
236 | }
237 | else if (statusScore > FIFTH_STEP) {
238 | return 'Passionate GitHub user';
239 | }
240 | };
241 |
242 | if (data.blog !== undefined && data.blog !== null && data.blog !== '') {
243 | view.website = addHttp + data.blog;
244 | }
245 |
246 | var resume = (data.type == 'User' ? 'views/resume.html' : 'views/resumeOrgs.html');
247 | $.ajax({
248 | url: resume,
249 | dataType: 'html',
250 | success: function(data) {
251 | var template = data,
252 | html = Mustache.to_html(template, view);
253 | $('#resume').html(html);
254 | document.title = name + "'s Résumé";
255 | $("#actions #print").click(function(){
256 | window.print();
257 | return false;
258 | });
259 | }
260 | });
261 | });
262 |
263 | github_user_repos(username, function(data) {
264 | var sorted = [],
265 | languages = {},
266 | popularity;
267 |
268 | $.each(data, function(i, repo) {
269 | if (repo.fork !== false) {
270 | return;
271 | }
272 |
273 | if (repo.language) {
274 | if (repo.language in languages) {
275 | languages[repo.language]++;
276 | } else {
277 | languages[repo.language] = 1;
278 | }
279 | }
280 |
281 | popularity = repo.watchers + repo.forks;
282 | sorted.push({position: i, popularity: popularity, info: repo});
283 | });
284 |
285 | function sortByPopularity(a, b) {
286 | return b.popularity - a.popularity;
287 | };
288 |
289 | sorted.sort(sortByPopularity);
290 |
291 | var languageTotal = 0;
292 | function sortLanguages(languages, limit) {
293 | var sorted_languages = [];
294 |
295 | for (var lang in languages) {
296 | if (typeof(lang) !== "string") {
297 | continue;
298 | }
299 | sorted_languages.push({
300 | name: lang,
301 | popularity: languages[lang],
302 | toString: function() {
303 | return '
' + this.name + '';
304 | }
305 | });
306 |
307 | languageTotal += languages[lang];
308 | }
309 |
310 | if (limit) {
311 | sorted_languages = sorted_languages.slice(0, limit);
312 | }
313 |
314 | return sorted_languages.sort(sortByPopularity);
315 | }
316 |
317 | $.ajax({
318 | url: 'views/job.html',
319 | dataType: 'html',
320 | success: function(response) {
321 | languages = sortLanguages(languages, maxLanguages);
322 |
323 | if (languages && languages.length > 0) {
324 | var ul = $('
'),
325 | percent, li;
326 |
327 | $.each(languages, function(i, lang) {
328 | x = i + 1;
329 | percent = parseInt((lang.popularity / languageTotal) * 100);
330 | li = $('
' + lang.toString() + ' ('+percent+'%)');
331 |
332 | if (x % 3 == 0 || (languages.length < 3 && i == languages.length - 1)) {
333 | li.attr('class', 'last');
334 | ul.append(li);
335 | $('#content-languages').append(ul);
336 | ul = $('
');
337 | } else {
338 | ul.append(li);
339 | $('#content-languages').append(ul);
340 | }
341 | });
342 | } else {
343 | $('#mylanguages').hide();
344 | }
345 |
346 | if (sorted.length > 0) {
347 | $('#jobs').html('');
348 | itemCount = 0;
349 | var since, until, date, view, template, html;
350 |
351 | $.each(sorted, function(index, repo) {
352 | if (itemCount >= maxItems) {
353 | return;
354 | }
355 |
356 | since = new Date(repo.info.created_at);
357 | since = since.getFullYear();
358 | until = new Date(repo.info.pushed_at);
359 | until = until.getFullYear();
360 | if (since == until) {
361 | date = since;
362 | } else {
363 | date = since + ' - ' + until;
364 | }
365 |
366 | view = {
367 | name: repo.info.name,
368 | date: date,
369 | language: repo.info.language,
370 | description: repo.info.description,
371 | username: username,
372 | watchers: repo.info.watchers,
373 | forks: repo.info.forks,
374 | watchersLabel: repo.info.watchers == 0 || repo.info.watchers > 1 ? 'watchers' : 'watcher',
375 | forksLabel: repo.info.forks == 0 || repo.info.forks > 1 ? 'forks' : 'fork',
376 | };
377 |
378 | if (itemCount == sorted.length - 1 || itemCount == maxItems - 1) {
379 | view.last = 'last';
380 | }
381 |
382 | template = response;
383 | html = Mustache.to_html(template, view);
384 |
385 | $('#jobs').append($(html));
386 | ++itemCount;
387 | });
388 | } else {
389 | if(data.length > 0){
390 | $('#jobs').html('').append('
All of this user\'s repositories seem to be forks. Sorry.
');
391 | } else {
392 | $('#jobs').html('').append('
Unfortunately, this user does not seem to have any public repositories.
');
393 | }
394 | }
395 | }
396 | });
397 | });
398 |
399 | github_user_orgs(username, function(response) {
400 | var sorted = [];
401 |
402 | $.each(response.data, function(i, org) {
403 | if (org.login === undefined) {
404 | return;
405 | }
406 | sorted.push({position: i, info: org});
407 | });
408 |
409 | $.ajax({
410 | url: 'views/org.html',
411 | dataType: 'html',
412 | success: function(response) {
413 | var now = new Date().getFullYear();
414 |
415 | if (sorted.length > 0) {
416 | $('#orgs').html('');
417 |
418 | var name, view, template, html;
419 |
420 | $.each(sorted, function(index, org) {
421 | if (itemCount >= maxItems) {
422 | return;
423 | }
424 | name = (org.info.name || org.info.login);
425 | view = {
426 | name: name,
427 | now: now
428 | };
429 |
430 | if (itemCount == sorted.length - 1 || itemCount == maxItems) {
431 | view.last = 'last';
432 | }
433 | template = response;
434 | html = Mustache.to_html(template, view);
435 |
436 | $('#orgs').append($(html));
437 | ++itemCount;
438 | });
439 | } else {
440 | $('#organizations').remove();
441 | }
442 | }
443 | });
444 | });
445 |
446 | };
447 |
448 | if (trackerId) {
449 | var _gaq = _gaq || [];
450 | _gaq.push(['_setAccount', trackerId]);
451 | _gaq.push(['_trackPageview']);
452 |
453 | (function() {
454 | var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
455 | ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
456 | var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
457 | })();
458 | }
459 |
460 | $(window).bind('error', error);
461 |
--------------------------------------------------------------------------------