diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..b9807ae --- /dev/null +++ b/AUTHORS @@ -0,0 +1,2 @@ +Nick Fitzgerald +Alexander Beletsky diff --git a/README.md b/README.md index 9cbb202..a906598 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,21 @@ Exposes `gh` to the global environment. Tries to follow both the form of Github HTTP API and JS style. - gh.authenticate("fitzgen", "sdfk32we-FAKE-uydfs7f-rhrwe8r7"); - var huddlej = gh.user("huddlej"); - huddlej.show(function (data) { - console.log(data.user); - }); - huddlej.repos(function (data) { - console.log("Number of repos: " + data.repositories.length); - }); - var wujs = gh.repo("fitzgen", "wu.js") - wujs.show(function (data) { - console.log("Number of watchers: " + data.repository.watchers); - }); - wujs.update({ has_wiki: 0 }); // Unfortunately, no callbacks with POSTs :( +```js +gh.authenticate("fitzgen", "sdfk32we-FAKE-uydfs7f-rhrwe8r7"); +var huddlej = gh.user("huddlej"); +huddlej.show(function (data) { + console.log(data.user); +}); +huddlej.repos(function (data) { + console.log("Number of repos: " + data.repositories.length); +}); +var wujs = gh.repo("fitzgen", "wu.js") +wujs.show(function (data) { + console.log("Number of watchers: " + data.repository.watchers); +}); +wujs.update({ has_wiki: 0 }); // Unfortunately, no callbacks with POSTs :( +``` COMPLETE ======== @@ -30,4 +32,4 @@ COMPLETE TODO ==== -* Documentation \ No newline at end of file +* Documentation diff --git a/github.js b/github.js index 0cea36e..7b5acde 100644 --- a/github.js +++ b/github.js @@ -11,6 +11,8 @@ // The username and authentication token of the library's user. authUsername, authToken, + // The access token of OAuth user, can be used instead of authUsername + authToken + authAccessToken, // 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. @@ -29,11 +31,8 @@ var id = +new Date, script = document.createElement("script"); - // prevention error fix - // if gh object calls are to frequent, there is a change of collision in id - // it has been reproduced with my tests, giving a runtime error, because the callback with same id have already been destroyed - if(gh.__jsonp_callbacks[id]!=null) - id+= +Math.random(); + 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]; @@ -46,7 +45,10 @@ url += prefix + "callback=" + encodeURIComponent("gh.__jsonp_callbacks[" + id + "]"); if (authUsername && authToken) { - url += "&login=" + authUsername + "&authToken=" + authToken; + url += "&login=" + authUsername + "&token=" + authToken; + } + if (authAccessToken) { + url += "&access_token=" + authAccessToken; } script.setAttribute("src", apiRoot + url); @@ -63,7 +65,12 @@ post = function (url, vals) { var form = document.createElement("form"), - iframe = document.createElement("iframe"), + iframe = document.createElement("iframe"); + + // Need to insert the iframe now so contentDocument and contentWindow are defined + document.body.appendChild(iframe); + + var doc = iframe.contentDocument !== undefined ? iframe.contentDocument : iframe.contentWindow.document, @@ -83,14 +90,13 @@ 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) { + if ((!authUsername || !authToken || authUsername !== username) && !authAccessToken) { throw new TypeError("gh: Must be authenticated to do that."); } }, @@ -126,9 +132,11 @@ // 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) { + // If using OAuth access token, you may pass `null` for `username` and `token` args. + gh.authenticate = function (username, token, accessToken) { authUsername = username; authToken = token; + authAccessToken = accessToken; return this; }; @@ -160,7 +168,14 @@ // Update a user's info. You must be authenticated as this user for this to // succeed. // - // TODO: example + // gh.user("fitzgen").update({name: "Nick Fitzgerald"}); + // + // Possible attributes to update include: + // - name + // - email + // - blog + // - company + // - location gh.user.prototype.update = function (params) { authRequired(this.username); var key, postData = { @@ -178,14 +193,18 @@ // Get a list of who this user is following. // - // TODO: example + // gh.user("fitzgen").following(function (data) { + // console.log(data.users); + // }); 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("fitzgen").followers(function (data) { + // console.log(data.users); + // }); gh.user.prototype.followers = function (callback, context) { jsonp("user/show/" + this.username + "/followers", callback, context); }; @@ -193,7 +212,7 @@ // Make this user follow some other user. You must be authenticated as this // user for this to succeed. // - // TODO: example + // gh.user("fitzgen").follow("mikepack"); gh.user.prototype.follow = function (user) { authRequired.call(this); post("user/follow/" + user); @@ -203,7 +222,7 @@ // Make this user quit following the given `user`. You must be authenticated // as this user to succeed. // - // TODO: example + // gh.user("fitzgen").unfollow("mikepack"); gh.user.prototype.unfollow = function (user) { authRequired.call(this); post("user/unfollow/" + user); @@ -212,19 +231,76 @@ // Get a list of repositories that this user is watching. // - // TODO: example + // gh.user("fitzgen").watching(function (data) { + // console.log(data.repositories); + // }); gh.user.prototype.watching = function (callback, context) { jsonp("repos/watched/" + this.username, callback, context); return this; }; - // Get a list of this user's repositories. + // Get a list of organizations that this user belongs to. // - // gh.user("fitzgen").repos(function (data) { + // gh.user("fitzgen").organizations( function(data) { + // alert(data.organizations.length); + // }); + gh.user.prototype.orgs = function (callback, context) { + jsonp("user/show/" + this.username + "/organizations", callback, context); + return this + }; + + // Get a list of all organization repos this user has access to. + // + // gh.authenticate("fitzgen", ); + // user = gh.user("fitzen"); + // + // user.allOrgRepos(function (data) { // alert(data.repositories.length); // }); - gh.user.prototype.repos = function (callback, context) { - gh.repo.forUser(this.username, callback, context); + gh.user.prototype.allOrgRepos = function (callback) { + authRequired(this.username); + jsonp("organizations/repositories", callback); + 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; }; @@ -397,12 +473,16 @@ context = arguments[2]; } url += "?" + paramify(opts); + jsonp(url, callback, context); return this; }; // Get all the repos that are owned by `user`. - gh.repo.forUser = function (user, callback, context) { - jsonp("repos/show/" + user, callback, context); + gh.repo.forUser = function (user, callback, context, page) { + if (!page) + page = 1; + + jsonp("repos/show/" + user + '?page=' + page, callback, context); return this; }; @@ -458,11 +538,28 @@ gh.issue = function (user, repo, number) { if ( !(this instanceof gh.issue) ) - return new gh.commit(user, repo, number); + return new gh.issue(user, repo, number); this.user = user; this.repo = repo; this.number = number; }; + + //View open issues + gh.issue.prototype.openIssues = function (callback, context) { + jsonp("issues/list/" + this.user + "/" + this.repo + "/open", + callback, + context); + return this; + }; + + //View closed issues + gh.issue.prototype.closedIssues = function (callback, context) { + jsonp("issues/list/" + this.user + "/" + this.repo + "/closed", + callback, + context); + return this; + }; + // View this issue's info. gh.issue.prototype.show = function (callback, context) { @@ -633,35 +730,86 @@ jsonp("blob/full/" + this.user + "/" + this.repo + "/" + sha, callback, context); - return this; + 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/", + "http://github.com/", function (callback, context) { jsonp(this.user + "/" + this.repo + "/network_meta", - callback, - context); + callback, + context); return this; } - ); + ); - 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; + // ### Pull Requests + + gh.pulls = function(user, repo) { + if (!(this instanceof gh.pulls)) { + return new gh.pulls(user, repo); } - ); + this.user = user; + this.repo = repo; + }; + + // Get all pull requests for the repo + // + // gh.pulls("fitzgen", "github-api").allPulls(function (data) { + // data.pulls.forEach(function (pull) { + // console.log("Title: " + pull.title); + // }); + // }); + gh.pulls.prototype.allPulls = function (callback, context) { + jsonp("pulls/" + this.user + "/" + this.repo, callback, context); + return this; + }; + + // Get pull requests filtered by state. `state` can be "open" or "closed". + // + // gh.pulls("fitzgen", "github-api").forState("closed", function (data) { + // data.pulls.forEach(function (pull) { + // console.log("Title: " + pull.title + " State: " + pull.state); + // }); + // }); + gh.pulls.prototype.forState = function (state, callback, context) { + jsonp("pulls/" + this.user + "/" + this.repo + "/" + state, callback, context); + return this; + }; + + // Get pull requests by number + // + // Important: This call returns a single object called "pull" instead of multiple "pulls" objects! + // + // gh.pulls("fitzgen", "github-api").forNumber("1", function (data) { + // console.log("Title: " + data.pull.title + " Number: " + data.pull.number); + // }); + gh.pulls.prototype.forNumber = function (number, callback, context) { + jsonp("pulls/" + this.user + "/" + this.repo + "/" + number, callback, context); + return this; + }; + + //TODO: Creating a Pull Request -}(window)); \ No newline at end of file +}(window));