The initial github resumes repository

This commit is contained in:
David Coallier 2011-02-06 14:36:30 +00:00
commit 47aba3b380
13 changed files with 1392 additions and 0 deletions

119
css/resume.css Normal file
View File

@ -0,0 +1,119 @@
/*
---------------------------------------------------------------------------------
STRIPPED DOWN RESUME TEMPLATE
html resume
v0.9: 5/28/09
design and code by: thingsthatarebrown.com
(matt brown)
---------------------------------------------------------------------------------
*/
.msg { padding: 10px; background: #222; position: relative; }
.msg h1 { color: #fff; }
.msg a { margin-left: 20px; background: #408814; color: white; padding: 4px 8px; text-decoration: none; }
.msg a:hover { background: #266400; }
/* //-- yui-grids style overrides -- */
body { font-family: Georgia; color: #444; }
#inner { padding: 10px 80px; margin: 80px auto; background: #f5f5f5; border: solid #666; border-width: 8px 0 2px 0; }
.yui-gf { margin-bottom: 2em; padding-bottom: 2em; border-bottom: 1px solid #ccc; }
/* //-- header, body, footer -- */
#hd { margin: 2.5em 0 3em 0; padding-bottom: 1.5em; border-bottom: 1px solid #ccc }
#hd h2 { text-transform: uppercase; letter-spacing: 2px; }
#bd, #ft { margin-bottom: 2em; }
/* //-- footer -- */
#ft { padding: 1em 0 5em 0; font-size: 92%; border-top: 1px solid #ccc; text-align: center; }
#ft p { margin-bottom: 0; text-align: center; }
/* //-- core typography and style -- */
#hd h1 { font-size: 48px; text-transform: uppercase; letter-spacing: 3px; }
h2 { font-size: 152% }
h3, h4 { font-size: 122%; }
h1, h2, h3, h4 { color: #333; }
p { font-size: 100%; line-height: 18px; padding-right: 3em; }
a { color: #990003 }
a:hover { text-decoration: none; }
strong { font-weight: bold; }
li { line-height: 24px; border-bottom: 1px solid #ccc; }
p.enlarge { font-size: 144%; padding-right: 6.5em; line-height: 24px; }
p.enlarge span { color: #000 }
.contact-info { margin-top: 7px; }
.first h2 { font-style: italic; }
.last { border-bottom: 0 }
/* //-- section styles -- */
a#pdf { display: block; float: left; background: #666; color: white; padding: 6px 50px 6px 12px; margin-bottom: 6px; text-decoration: none; }
a#pdf:hover { background: #222; }
.job { position: relative; margin-bottom: 1em; padding-bottom: 1em; border-bottom: 1px solid #ccc; }
.job h4 { position: absolute; top: 0.35em; right: 0 }
.job p { margin: 0.75em 0 3em 0; }
.last { border: none; }
.skills-list { }
.skills-list ul { margin: 0; }
.skills-list li { margin: 3px 0; padding: 3px 0; }
.skills-list li span { font-size: 152%; display: block; margin-bottom: -2px; padding: 0 }
.talent { width: 32%; float: left }
.talent h2 { margin-bottom: 6px; }
#srt-ttab { margin-bottom: 100px; text-align: center; }
#srt-ttab img.last { margin-top: 20px }
/* --// override to force 1/8th width grids -- */
.yui-gf .yui-u{width:80.2%;}
.yui-gf div.first{width:12.3%;}
input#username {
position: relative;
float: left;
height: 30px;
width: 75%;
border: 2px solid #444;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
font-family: Georgia; color: #444;
font-size: 18px;
padding: 5px;
}
button#gen {
position: relative;
float: left;
margin-left: 10px;
width: 20%;
height: 44px;
font-family: Georgia; color: #444;
font-size: 18px;
color: white;
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))
);
background-image: -moz-linear-gradient(
center bottom,
rgb(31,31,31) 21%,
rgb(51,51,51) 61%
);
border: 1px solid #444;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
button#gen:hover {
cursor: pointer;
}

BIN
images/srt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
images/ttab.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

23
index.html Normal file
View File

@ -0,0 +1,23 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Github Resume</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="keywords" content="cv, resume, hResume, github, community, free, fun, nice, useful" />
<meta name="description" content="Github resumes, generated by the community, for the community." />
<link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/2.7.0/build/reset-fonts-grids/reset-fonts-grids.css" media="all" />
<link rel="stylesheet" type="text/css" href="css/resume.css" media="all" />
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script>
<script type="text/javascript" src="js/mustache.js"></script>
<script type="text/javascript" src="js/github.js"></script>
<script type="text/javascript" src="js/githubresume.js"></script>
</head>
<body id="resume">
</body>
</html>

667
js/github.js Normal file
View File

@ -0,0 +1,667 @@
// ## Client-side Javascript API wrapper for GitHub
//
// Tries to map one-to-one with the GitHub API V2, but in a Javascripty manner.
(function (globals) {
// Before we implement the API methods, we will define all of our private
// variables and helper functions with one `var` statement.
var
// The username and authentication token of the library's user.
authUsername,
authToken,
// To save keystrokes when we make JSONP calls to the HTTP API, we will keep
// track of the root from which all V2 urls extend.
apiRoot = "https://github.com/api/v2/json/",
// Send a JSONP request to the Github API that calls `callback` with
// the `context` argument as `this`.
//
// The `url` parameter is concatenated with the apiRoot, for the reasons
// mentioned above. The way that we are supporting non-global, anonymous
// functions is by sticking them in the globally exposed
// `gh.__jsonp_callbacks` object with a "unique" `id` that is the current
// time in milliseconds. Once the callback is called, it is deleted from the
// object to prevent memory leaks.
jsonp = function (url, callback, context) {
var id = +new Date,
script = document.createElement("script");
while (gh.__jsonp_callbacks[id] !== undefined)
id += Math.random(); // Avoid slight possibility of id clashes.
gh.__jsonp_callbacks[id] = function () {
delete gh.__jsonp_callbacks[id];
callback.apply(context, arguments);
};
var prefix = "?";
if (url.indexOf("?") >= 0)
prefix = "&";
url += prefix + "callback=" + encodeURIComponent("gh.__jsonp_callbacks[" + id + "]");
if (authUsername && authToken) {
url += "&login=" + authUsername + "&authToken=" + authToken;
}
script.setAttribute("src", apiRoot + url);
document.getElementsByTagName('head')[0].appendChild(script);
},
// Send an HTTP POST. Unfortunately, it isn't possible to support a callback
// with the resulting data. (Please prove me wrong if you can!)
//
// This is implemented with a hack to get around the cross-domain
// restrictions on ajax calls. Basically, a form is created that will POST
// to the GitHub API URL, stuck inside an iframe so that it won't redirect
// this page, and then submitted.
post = function (url, vals) {
var
form = document.createElement("form"),
iframe = document.createElement("iframe"),
doc = iframe.contentDocument !== undefined ?
iframe.contentDocument :
iframe.contentWindow.document,
key, field;
vals = vals || {};
form.setAttribute("method", "post");
form.setAttribute("action", apiRoot + url);
for (key in vals) {
if (vals.hasOwnProperty(key)) {
field = document.createElement("input");
field.type = "hidden";
field.value = encodeURIComponent(vals[key]);
form.appendChild(field);
}
}
iframe.setAttribute("style", "display: none;");
doc.body.appendChild(form);
document.body.appendChild(iframe);
form.submit();
},
// This helper function will throw a TypeError if the library user is not
// properly authenticated. Otherwise, it silently returns.
authRequired = function (username) {
if (!authUsername || !authToken || authUsername !== username) {
throw new TypeError("gh: Must be authenticated to do that.");
}
},
// Convert an object to a url parameter string.
//
// paramify({foo:1, bar:3}) -> "foo=1&bar=3".
paramify = function (params) {
var str = "", key;
for (key in params) if (params.hasOwnProperty(key))
str += key + "=" + params[key] + "&";
return str.replace(/&$/, "");
},
// Get around how the GH team haven't migrated all the API to version 2, and
// how gists use a different api root.
withTempApiRoot = function (tempApiRoot, fn) {
return function () {
var oldRoot = apiRoot;
apiRoot = tempApiRoot;
fn.apply(this, arguments);
apiRoot = oldRoot;
};
},
// Expose the global `gh` variable, through which every API method is
// accessed, but keep a local variable around so we can reference it easily.
gh = globals.gh = {};
// Psuedo private home for JSONP callbacks (which are required to be global
// by the nature of JSONP, as discussed earlier).
gh.__jsonp_callbacks = {};
// Authenticate as a user. Does not try to validate at any point; that job
// is up to each individual method, which calls `authRequired` as needed.
gh.authenticate = function (username, token) {
authUsername = username;
authToken = token;
return this;
};
// ### Users
// The constructor for user objects. Just creating an instance of a user
// doesn't fetch any data from GitHub, you need to get explicit about what
// you want to do that.
//
// var huddlej = gh.user("huddlej");
gh.user = function (username) {
if ( !(this instanceof gh.user)) {
return new gh.user(username);
}
this.username = username;
};
// Show basic user info; you can get more info if you are authenticated as
// this user.
//
// gh.user("fitzgen").show(function (data) {
// console.log(data.user);
// });
gh.user.prototype.show = function (callback, context) {
jsonp("user/show/" + this.username, callback, context);
return this;
};
// Update a user's info. You must be authenticated as this user for this to
// succeed.
//
// TODO: example
gh.user.prototype.update = function (params) {
authRequired(this.username);
var key, postData = {
login: authUsername,
token: authToken
};
for (key in params) {
if (params.hasOwnProperty(key)) {
postData["values["+key+"]"] = encodeURIComponent(params[key]);
}
}
post("user/show/" + this.username, postData);
return this;
};
// Get a list of who this user is following.
//
// TODO: example
gh.user.prototype.following = function (callback, context) {
jsonp("user/show/" + this.username + "/following", callback, context);
};
// Find out what other users are following this user.
//
// TODO: example
gh.user.prototype.followers = function (callback, context) {
jsonp("user/show/" + this.username + "/followers", callback, context);
};
// Make this user follow some other user. You must be authenticated as this
// user for this to succeed.
//
// TODO: example
gh.user.prototype.follow = function (user) {
authRequired.call(this);
post("user/follow/" + user);
return this;
};
// Make this user quit following the given `user`. You must be authenticated
// as this user to succeed.
//
// TODO: example
gh.user.prototype.unfollow = function (user) {
authRequired.call(this);
post("user/unfollow/" + user);
return this;
};
// Get a list of repositories that this user is watching.
//
// TODO: example
gh.user.prototype.watching = function (callback, context) {
jsonp("repos/watched/" + this.username, callback, context);
return this;
};
// Get a list of this user's repositories.
//
// gh.user("fitzgen").repos(function (data) {
// alert(data.repositories.length);
// });
gh.user.prototype.repos = function (callback, context) {
gh.repo.forUser(this.username, callback, context);
return this;
};
// Make this user fork the repo that lives at
// http://github.com/user/repo. You must be authenticated as this user for
// this to succeed.
//
// gh.user("fitzgen").forkRepo("brianleroux", "wtfjs");
gh.user.prototype.forkRepo = function (user, repo) {
authRequired(this.username);
post("repos/fork/" + user + "/" + repo);
return this;
};
// Get a list of all repos that this user can push to (including ones that
// they are just a collaborator on, and do not own). Must be authenticated
// as this user.
gh.user.prototype.pushable = function (callback, context) {
authRequired(authUsername);
jsonp("repos/pushable", callback, context);
};
gh.user.prototype.publicGists = withTempApiRoot(
"http://gist.github.com/api/v1/json/gists/",
function (callback, context) {
jsonp(this.username, callback, context);
return this;
}
);
// Search users for `query`.
gh.user.search = function (query, callback, context) {
jsonp("user/search/" + query, callback, context);
return this;
};
// ### Repositories
// This is the base constructor for creating repo objects. Note that this
// won't actually hit the GitHub API until you specify what data you want,
// or what action you wish to take via a prototype method.
gh.repo = function (user, repo) {
if ( !(this instanceof gh.repo)) {
return new gh.repo(user, repo);
}
this.repo = repo;
this.user = user;
};
// Get basic information on this repo.
//
// gh.repo("schacon", "grit").show(function (data) {
// console.log(data.repository.description);
// });
gh.repo.prototype.show = function (callback, context) {
jsonp("repos/show/" + this.user + "/" + this.repo, callback, context);
return this;
};
// Update the information for this repo. Must be authenticated as the
// repository owner. Params can include:
//
// * description
// * homepage
// * has_wiki
// * has_issues
// * has_downloads
gh.repo.prototype.update = function (params) {
authRequired(this.user);
var key, postData = {
login: authUsername,
token: authToken
};
for (key in params) {
if (params.hasOwnProperty(key)) {
postData["values["+key+"]"] = encodeURIComponent(params[key]);
}
}
post("repos/show/" + this.user + "/" + this.repo, postData);
return this;
};
// Get all tags for this repo.
gh.repo.prototype.tags = function (callback, context) {
jsonp("repos/show/" + this.user + "/" + this.repo + "/tags",
callback,
context);
return this;
};
// Get all branches in this repo.
gh.repo.prototype.branches = function (callback, context) {
jsonp("repos/show/" + this.user + "/" + this.repo + "/branches",
callback,
context);
return this;
};
// Gather line count information on the language(s) used in this repo.
gh.repo.prototype.languages = function (callback, context) {
jsonp("/repos/show/" + this.user + "/" + this.repo + "/languages",
callback,
context);
return this;
};
// Gather data on all the forks of this repo.
gh.repo.prototype.network = function (callback, context) {
jsonp("repos/show/" + this.user + "/" + this.repo + "/network",
callback,
context);
return this;
};
// All users who have contributed to this repo. Pass `true` to showAnon if you
// want to see the non-github contributors.
gh.repo.prototype.contributors = function (callback, context, showAnon) {
var url = "repos/show/" + this.user + "/" + this.repo + "/contributors";
if (showAnon)
url += "/anon";
jsonp(url,
callback,
context);
return this;
};
// Get all of the collaborators for this repo.
gh.repo.prototype.collaborators = function (callback, context) {
jsonp("repos/show/" + this.user + "/" + this.repo + "/collaborators",
callback,
context);
return this;
};
// Add a collaborator to this project. Must be authenticated.
gh.repo.prototype.addCollaborator = function (collaborator) {
authRequired(this.user);
post("repos/collaborators/" + this.repo + "/add/" + collaborator);
return this;
};
// Remove a collaborator from this project. Must be authenticated.
gh.repo.prototype.removeCollaborator = function (collaborator) {
authRequired(this.user);
post("repos/collaborators/" + this.repo + "/remove/" + collaborator);
return this;
};
// Make this repository private. Authentication required.
gh.repo.prototype.setPrivate = function () {
authRequired(this.user);
post("repo/set/private/" + this.repo);
return this;
};
// Make this repository public. Authentication required.
gh.repo.prototype.setPublic = function () {
authRequired(this.user);
post("repo/set/public/" + this.repo);
return this;
};
// Search for repositories. `opts` may include `start_page` or `language`,
// which must be capitalized.
gh.repo.search = function (query, opts, callback, context) {
var url = "repos/search/" + query.replace(" ", "+");
if (typeof opts === "function") {
opts = {};
callback = arguments[1];
context = arguments[2];
}
url += "?" + paramify(opts);
return this;
};
// Get all the repos that are owned by `user`.
gh.repo.forUser = function (user, callback, context) {
jsonp("repos/show/" + user, callback, context);
return this;
};
// Create a repository. Must be authenticated.
gh.repo.create = function (name, opts) {
authRequired(authUsername);
opts.name = name;
post("repos/create", opts);
return this;
};
// Delete a repository. Must be authenticated.
gh.repo.del = function (name) {
authRequired(authUsername);
post("repos/delete/" + name);
return this;
};
// ### Commits
gh.commit = function (user, repo, sha) {
if ( !(this instanceof gh.commit) )
return new gh.commit(user, repo, sha);
this.user = user;
this.repo = repo;
this.sha = sha;
};
gh.commit.prototype.show = function (callback, context) {
jsonp("commits/show/" + this.user + "/" + this.repo + "/" + this.sha,
callback,
context);
return this;
};
// Get a list of all commits on a repos branch.
gh.commit.forBranch = function (user, repo, branch, callback, context) {
jsonp("commits/list/" + user + "/" + repo + "/" + branch,
callback,
context);
return this;
};
// Get a list of all commits on this path (file or dir).
gh.commit.forPath = function (user, repo, branch, path, callback, context) {
jsonp("commits/list/" + user + "/" + repo + "/" + branch + "/" + path,
callback,
context);
return this;
};
// ### Issues
gh.issue = function (user, repo, number) {
if ( !(this instanceof gh.issue) )
return new gh.commit(user, repo, number);
this.user = user;
this.repo = repo;
this.number = number;
};
// View this issue's info.
gh.issue.prototype.show = function (callback, context) {
jsonp("issues/show/" + this.user + "/" + this.repo + "/" + this.number,
callback,
context);
return this;
};
// Get a list of all comments on this issue.
gh.issue.prototype.comments = function (callback, context) {
jsonp("issues/comments/" + this.user + "/" + this.repo + "/" + this.number,
callback,
context);
return this;
};
// Close this issue.
gh.issue.prototype.close = function () {
authRequired(this.user);
post("issues/close/" + this.user + "/" + this.repo + "/" + this.number);
return this;
};
// Reopen this issue.
gh.issue.prototype.reopen = function () {
authRequired(this.user);
post("issues/reopen/" + this.user + "/" + this.repo + "/" + this.number);
return this;
};
// Reopen this issue.
gh.issue.prototype.update = function (title, body) {
authRequired(this.user);
post("issues/edit/" + this.user + "/" + this.repo + "/" + this.number, {
title: title,
body: body
});
return this;
};
// Add `label` to this issue. If the label is not yet in the system, it will
// be created.
gh.issue.prototype.addLabel = function (label) {
post("issues/label/add/" + this.user + "/" + this.repo + "/" + label + "/" + this.number);
return this;
};
// Remove a label from this issue.
gh.issue.prototype.removeLabel = function (label) {
post("issues/label/remove/" + this.user + "/" + this.repo + "/" + label + "/" + this.number);
return this;
};
// Comment on this issue as the user that is authenticated.
gh.issue.prototype.comment = function (comment) {
authRequired(authUsername);
post("/issues/comment/" + user + "/" + repo + "/" + this.number, {
comment: comment
});
return this;
};
// Get all issues' labels for the repo.
gh.issue.labels = function (user, repo) {
jsonp("issues/labels/" + user + "/" + repo,
callback,
context);
return this;
};
// Open an issue. Must be authenticated.
gh.issue.open = function (repo, title, body) {
authRequired(authUsername);
post("issues/open/" + authUsername + "/" + repo, {
title: title,
body: body
});
return this;
};
// Search a repository's issue tracker. `state` can be "open" or "closed".
gh.issue.search = function (user, repo, state, query, callback, context) {
jsonp("/issues/search/" + user + "/" + repo + "/" + state + "/" + query,
callback,
context);
return this;
};
// Get a list of issues for the given repo. `state` can be "open" or
// "closed".
gh.issue.list = function (user, repo, state, callback, context) {
jsonp("issues/list/" + user + "/" + repo + "/" + state,
callback,
context);
return this;
};
// ### Gists
gh.gist = function (id) {
if ( !(this instanceof gh.gist) ) {
return new gh.gist(id);
}
this.id = id;
};
gh.gist.prototype.show = withTempApiRoot(
"http://gist.github.com/api/v1/json/",
function (callback, context) {
jsonp(this.id, callback, cont);
return this;
}
);
gh.gist.prototype.file = withTempApiRoot(
"http://gist.github.com/raw/v1/json/",
function (filename, callback, context) {
jsonp(this.id + "/" + filename, callback, cont);
return this;
}
);
// ### Objects
gh.object = function (user, repo) {
if (!(this instanceof gh.object)) {
return new gh.object(user, repo);
}
this.user = user;
this.repo = repo;
};
// Get the contents of a tree by tree SHA
gh.object.prototype.tree = function (sha, callback, context) {
jsonp("tree/show/" + this.user + "/" + this.repo + "/" + sha,
callback,
context);
return this;
};
// Get the data about a blob by tree SHA and path
gh.object.prototype.blob = function (path, sha, callback, context) {
jsonp("blob/show/" + this.user + "/" + this.repo + "/" + sha + "/" + path,
callback,
context);
return this;
};
// Get only blob meta
gh.object.prototype.blobMeta = function (path, sha, callback, context) {
jsonp("blob/show/" + this.user + "/" + this.repo + "/" + sha + "/" + path + "?meta=1",
callback,
context);
return this;
};
// Get list of blobs
gh.object.prototype.blobAll = function (branch, callback, context) {
jsonp("blob/all/" + this.user + "/" + this.repo + "/" + branch,
callback,
context);
return this;
};
// Get meta of each blob in tree
gh.object.prototype.blobFull = function (sha, callback, context) {
jsonp("blob/full/" + this.user + "/" + this.repo + "/" + sha,
callback,
context);
return this;
};
// ### Network
gh.network = function(user, repo) {
if (!(this instanceof gh.network)) {
return new gh.network(user, repo);
}
this.user = user;
this.repo = repo;
};
gh.network.prototype.data = withTempApiRoot(
"http://github.com/",
function (nethash, start, end, callback, context) {
jsonp(this.user + "/" + this.repo + "/network_data_chunk?"
+ nethash + "&" + start + "&" + end,
callback,
context);
return this;
}
);
gh.network.prototype.meta = withTempApiRoot(
"http://github.com/",
function (callback, context) {
jsonp(this.user + "/" + this.repo + "/network_meta",
callback,
context);
return this;
}
);
}(window));

140
js/githubresume.js Normal file
View File

@ -0,0 +1,140 @@
var urlParams = {};
(function () {
var e,
a = /\+/g, // Regex for replacing addition symbol with a space
r = /([^&=]+)=?([^&]*)/g,
d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
q = window.location.search.substring(1);
while (e = r.exec(q))
urlParams[0] = d(e[1]);
})();
var username;
$(document).ready(function() {
try {
if (urlParams[0] !== undefined) {
username = urlParams[0];
run();
} else {
home();
}
} catch (err) {
console.log(err);
}
});
var error = function() {
$.ajax({
url: 'views/error.html',
dataType: 'html',
success: function(data) {
var template = data;
$('#resume').html(data);
}
});
};
var home = function() {
$.ajax({
url: 'views/index.html',
dataType: 'html',
success: function(data) {
var template = data;
$('#resume').html(data);
}
});
};
var run = function() {
var gh_user = gh.user(username);
var itemCount = 0, maxItems = 5;
var res = gh_user.show(function(data) {
gh_user.repos(function(data) {
repos = data;
});
var since = new Date(data.user.created_at);
since = since.getFullYear();
var view = {
name: data.user.name,
email: data.user.email,
created_at: data.user.created_at,
blog: data.user.blog,
location: data.user.location,
repos: data.user.public_repo_count,
plural: data.user.public_repo_count > 1 ? 'repositories' : 'repository',
username: username,
since: since
};
$.ajax({
url: 'views/resume.html',
dataType: 'html',
success: function(data) {
var template = data;
var html = Mustache.to_html(template, view);
$('#resume').html(html);
}
});
});
gh_user.repos(function(data) {
var repos = data.repositories;
var sorted = [];
var languages = [];
repos.forEach(function(elm, i, arr) {
if (arr[i].fork !== false) {
return;
}
var popularity = arr[i].watchers + arr[i].forks;
sorted.push({position: i, popularity: popularity, info: arr[i]});
});
function sortByPopularity(a, b) {
return b.popularity - a.popularity;
};
sorted.sort(sortByPopularity);
$.ajax({
url: 'views/job.html',
dataType: 'html',
success: function(response) {
var now = new Date().getFullYear();
sorted.forEach(function(elm, index, arr) {
if (itemCount >= maxItems) {
return;
}
var since = new Date(arr[index].info.created_at);
since = since.getFullYear();
var view = {
name: arr[index].info.name,
since: since,
now: now,
description: arr[index].info.description,
username: username,
watchers: arr[index].info.watchers,
forks: arr[index].info.forks
};
var template = response;
var html = Mustache.to_html(template, view);
$('#jobs').append($(html));
++itemCount;
});
}
});
});
};
$(window).bind('error', error);

298
js/mustache.js Normal file
View File

@ -0,0 +1,298 @@
/*
Shameless port of http://github.com/defunkt/mustache
by Jan Lehnardt <jan@apache.org>,
Alexander Lang <alex@upstream-berlin.com>,
Sebastian Cohnen <sebastian.cohnen@googlemail.com>
Thanks @defunkt for the awesome code.
See http://github.com/defunkt/mustache for more info.
*/
var Mustache = function() {
var Renderer = function() {};
Renderer.prototype = {
otag: "{{",
ctag: "}}",
pragmas: {},
buffer: [],
pragmas_parsed: false,
render: function(template, context, partials, in_recursion) {
// fail fast
if(template.indexOf(this.otag) == -1) {
if(in_recursion) {
return template;
} else {
this.send(template);
return;
}
}
if(!in_recursion) {
this.buffer = [];
}
if(!this.pragmas_parsed) {
template = this.render_pragmas(template);
}
var html = this.render_section(template, context, partials);
if(in_recursion) {
return this.render_tags(html, context, partials, in_recursion);
}
this.render_tags(html, context, partials, in_recursion);
},
/*
Sends parsed lines
*/
send: function(line) {
if(line != "") {
this.buffer.push(line);
}
},
/*
Looks for %PRAGMAS
*/
render_pragmas: function(template) {
this.pragmas_parsed = true;
// no pragmas
if(template.indexOf(this.otag + "%") == -1) {
return template;
}
var that = this;
var regex = new RegExp(this.otag + "%([\\w_-]+) ?([\\w]+=[\\w]+)?"
+ this.ctag);
return template.replace(regex, function(match, pragma, options) {
that.pragmas[pragma] = {};
if(options) {
var opts = options.split("=");
that.pragmas[pragma][opts[0]] = opts[1];
}
return "";
// ignore unknown pragmas silently
});
},
/*
Tries to find a partial in the global scope and render it
*/
render_partial: function(name, context, partials) {
if(typeof(context[name]) != "object") {
throw({message: "subcontext for '" + name + "' is not an object"});
}
if(!partials || !partials[name]) {
throw({message: "unknown_partial '" + name + "'"});
}
return this.render(partials[name], context[name], partials, true);
},
/*
Renders boolean and enumerable sections
*/
render_section: function(template, context, partials) {
if(template.indexOf(this.otag + "#") == -1) {
return template;
}
var that = this;
// CSW - Added "+?" so it finds the tighest bound, not the widest
var regex = new RegExp(this.otag + "\\#(.+)" + this.ctag +
"\\s*([\\s\\S]+?)" + this.otag + "\\/\\1" + this.ctag + "\\s*", "mg");
// for each {{#foo}}{{/foo}} section do...
return template.replace(regex, function(match, name, content) {
var value = that.find(name, context);
if(that.is_array(value)) { // Enumerable, Let's loop!
return that.map(value, function(row) {
return that.render(content, that.merge(context,
that.create_context(row)), partials, true);
}).join("");
} else if(value) { // boolean section
return that.render(content, context, partials, true);
} else {
return "";
}
});
},
/*
Replace {{foo}} and friends with values from our view
*/
render_tags: function(template, context, partials, in_recursion) {
// tit for tat
var that = this;
var new_regex = function() {
return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\/#]+?)\\1?" +
that.ctag + "+", "g");
};
var regex = new_regex();
var lines = template.split("\n");
for (var i=0; i < lines.length; i++) {
lines[i] = lines[i].replace(regex, function(match, operator, name) {
switch(operator) {
case "!": // ignore comments
return match;
case "=": // set new delimiters, rebuild the replace regexp
that.set_delimiters(name);
regex = new_regex();
return "";
case ">": // render partial
return that.render_partial(name, context, partials);
case "{": // the triple mustache is unescaped
return that.find(name, context);
default: // escape the value
return that.escape(that.find(name, context));
}
}, this);
if(!in_recursion) {
this.send(lines[i]);
}
}
if(in_recursion) {
return lines.join("\n");
}
},
set_delimiters: function(delimiters) {
var dels = delimiters.split(" ");
this.otag = this.escape_regex(dels[0]);
this.ctag = this.escape_regex(dels[1]);
},
escape_regex: function(text) {
// thank you Simon Willison
if(!arguments.callee.sRE) {
var specials = [
'/', '.', '*', '+', '?', '|',
'(', ')', '[', ']', '{', '}', '\\'
];
arguments.callee.sRE = new RegExp(
'(\\' + specials.join('|\\') + ')', 'g'
);
}
return text.replace(arguments.callee.sRE, '\\$1');
},
/*
find `name` in current `context`. That is find me a value
from the view object
*/
find: function(name, context) {
name = this.trim(name);
if(typeof context[name] === "function") {
return context[name].apply(context);
}
if(context[name] !== undefined) {
return context[name];
}
// silently ignore unkown variables
return "";
},
// Utility methods
/*
Does away with nasty characters
*/
escape: function(s) {
return ((s == null) ? "" : s).toString().replace(/[&"<>\\]/g, function(s) {
switch(s) {
case "&": return "&amp;";
case "\\": return "\\\\";;
case '"': return '\"';;
case "<": return "&lt;";
case ">": return "&gt;";
default: return s;
}
});
},
/*
Merges all properties of object `b` into object `a`.
`b.property` overwrites a.property`
*/
merge: function(a, b) {
var _new = {};
for(var name in a) {
if(a.hasOwnProperty(name)) {
_new[name] = a[name];
}
};
for(var name in b) {
if(b.hasOwnProperty(name)) {
_new[name] = b[name];
}
};
return _new;
},
// by @langalex, support for arrays of strings
create_context: function(_context) {
if(this.is_object(_context)) {
return _context;
} else if(this.pragmas["IMPLICIT-ITERATOR"]) {
var iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator || ".";
var ctx = {};
ctx[iterator] = _context
return ctx;
}
},
is_object: function(a) {
return a && typeof a == "object";
},
is_array: function(a) {
return Object.prototype.toString.call(a) === '[object Array]';
},
/*
Gets rid of leading and trailing whitespace
*/
trim: function(s) {
return s.replace(/^\s*|\s*$/g, "");
},
/*
Why, why, why? Because IE. Cry, cry cry.
*/
map: function(array, fn) {
if (typeof array.map == "function") {
return array.map(fn)
} else {
var r = [];
var l = array.length;
for(i=0;i<l;i++) {
r.push(fn(array[i]));
}
return r;
}
}
};
return({
name: "mustache.js",
version: "0.2.3-dev",
/*
Turns a template and view into HTML
*/
to_html: function(template, view, partials, send_fun) {
var renderer = new Renderer();
if(send_fun) {
renderer.send = send_fun;
}
renderer.render(template, view, partials);
if(!send_fun) {
return renderer.buffer.join("\n");
}
}
});
}();

12
views/error.html Normal file
View File

@ -0,0 +1,12 @@
<div id="doc" class="yui-t7">
<div id="hd" role="banner"><h1>Uh oh :(</h1></div>
<div id="bd" role="main">
<div class="yui-g">
<p>
We couldn't find enough to build a resume from. Make sure this is the good username :-). <a href="/" title="Home">Try again with another username?</a>
</p>
</div>
</div>
<div id="ft" role="contentinfo"><p>Brought to your by the brilliant mind of <a href="http://twitter.com/davidcoallier">@davidcoallier</a> &lt;/modesty&gt;</p></div>
</div>

42
views/index.html Normal file
View File

@ -0,0 +1,42 @@
<div id="doc" class="yui-t7">
<div id="hd" role="banner"><h1>My Github Resume</h1></div>
<div id="bd" role="main">
<div class="yui-g">
<p>
As a software startup owner I really enjoy when people send us their
resumes and they include their github account so we can see tangible work they have done.
</p>
<p><br /></p>
<p>
After a <a href="https://twitter.com/#!/jeresig/status/33968704983138304">tweet by John Resig</a>
I imagined that it may be nice for people to be able to generate their <a href="https://github.com" title="Github">Github</a> resumes.
</p>
<p><br /></p>
<p>
<input id="username" type="text" value="Enter your github username and click on generate" />
<button type="button" id="gen">Generate</button>
</p>
</div>
</div>
<div id="ft" role="contentinfo"><p>Brought to your by the brilliant mind of <a href="http://twitter.com/davidcoallier">@davidcoallier</a> &lt;/modesty&gt;</p></div>
</div>
<script type="text/javascript">
$(document).ready(function() {
$('input#username').live('click', function() {
$(this).val('');
});
$(document).keyup(function(e) {
if (e.keyCode == 13) {
var genUser = $('#username').val();
document.location.href = "/?" + genUser;
}
});
$('#gen').live('click', function() {
var genUser = $('#username').val();
document.location.href = "/?" + genUser;
});
});
</script>

9
views/job.html Normal file
View File

@ -0,0 +1,9 @@
<div class="job">
<h2>{{name}}</h2>
<h3>Creator & Owner</h3>
<h4>{{since}} - {{now}}</h4>
<p>{{description}}.</p>
<p>This repository has {{watchers}} watcher(s) and {{forks}} fork(s). If you would like more information about this repository and my contributed code, please visit <a href="https://github.com/{{username}}/{{name}}">the repo</a> on Github.</p>
</div>

4
views/repo.html Normal file
View File

@ -0,0 +1,4 @@
{{#repo}}
<h1>{{repo}}</h1>
<p>{{description}}</p>
{{/repo}}

76
views/resume.html Normal file
View File

@ -0,0 +1,76 @@
<div id="doc2" class="yui-t7">
<div id="inner">
<div id="hd">
<div class="yui-gc">
<div class="yui-u first">
<h1>{{name}}</h1>
<h2>Passionate github user</h2>
</div>
<div class="yui-u">
<div class="contact-info">
<h3><a href="mailto:{{email}}">{{email}}</a></h3>
</div><!--// .contact-info -->
</div>
</div><!--// .yui-gc -->
</div><!--// hd -->
<div id="bd">
<div id="yui-main">
<div class="yui-b">
<div class="yui-gf">
<div class="yui-u first">
<h2>Github Profile</h2>
</div>
<div class="yui-u">
<p class="enlarge">
I'm a developer
{{#location}}
based in {{location}}
{{/location}}
with {{repos}} public {{plural}}. I've been using github.com since {{since}}
{{#blog}}
and sometimes I blog at <a href="{{blog}}" title="my blog">{{blog}}</a>
{{/blog}}.
</p>
</div>
</div><!--// .yui-gf -->
<div class="yui-gf">
<div class="yui-u first">
<h2>My Popular Repositories</h2>
</div><!--// .yui-u -->
<div class="yui-u" id="jobs">
</div><!--// .yui-u -->
</div><!--// .yui-gf -->
<div class="yui-gf last">
<div class="yui-u first">
<h2>About This Resume</h2>
</div>
<div class="yui-u">
<p class="enlarge">
This resume is generated automatically using information from my github account. The repositories are
ordered by popularity based on a very simple popularity heuristic that defines the popularity of a repository
by it's sum of watchers and forks. Do not hesitate to visit <a href="https://github.com/{{{username}}}" title="My Github page">my github page</a>
for more information about my repositories and work.
</p>
</div>
</div><!--// .yui-gf -->
</div><!--// .yui-b -->
</div><!--// yui-main -->
</div><!--// bd -->
<div id="ft">
<p>{{name}} &mdash; <a href="mailto:{{email}}">{{email}}</a> &mdash; <a href="https://github.com/{{username}}" title="Github profile">https://github.com/{{username}}</a></p>
</div><!--// footer -->
</div><!-- // inner -->
</div><!--// doc -->

2
views/user.html Normal file
View File

@ -0,0 +1,2 @@
<h1>{{full_name}}</h1>
<p>Information about this user</p>