diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..b093f97 --- /dev/null +++ b/README.markdown @@ -0,0 +1,6 @@ +####A service that creates your resume based on your GitHub repos. + +Possible Usecases: + + * Boon for all the tech-savy bosses who want to have a **quick view** of person's git/github activity, _before the interview_ + diff --git a/config.ru b/config.ru new file mode 100644 index 0000000..6ef85f9 --- /dev/null +++ b/config.ru @@ -0,0 +1,2 @@ +use Rack::Static, :urls => ["/css", "/images", "/js", "/views"], :root => "." +run lambda { |env| [200, { 'Content-Type' => 'text/html' }, File.open('index.html', File::RDONLY)] } diff --git a/css/print.css b/css/print.css new file mode 100644 index 0000000..3745ce6 --- /dev/null +++ b/css/print.css @@ -0,0 +1,124 @@ +/* + Print stylesheet for résumés +*/ + + +/* =! Template */ + +#doc2 { + width: 100%; +} +#inner { + margin: 0 auto; + padding: 10pt; + border: 0; +} +#hd { + margin-top: 0; +} + +.enlarge { + padding-right: 0; +} + +.talent li { + border: 0; +} + +.org p, +.job p { + margin: 5pt 0 15pt; +} + +#repositories { + padding-bottom: 0; +} + +#jobs h4 { + margin-top: 10pt; +} + +#jobs .job, +#about { + page-break-inside: avoid; +} + +#bd, +#about { + margin-bottom: 0; +} + +#ft { + padding: 5pt 0; +} + +/* --------- */ + + + +/* =! Font */ + +body { + font-size: 10pt; +} + +#hd h1 { + font-size: 28pt; +} +#hd h2 { + font-size: 14pt; +} + +#bd h2, +#profile .enlarge, +#about .enlarge { + font-size: 12pt; +} + +/* --------- */ + + + +/* =! Colors */ + +body, +h1, h2, h3, h4 { + color: #000; +} + +#inner { + background: #fff; +} + +/* --------- */ + + + +/* =! Links */ + +#profile a:after, +#repositories a:after, +#about a:after { + content: " (" attr(href) ")"; + font-style: italic; + font-size: 10pt; +} +#profile #myblog:after, +#jobs p a:after { + content: none; +} + +a { + text-decoration: none; +} + +#mylanguages a, +#jobs p a { + color: inherit; +} + +#actions { + visibility: hidden; +} + +/* --------- */ \ No newline at end of file diff --git a/css/resume.css b/css/resume.css index 46ba5ed..6ea05f8 100644 --- a/css/resume.css +++ b/css/resume.css @@ -1,134 +1,289 @@ /* --------------------------------------------------------------------------------- - STRIPPED DOWN RESUME TEMPLATE + STRIPPED DOWN RESUME TEMPLATE html resume v0.9: 5/28/09 - design and code by: thingsthatarebrown.com + design and code by: thingsthatarebrown.com (matt brown) --------------------------------------------------------------------------------- */ +.msg { + padding: 10px; + background: #222; + position: relative; +} -.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; } +.msg h1 { color: #fff } + +.msg a { + margin-left: 20px; + background: #408814; + color: white; + padding: 4px 8px; + text-decoration: none; +} + +.msg a:hover { background: #266400 } + +html { + height: 100% ! important; + background: url('../images/low_contrast_linen.png'); +} /* //-- 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; } + +body { + font-family: Georgia, Garamond, "Times New Roman", Times, serif; + color: #444; +} + +#inner { + padding: 10px 80px; + margin: 0 auto; + background: #f5f5f5; + border: solid #666; + border-width: 8px 0 2px 0; + box-shadow: 0 1px 3px #000; +} + +.yui-gf { + margin-bottom: 2em; + padding-bottom: 2em; + border-bottom: 1px solid #ccc; +} + +.yui-gf div.first { width: 12.3% } + +.yui-gf .yui-u { width: 80.2% } /* //-- 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; } + +#hd { + margin: 2.5em 0 3em 0; + padding-bottom: 1.5em; + border-bottom: 1px solid #ccc; +} + +#hd h1 { + font-size: 48px; + text-transform: uppercase; + letter-spacing: 3px; +} + +#hd h2 { + text-transform: uppercase; + letter-spacing: 2px; +} + +#bd, +#ft { margin-bottom: 2em } + +#ft p { + margin-bottom: 0; + text-align: center; +} /* //-- 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; } + +#ft { + padding: 1em 0 5em 0; + font-size: 92%; + border-top: 1px solid #ccc; + 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; text-align: right; font-size: 12px; position: relative; float: left; width: 100%;} -.contact-info a { position :relative; float: left; width: 100%;} -.contact-info img {float: right; border: 1px solid #ccc; width: 150px; height: 150px; margin: -24px 0 14px;} -.first h2 { font-style: italic; } -.last { border-bottom: 0 } +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; +} + +.contact-info { + margin-top: 7px; + text-align: right; + font-size: 12px; + position: relative; + float: left; + width: 100%; +} + +.contact-info img { + float: right; + border: 1px solid #ccc; + width: 140px; + height: 140px; + margin: -24px 0 14px; + border-radius: 3px; +} + +.contact-info a { + position : relative; + float: left; + width: 100%; +} + +.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; } +#pdf { + display: block; + float: left; + background: #666; + color: #fff; + padding: 6px 50px 6px 12px; + margin-bottom: 6px; + text-decoration: none; +} -.org, .job { position: relative; margin-bottom: 1em; padding-bottom: 1em; border-bottom: 1px solid #ccc; } -.org a, .job a { border: none; text-decoration: none; } -.org h4, .job h4 { position: absolute; top: 0.35em; right: 0 } -.org p, .job p { margin: 0.75em 0 3em 0; } +#pdf:hover { background: #222 } -.last { border: none; } -.content-languages { } -.content-languages ul { margin: 0; display: inline; float: left; position: relative; width: 30%;} -.content-languages li { margin: 3px 0; padding: 3px 0; position: relative; float: left; display-inline; } -.content-languages li span { font-size: 152%; display: block; margin-bottom: -2px; padding: 0 } -.talent { width: 32%; float: left } -.talent h2 { margin-bottom: 6px; } +.org, +.job { + position: relative; + margin-bottom: 1em; + padding-bottom: 1em; + border-bottom: 1px solid #ccc; +} -#srt-ttab { margin-bottom: 100px; text-align: center; } -#srt-ttab img.last { margin-top: 20px } +.org h4, +.job h4 { + position: absolute; + top: 0.35em; + right: 0; +} + +.org a, +.job a { + border: none; + text-decoration: none; +} + +.org p, +.job p { margin: 0.75em 0 3em 0 } + +.last { border: none } + +.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; +#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, Garamond, "Times New Roman", Times, serif; + 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; +#gen { + position: relative; + float: left; + margin-left: 10px; + width: 20%; + height: 44px; + font-family: Georgia, Garamond, "Times New Roman", Times, serif; + font-size: 18px; + color: #fff; + 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; -} - -.yui-gb ul { - width: 80%; -} +#gen:hover { cursor: pointer } .yui-gb { - margin-top: 10px; - margin-bottom: 10px; - padding-bottom: 10px; + margin-top: 10px; + margin-bottom: 10px; + padding-bottom: 10px; } -.enlarge-medium { - font-size: 15px; +.enlarge-medium { font-size: 15px } + +.yui-gb ul { width: 80% } + +noscript { + display: block; + margin-top: 30px; + font-size: 152%; + color: #990003; } + +#actions { + padding: 30px 0 40px 0; + margin: 0 auto 0 auto; + width: 73.076em; +} + +#actions a { + color: #ccc; + text-decoration: none; + text-shadow: 0 0 5px #000; + margin-left: 1em; +} + +#actions * { float: right } + +#doc { + margin-top: 70px; + background: #f5f5f5; + padding: 25px 107px 0px 107px; +} + +#doc2 { padding-bottom: 70px } \ No newline at end of file diff --git a/images/low_contrast_linen.png b/images/low_contrast_linen.png new file mode 100644 index 0000000..17b491e Binary files /dev/null and b/images/low_contrast_linen.png differ diff --git a/index.html b/index.html index ac1e791..2e1b7c6 100644 --- a/index.html +++ b/index.html @@ -1,17 +1,18 @@ - + - Github Résumé + GitHub Résumé - + - + + - + diff --git a/js/github.js b/js/github.js deleted file mode 100644 index a67bf7d..0000000 --- a/js/github.js +++ /dev/null @@ -1,702 +0,0 @@ -// ## 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, 30 per page - // - // gh.user("fitzgen").repos(function (data) { - // data.repositories.forEach(function (repo) { - // ... - // }); - // }); - gh.user.prototype.repos = function (callback, context, page) { - gh.repo.forUser(this.username, callback, context, page); - return this; - }; - - // Get a list of all repos for this user. - // - // gh.user("fitzgen").allRepos(function (data) { - // alert(data.repositories.length); - // }); - gh.user.prototype.allRepos = function (callback, context) { - var repos = [], - username = this.username, - page = 1; - - function exitCallback () { - callback.call(context, { repositories: repos }); - } - - function pageLoop (data) { - if (data.repositories.length == 0) { - exitCallback(); - } else { - repos = repos.concat(data.repositories); - page += 1; - gh.repo.forUser(username, pageLoop, context, page); - } - } - - gh.repo.forUser(username, pageLoop, context, page); - - 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, page) { - if (!page) - page = 1; - - jsonp("repos/show/" + user + '?page=' + page, 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)); - diff --git a/js/githubresume.js b/js/githubresume.js index ea8535b..d8d5450 100644 --- a/js/githubresume.js +++ b/js/githubresume.js @@ -53,96 +53,122 @@ var home = function() { }); }; +var github_user = function(username, callback) { + $.getJSON('https://api.github.com/users/' + username + '?callback=?', callback); +} + +var github_user_repos = function(username, callback, page_number, prev_data) { + var page = (page_number ? page_number : 1), + url = 'https://api.github.com/users/' + username + '/repos?callback=?', + data = (prev_data ? prev_data : []); + + if (page_number > 1) { + url += '&page=' + page_number; + } + $.getJSON(url, function(repos) { + data = data.concat(repos.data); + if (repos.data.length > 0) { + github_user_repos(username, callback, page + 1, data); + } else { + callback(data); + } + }); +} + +var github_user_orgs = function(username, callback) { + $.getJSON('https://api.github.com/users/' + username + '/orgs?callback=?', callback); +} + var run = function() { + var itemCount = 0, + maxItems = 5, + maxLanguages = 9; - var gh_user = gh.user(username); - var itemCount = 0, maxItems = 5, maxLanguages = 9; - - var res = gh_user.show(function(data) { - gh_user.allRepos(function(data) { - repos = data; - }); - - var sinceDate = new Date(data.user.created_at); - since = sinceDate.getFullYear(); - var sinceMonth = sinceDate.getMonth(); + var res = github_user(username, function(data) { + var sinceDate = new Date(data.created_at); + var since = sinceDate.getFullYear(); + var sinceMonth = sinceDate.getMonth(); var currentYear = (new Date).getFullYear(); - switch (since) { - case currentYear-1: - since = 'last year'; - break; - case currentYear: - since = 'this year'; - break; - } + switch (since) { + case currentYear-1: + since = 'last year'; + break; + case currentYear: + since = 'this year'; + break; + } var addHttp = ''; - if (data.user.blog !== undefined && data.user.blog !== null) { - if (data.user.blog.indexOf('http') < 0) { - addHttp = 'http://'; - } + if (data.blog && data.blog.indexOf('http') < 0) { + addHttp = 'http://'; } var name = username; - if (data.user.name !== null && data.user.name !== undefined) { - name = data.user.name; + if (data.name !== null && data.name !== undefined) { + name = data.name; } var view = { name: name, - email: data.user.email, - created_at: data.user.created_at, - earlyAdopter: 0, - location: data.user.location, - gravatar_id: data.user.gravatar_id, - repos: data.user.public_repo_count, - plural: data.user.public_repo_count > 1 ? 'repositories' : 'repository', + email: data.email, + created_at: data.created_at, + earlyAdopter: 0, + location: data.location, + gravatar_id: data.gravatar_id, + repos: data.public_repos, + reposLabel: data.public_repos > 1 ? 'repositories' : 'repository', + followers: data.followers, + followersLabel: data.followers > 1 ? 'followers' : 'follower', username: username, - since: since + since: since, + resume_url: window.location }; - if (data.user.blog !== undefined && data.user.blog !== null) { - view.blog = addHttp + data.user.blog; + if (data.blog !== undefined && data.blog !== null && data.blog !== '') { + view.blog = addHttp + data.blog; + } + + // We consider a limit of 4 months since the Github opening (Feb 2008) to be considered as an early adopter + if (since == '2008' && sinceMonth <= 5) { + view.earlyAdopter = 1; } - // We consider a limit of 4 months since the Github opening (Feb 2008) to be considered as an early adopter - if (since == '2008' && sinceMonth <= 5) { - view.earlyAdopter = 1; - } - $.ajax({ url: 'views/resume.html', dataType: 'html', success: function(data) { - var template = data; - var html = Mustache.to_html(template, view); + var template = data, + html = Mustache.to_html(template, view); $('#resume').html(html); document.title = name + "'s Résumé"; + $("#actions #print").click(function(){ + window.print(); + return false; + }); } }); }); - gh_user.allRepos(function(data) { - var repos = data.repositories; + github_user_repos(username, function(data) { + var sorted = [], + languages = {}, + popularity; - var sorted = []; - var languages = {}; - - repos.forEach(function(elm, i, arr) { - if (arr[i].fork !== false) { + $.each(data, function(i, repo) { + if (repo.fork !== false) { return; } - - if (arr[i].language) { - if (arr[i].language in languages) { - languages[arr[i].language]++; + + if (repo.language) { + if (repo.language in languages) { + languages[repo.language]++; } else { - languages[arr[i].language] = 1; + languages[repo.language] = 1; } } - var popularity = arr[i].watchers + arr[i].forks; - sorted.push({position: i, popularity: popularity, info: arr[i]}); + popularity = repo.watchers + repo.forks; + sorted.push({position: i, popularity: popularity, info: repo}); }); function sortByPopularity(a, b) { @@ -154,6 +180,7 @@ var run = function() { var languageTotal = 0; function sortLanguages(languages, limit) { var sorted_languages = []; + for (var lang in languages) { if (typeof(lang) !== "string") { continue; @@ -167,11 +194,12 @@ var run = function() { }); languageTotal += languages[lang]; - } + if (limit) { sorted_languages = sorted_languages.slice(0, limit); } + return sorted_languages.sort(sortByPopularity); } @@ -179,15 +207,17 @@ var run = function() { url: 'views/job.html', dataType: 'html', success: function(response) { - var now = new Date().getFullYear(); languages = sortLanguages(languages, maxLanguages); if (languages && languages.length > 0) { - var ul = $(''); - languages.forEach(function(elm, i, arr) { + var ul = $(''), + percent, li; + + $.each(languages, function(i, lang) { x = i + 1; - var percent = parseInt((arr[i].popularity / languageTotal) * 100); - var li = $('
  • ' + arr[i].toString() + ' ('+percent+'%)
  • '); + percent = parseInt((lang.popularity / languageTotal) * 100); + li = $('
  • ' + lang.toString() + ' ('+percent+'%)
  • '); + if (x % 3 == 0 || (languages.length < 3 && i == languages.length - 1)) { li.attr('class', 'last'); ul.append(li); @@ -205,54 +235,60 @@ var run = function() { if (sorted.length > 0) { $('#jobs').html(''); itemCount = 0; - sorted.forEach(function(elm, index, arr) { + var since, until, date, view, template, html; + + $.each(sorted, function(index, repo) { if (itemCount >= maxItems) { return; } - var since = new Date(arr[index].info.created_at); + since = new Date(repo.info.created_at); since = since.getFullYear(); + until = new Date(repo.info.pushed_at); + until = until.getFullYear(); + if (since == until) { + date = since; + } else { + date = since + ' - ' + until; + } - var view = { - name: arr[index].info.name, - since: since, - now: now, - language: arr[index].info.language, - description: arr[index].info.description, + view = { + name: repo.info.name, + date: date, + language: repo.info.language, + description: repo.info.description, username: username, - watchers: arr[index].info.watchers, - forks: arr[index].info.forks + watchers: repo.info.watchers, + forks: repo.info.forks, + watchersLabel: repo.info.watchers > 1 ? 'watchers' : 'watcher', + forksLabel: repo.info.forks > 1 ? 'forks' : 'fork', }; if (itemCount == sorted.length - 1 || itemCount == maxItems - 1) { view.last = 'last'; } - var template = response; - var html = Mustache.to_html(template, view); - + template = response; + html = Mustache.to_html(template, view); $('#jobs').append($(html)); ++itemCount; }); } else { - $('#jobs').html(''); - $('#jobs').append('

    I do not have any public repository. Sorry.

    '); + $('#jobs').html('').append('

    I do not have any public repositories. Sorry.

    '); } } }); }); - gh_user.orgs(function(data) { - var orgs = data.organizations; - + github_user_orgs(username, function(response) { var sorted = []; - orgs.forEach(function(elm, i, arr) { - if (arr[i].name === undefined) { + $.each(response.data, function(i, org) { + if (org.login === undefined) { return; } - sorted.push({position: i, info: arr[i]}); + sorted.push({position: i, info: org}); }); $.ajax({ @@ -263,21 +299,24 @@ var run = function() { if (sorted.length > 0) { $('#orgs').html(''); - sorted.forEach(function(elm, index, arr) { + + var name, view, template, html; + + $.each(sorted, function(index, org) { if (itemCount >= maxItems) { return; } - var view = { - name: arr[index].info.name, - login: arr[index].info.login, + name = (org.info.name || org.info.login); + view = { + name: name, now: now }; if (itemCount == sorted.length - 1 || itemCount == maxItems) { view.last = 'last'; } - var template = response; - var html = Mustache.to_html(template, view); + template = response; + html = Mustache.to_html(template, view); $('#orgs').append($(html)); ++itemCount; diff --git a/views/error.html b/views/error.html index 72d4306..1b03f72 100644 --- a/views/error.html +++ b/views/error.html @@ -3,7 +3,7 @@

    - We couldn't find enough to build a résumé from. Make sure this is the good username :-). Try again with another username? + There was an issue accessing the requested profile to auto-generate this résumé. Access the user's profile directly.

    diff --git a/views/index.html b/views/index.html index f92e783..baac670 100644 --- a/views/index.html +++ b/views/index.html @@ -1,5 +1,5 @@
    - +

    @@ -9,7 +9,7 @@


    After a tweet by John Resig - I imagined that it may be nice for people to be able to generate their Github résumés. + I imagined that it may be nice for people to be able to generate their GitHub résumés.



    @@ -54,9 +54,7 @@


    This is the first version. I am planning on adding things as such as your most committed forks, most committed repositories and make the "My Popular Repositories" be built from - your complete list of repositories. The issue right now is say you have 37 repositories, this will only retrieve the first - 30 repositories that were created. I'll put a note here when this is fixed or feel free to fork the page, - make changes and send them back :-) + your complete list of repositories. Feel free to fork the page if you want to help :-)

    diff --git a/views/job.html b/views/job.html index 5a141ff..449d005 100644 --- a/views/job.html +++ b/views/job.html @@ -3,14 +3,14 @@ {{name}}

    {{#language}}{{language}} - {{/language}}Creator & Owner

    -

    {{since}} - {{now}}

    -

    {{description}}.

    +

    {{date}}

    +

    {{description}}

    - This repository has {{watchers}} watcher(s) and {{forks}} fork(s). + This repository has {{watchers}} {{watchersLabel}} and {{forks}} {{forksLabel}}. If you would like more information about this repository and my contributed code, please visit the repo on - Github. + GitHub.

    diff --git a/views/org.html b/views/org.html index a69c970..8b80672 100644 --- a/views/org.html +++ b/views/org.html @@ -3,7 +3,7 @@

    {{name}}

    Member

    {{now}}

    -

    If you would like more information about this organization, please visit the organization page on Github.

    +

    If you would like more information about this organization, please visit the organization page on GitHub.

    diff --git a/views/resume.html b/views/resume.html index 8df4c79..5e2450f 100644 --- a/views/resume.html +++ b/views/resume.html @@ -1,11 +1,17 @@ +
    + Print + Email +
    +
    -
    + +
    -

    {{name}}

    +

    {{name}}

    Passionate github user

    @@ -13,9 +19,9 @@
    {{#gravatar_id}} - + {{/gravatar_id}} -

    {{email}}

    + {{#email}}

    {{/email}}
    @@ -25,34 +31,32 @@
    -
    +
    -

    Github Profile

    +

    GitHub Profile

    I'm a developer {{#location}} - based in {{location}} + based in {{location}} {{/location}} - {{#repos}}with {{repos}} public {{plural}}{{/repos}}{{^repos}}without any public repository for now{{/repos}}. + {{#repos}}with {{repos}} public {{reposLabel}}{{/repos}}{{^repos}}without any public repository for now{{/repos}}{{#followers}} and {{followers}} {{followersLabel}}{{/followers}}. I've been using github.com since {{since}}{{#earlyAdopter}}, therefore I'm an early adopter,{{/earlyAdopter}} - {{#blog}} - and sometimes I blog at {{blog}}. - {{/blog}} + {{#blog}} and sometimes I blog at {{blog}}.{{/blog}}

    -
    +

    Languages

    - +
    -
    +

    My Popular Repositories

    @@ -72,7 +76,7 @@
    -
    +

    About This Résumé

    @@ -80,7 +84,7 @@

    This résumé 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 its sum of watchers and forks. Do not hesitate to visit my github page + by its sum of watchers and forks. Do not hesitate to visit my github page for more information about my repositories and work.

    @@ -90,15 +94,9 @@
    -

    {{name}} — {{email}}https://github.com/{{username}}

    +

    {{name}} — {{#email}}{{email}} — {{/email}} https://github.com/{{username}}

    - diff --git a/views/resumeOrgs.html b/views/resumeOrgs.html new file mode 100644 index 0000000..039d805 --- /dev/null +++ b/views/resumeOrgs.html @@ -0,0 +1,85 @@ +
    +
    + +
    +
    +
    +
    +

    {{name}}

    +

    Github organization

    +
    +
    + +
    +
    + {{#gravatar_id}} + + {{/gravatar_id}} + {{#email}}

    {{email}}

    {{/email}} +
    +
    +
    +
    + +
    +
    +
    + +
    +
    +

    Github Profile

    +
    +
    +

    + We are XX developers gathered in a group + {{#location}} + based in {{location}} + {{/location}} + with {{repos}} public {{reposLabel}}. + We've created this Github group in {{since}}{{#earlyAdopter}}, therefore I'm an early adopter,{{/earlyAdopter}}{{#blog}} and you can find more informations about us at {{blog}}{{/blog}}. +

    +

    +
    +
    +
    +
    +

    Languages

    +
    +
    + +
    +
    +
    +
    +

    Our Popular Repositories

    +
    + +
    + Loading information... +
    +
    + +
    +
    +

    About This Résumé

    +
    +
    +

    + This résumé is generated automatically using information from github. The repositories are + ordered by popularity based on a very simple popularity heuristic that defines the popularity of a repository + by its sum of watchers and forks. Do not hesitate to visit our github group's page + for more information about our repositories and work. +

    +
    +
    +
    +
    +
    + +
    +

    {{name}} — {{#email}}{{email}} — {{/email}} https://github.com/{{username}}

    +
    + +
    + +