text: /*!
   * @overview  Github.js
   *
   * @copyright (c) 2013 Michael Aufreiter, Development Seed
   *            Github.js is freely distributable.
   *
   * @license   Licensed under MIT license
   *
   *            For all details and documentation:
   *            http://substance.io/michael/github
   */
  
  (function() {
  
    // Initial Setup
    // -------------
  
    var XMLHttpRequest,  _;
    if (typeof exports !== 'undefined') {
        XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
        _ = require('underscore');
        btoa = require('btoa');
    } else {
        _ = window._;
    }
    //prefer native XMLHttpRequest always
    if (typeof window !== 'undefined' && typeof window.XMLHttpRequest !== 'undefined'){
        XMLHttpRequest = window.XMLHttpRequest;
    }
  
  
    var API_URL = 'https://api.github.com';
  
    var Github = function(options) {
  
      // HTTP Request Abstraction
      // =======
      //
      // I'm not proud of this and neither should you be if you were responsible for the XMLHttpRequest spec.
  
      function _request(method, path, data, cb, raw, sync) {
        function getURL() {
          var url = path.indexOf('//') >= 0 ? path : API_URL + path;
          return url + ((/\?/).test(url) ? "&" : "?") + (new Date()).getTime();
        }
  
        var xhr = new XMLHttpRequest();
        if (!raw) {xhr.dataType = "json";}
  
        xhr.open(method, getURL(), !sync);
        if (!sync) {
          xhr.onreadystatechange = function () {
            if (this.readyState == 4) {
              if (this.status >= 200 && this.status < 300 || this.status === 304) {
                cb(null, raw ? this.responseText : this.responseText ? JSON.parse(this.responseText) : true, this);
              } else {
                cb({path: path, request: this, error: this.status});
              }
            }
          };
        }
        xhr.setRequestHeader('Accept','application/vnd.github.v3.raw+json');
        xhr.setRequestHeader('Content-Type','application/json;charset=UTF-8');
        if ((options.token) || (options.username && options.password)) {
          var authorization = options.token ? 'token ' + options.token : 'Basic ' + btoa(options.username + ':' + options.password);
          xhr.setRequestHeader('Authorization', authorization);
        }
        if (data)
          xhr.send(JSON.stringify(data));
        else
          xhr.send();
        if (sync) return xhr.response;
      }
  
      function _requestAllPages(path, cb) {
        var results = [];
        (function iterate() {
          _request("GET", path, null, function(err, res, xhr) {
            if (err) {
              return cb(err);
            }
  
            results.push.apply(results, res);
  
            var links = (xhr.getResponseHeader('link') || '').split(/\s*,\s*/g),
                next = _.find(links, function(link) { return /rel="next"/.test(link); });
  
            if (next) {
              next = (/<(.*)>/.exec(next) || [])[1];
            }
  
            if (!next) {
              cb(err, results);
            } else {
              path = next;
              iterate();
            }
          });
        })();
      }
  
  
      // User API
      // =======
  
      Github.User = function() {
        this.repos = function(cb) {
          // Github does not always honor the 1000 limit so we want to iterate over the data set.
          _requestAllPages("/user/repos?type=all&per_page=1000&sort=updated", function(err, res) {
            cb(err, res);
          });
        };
  
        // List user organizations
        // -------
  
        this.orgs = function(cb) {
          _request("GET", "/user/orgs", null, function(err, res) {
            cb(err, res);
          });
        };
  
        // List authenticated user's gists
        // -------
  
        this.gists = function(cb) {
          _request("GET", "/gists", null, function(err, res) {
            cb(err,res);
          });
        };
  
        // List authenticated user's unread notifications
        // -------
  
        this.notifications = function(cb) {
          _request("GET", "/notifications", null, function(err, res) {
            cb(err,res);
          });
        };
  
        // Show user information
        // -------
  
        this.show = function(username, cb) {
          var command = username ? "/users/"+username : "/user";
  
          _request("GET", command, null, function(err, res) {
            cb(err, res);
          });
        };
  
        // List user repositories
        // -------
  
        this.userRepos = function(username, cb) {
          // Github does not always honor the 1000 limit so we want to iterate over the data set.
          _requestAllPages("/users/"+username+"/repos?type=all&per_page=1000&sort=updated", function(err, res) {
            cb(err, res);
          });
        };
  
        // List a user's gists
        // -------
  
        this.userGists = function(username, cb) {
          _request("GET", "/users/"+username+"/gists", null, function(err, res) {
            cb(err,res);
          });
        };
  
        // List organization repositories
        // -------
  
        this.orgRepos = function(orgname, cb) {
          // Github does not always honor the 1000 limit so we want to iterate over the data set.
          _requestAllPages("/orgs/"+orgname+"/repos?type=all&&page_num=1000&sort=updated&direction=desc", function(err, res) {
            cb(err, res);
          });
        };
  
        // Follow user
        // -------
  
        this.follow = function(username, cb) {
          _request("PUT", "/user/following/"+username, null, function(err, res) {
            cb(err, res);
          });
        };
  
        // Unfollow user
        // -------
  
        this.unfollow = function(username, cb) {
          _request("DELETE", "/user/following/"+username, null, function(err, res) {
            cb(err, res);
          });
        };
  
        // Create a repo
        // -------
        this.createRepo = function(options, cb) {
          _request("POST", "/user/repos", options, cb);
        };
  
      };
  
      // Repository API
      // =======
  
      Github.Repository = function(options) {
        var repo = options.name;
        var user = options.user;
  
        var that = this;
        var repoPath = "/repos/" + user + "/" + repo;
  
        var currentTree = {
          "branch": null,
          "sha": null
        };
  
  
        // Delete a repo
        // --------
  
        this.deleteRepo = function(cb) {
          _request("DELETE", repoPath, options, cb);
        };
  
        // Uses the cache if branch has not been changed
        // -------
  
        function updateTree(branch, cb) {
          if (branch === currentTree.branch && currentTree.sha) return cb(null, currentTree.sha);
          that.getRef("heads/"+branch, function(err, sha) {
            currentTree.branch = branch;
            currentTree.sha = sha;
            cb(err, sha);
          });
        }
  
        // Get a particular reference
        // -------
  
        this.getRef = function(ref, cb) {
          _request("GET", repoPath + "/git/refs/" + ref, null, function(err, res) {
            if (err) return cb(err);
            cb(null, res.object.sha);
          });
        };
  
        // Create a new reference
        // --------
        //
        // {
        //   "ref": "refs/heads/my-new-branch-name",
        //   "sha": "827efc6d56897b048c772eb4087f854f46256132"
        // }
  
        this.createRef = function(options, cb) {
          _request("POST", repoPath + "/git/refs", options, cb);
        };
  
        // Delete a reference
        // --------
        //
        // repo.deleteRef('heads/gh-pages')
        // repo.deleteRef('tags/v1.0')
  
        this.deleteRef = function(ref, cb) {
          _request("DELETE", repoPath + "/git/refs/"+ref, options, cb);
        };
  
        // Create a repo
        // -------
  
        this.createRepo = function(options, cb) {
          _request("POST", "/user/repos", options, cb);
        };
  
        // Delete a repo
        // --------
  
        this.deleteRepo = function(cb) {
          _request("DELETE", repoPath, options, cb);
        };
  
        // List all tags of a repository
        // -------
  
        this.listTags = function(cb) {
          _request("GET", repoPath + "/tags", null, function(err, tags) {
            if (err) return cb(err);
            cb(null, tags);
          });
        };
  
        // List all pull requests of a respository
        // -------
  
        this.listPulls = function(state, cb) {
          _request("GET", repoPath + "/pulls" + (state ? '?state=' + state : ''), null, function(err, pulls) {
            if (err) return cb(err);
            cb(null, pulls);
          });
        };
  
        // Gets details for a specific pull request
        // -------
  
        this.getPull = function(number, cb) {
          _request("GET", repoPath + "/pulls/" + number, null, function(err, pull) {
            if (err) return cb(err);
            cb(null, pull);
          });
        };
  
        // Retrieve the changes made between base and head
        // -------
  
        this.compare = function(base, head, cb) {
          _request("GET", repoPath + "/compare/" + base + "..." + head, null, function(err, diff) {
            if (err) return cb(err);
            cb(null, diff);
          });
        };
  
        // List all branches of a repository
        // -------
  
        this.listBranches = function(cb) {
          _request("GET", repoPath + "/git/refs/heads", null, function(err, heads) {
            if (err) return cb(err);
            cb(null, _.map(heads, function(head) { return _.last(head.ref.split('/')); }));
          });
        };
  
        // Retrieve the contents of a blob
        // -------
  
        this.getBlob = function(sha, cb) {
          _request("GET", repoPath + "/git/blobs/" + sha, null, cb, 'raw');
        };
  
        // For a given file path, get the corresponding sha (blob for files, tree for dirs)
        // -------
  
        this.getSha = function(branch, path, cb) {
          if (!path || path === "") return that.getRef("heads/"+branch, cb);
          _request("GET", repoPath + "/contents/"+path, {ref: branch}, function(err, pathContent) {
            if (err) return cb(err);
            cb(null, pathContent.sha);
          });
        };
  
        // Retrieve the tree a commit points to
        // -------
  
        this.getTree = function(tree, cb) {
          _request("GET", repoPath + "/git/trees/"+tree, null, function(err, res) {
            if (err) return cb(err);
            cb(null, res.tree);
          });
        };
  
        // Post a new blob object, getting a blob SHA back
        // -------
  
        this.postBlob = function(content, cb) {
          if (typeof(content) === "string") {
            content = {
              "content": content,
              "encoding": "utf-8"
            };
          } else {
            	content = {
                "content": btoa(String.fromCharCode.apply(null, new Uint8Array(content))),
                "encoding": "base64"
              };
            }
  
          _request("POST", repoPath + "/git/blobs", content, function(err, res) {
            if (err) return cb(err);
            cb(null, res.sha);
          });
        };
  
        // Update an existing tree adding a new blob object getting a tree SHA back
        // -------
  
        this.updateTree = function(baseTree, path, blob, cb) {
          var data = {
            "base_tree": baseTree,
            "tree": [
              {
                "path": path,
                "mode": "100644",
                "type": "blob",
                "sha": blob
              }
            ]
          };
          _request("POST", repoPath + "/git/trees", data, function(err, res) {
            if (err) return cb(err);
            cb(null, res.sha);
          });
        };
  
        // Post a new tree object having a file path pointer replaced
        // with a new blob SHA getting a tree SHA back
        // -------
  
        this.postTree = function(tree, cb) {
          _request("POST", repoPath + "/git/trees", { "tree": tree }, function(err, res) {
            if (err) return cb(err);
            cb(null, res.sha);
          });
        };
  
        // Create a new commit object with the current commit SHA as the parent
        // and the new tree SHA, getting a commit SHA back
        // -------
  
        this.commit = function(parent, tree, message, cb) {
          var user = new Github.User();
          user.show(null, function(err, userData){
            if (err) return cb(err);
            var data = {
              "message": message,
              "author": {
                "name": options.user,
                "email": userData.email
              },
              "parents": [
                parent
              ],
              "tree": tree
            };
            _request("POST", repoPath + "/git/commits", data, function(err, res) {
              if (err) return cb(err);
              currentTree.sha = res.sha; // update latest commit
              cb(null, res.sha);
            });
          });
        };
  
        // Update the reference of your head to point to the new commit SHA
        // -------
  
        this.updateHead = function(head, commit, cb) {
          _request("PATCH", repoPath + "/git/refs/heads/" + head, { "sha": commit }, function(err, res) {
            cb(err);
          });
        };
  
        // Show repository information
        // -------
  
        this.show = function(cb) {
          _request("GET", repoPath, null, cb);
        };
  
        // Get contents
        // --------
  
        this.contents = function(ref, path, cb) {
          _request("GET", repoPath + "/contents/"+path, { ref: ref }, cb);
        };
  
        // Fork repository
        // -------
  
        this.fork = function(cb) {
          _request("POST", repoPath + "/forks", null, cb);
        };
  
        // Branch repository
        // --------
  
        this.branch = function(oldBranch,newBranch,cb) {
          if(arguments.length === 2 && typeof arguments[1] === "function") {
            cb = newBranch;
            newBranch = oldBranch;
            oldBranch = "master";
          }
          this.getRef("heads/" + oldBranch, function(err,ref) {
            if(err && cb) return cb(err);
            that.createRef({
              ref: "refs/heads/" + newBranch,
              sha: ref
            },cb);
          });
        };
  
        // Create pull request
        // --------
  
        this.createPullRequest = function(options, cb) {
          _request("POST", repoPath + "/pulls", options, cb);
        };
  
        // List hooks
        // --------
  
        this.listHooks = function(cb) {
          _request("GET", repoPath + "/hooks", null, cb);
        };
  
        // Get a hook
        // --------
  
        this.getHook = function(id, cb) {
          _request("GET", repoPath + "/hooks/" + id, null, cb);
        };
  
        // Create a hook
        // --------
  
        this.createHook = function(options, cb) {
          _request("POST", repoPath + "/hooks", options, cb);
        };
  
        // Edit a hook
        // --------
  
        this.editHook = function(id, options, cb) {
          _request("PATCH", repoPath + "/hooks/" + id, options, cb);
        };
  
        // Delete a hook
        // --------
  
        this.deleteHook = function(id, cb) {
          _request("DELETE", repoPath + "/hooks/" + id, null, cb);
        };
  
        // Read file at given path
        // -------
  
        this.read = function(branch, path, cb) {
          _request("GET", repoPath + "/contents/"+path, {ref: branch}, function(err, obj) {
            if (err && err.error === 404) return cb("not found", null, null);
  
            if (err) return cb(err);
            cb(null, obj);
          }, true);
        };
  
  
        // Remove a file
        // -------
  
        this.remove = function(branch, path, cb) {
          that.getSha(branch, path, function(err, sha) {
            if (err) return cb(err);
            _request("DELETE", repoPath + "/contents/" + path, {
              message: path + " is removed",
              sha: sha,
              branch: branch
            }, cb);
          });
        };
  
        // Delete a file from the tree
        // -------
  
        this.delete = function(branch, path, cb) {
          that.getSha(branch, path, function(err, sha) {
            if (!sha) return cb("not found", null);
            var delPath = repoPath + "/contents/" + path;
            var params = {
              "message": "Deleted " + path,
              "sha": sha
            };
            delPath += "?message=" + encodeURIComponent(params.message);
            delPath += "&sha=" + encodeURIComponent(params.sha);
            delPath += '&branch=' + encodeURIComponent(branch);
            _request("DELETE", delPath, null, cb);
          });
        };
  
        // Move a file to a new location
        // -------
  
        this.move = function(branch, path, newPath, cb) {
          updateTree(branch, function(err, latestCommit) {
            that.getTree(latestCommit+"?recursive=true", function(err, tree) {
              // Update Tree
              _.each(tree, function(ref) {
                if (ref.path === path) ref.path = newPath;
                if (ref.type === "tree") delete ref.sha;
              });
  
              that.postTree(tree, function(err, rootTree) {
                that.commit(latestCommit, rootTree, 'Deleted '+path , function(err, commit) {
                  that.updateHead(branch, commit, function(err) {
                    cb(err);
                  });
                });
              });
            });
          });
        };
  
        // Write file contents to a given branch and path
        // -------
  
        this.write = function(branch, path, content, message, cb) {
          that.getSha(branch, path, function(err, sha) {
            if (err && err.error!=404) return cb(err);
            _request("PUT", repoPath + "/contents/" + path, {
              message: message,
              content: btoa(content),
              branch: branch,
              sha: sha
            }, cb);
          });
        };
  
        // List commits on a repository. Takes an object of optional paramaters:
        // sha: SHA or branch to start listing commits from
        // path: Only commits containing this file path will be returned
        // since: ISO 8601 date - only commits after this date will be returned
        // until: ISO 8601 date - only commits before this date will be returned
        // -------
  
        this.getCommits = function(options, cb) {
            options = options || {};
            var url = repoPath + "/commits";
            var params = [];
            if (options.sha) {
                params.push("sha=" + encodeURIComponent(options.sha));
            }
            if (options.path) {
                params.push("path=" + encodeURIComponent(options.path));
            }
            if (options.since) {
                var since = options.since;
                if (since.constructor === Date) {
                    since = since.toISOString();
                }
                params.push("since=" + encodeURIComponent(since));
            }
            if (options.until) {
                var until = options.until;
                if (until.constructor === Date) {
                    until = until.toISOString();
                }
                params.push("until=" + encodeURIComponent(until));
            }
            if (options.page) {
                params.push("page=" + options.page);
            }
            if (options.perpage) {
                params.push("per_page=" + options.perpage);
            }
            if (params.length > 0) {
                url += "?" + params.join("&");
            }
            _request("GET", url, null, cb);
        };
      };
  
      // Gists API
      // =======
  
      Github.Gist = function(options) {
        var id = options.id;
        var gistPath = "/gists/"+id;
  
        // Read the gist
        // --------
  
        this.read = function(cb) {
          _request("GET", gistPath, null, function(err, gist) {
            cb(err, gist);
          });
        };
  
        // Create the gist
        // --------
        // {
        //  "description": "the description for this gist",
        //    "public": true,
        //    "files": {
        //      "file1.txt": {
        //        "content": "String file contents"
        //      }
        //    }
        // }
  
        this.create = function(options, cb){
          _request("POST","/gists", options, cb);
        };
  
        // Delete the gist
        // --------
  
        this.delete = function(cb) {
          _request("DELETE", gistPath, null, function(err,res) {
            cb(err,res);
          });
        };
  
        // Fork a gist
        // --------
  
        this.fork = function(cb) {
          _request("POST", gistPath+"/fork", null, function(err,res) {
            cb(err,res);
          });
        };
  
        // Update a gist with the new stuff
        // --------
  
        this.update = function(options, cb) {
          _request("PATCH", gistPath, options, function(err,res) {
            cb(err,res);
          });
        };
  
        // Star a gist
        // --------
  
        this.star = function(cb) {
          _request("PUT", gistPath+"/star", null, function(err,res) {
            cb(err,res);
          });
        };
  
        // Untar a gist
        // --------
  
        this.unstar = function(cb) {
          _request("DELETE", gistPath+"/star", null, function(err,res) {
            cb(err,res);
          });
        };
  
        // Check if a gist is starred
        // --------
  
        this.isStarred = function(cb) {
          _request("GET", gistPath+"/star", null, function(err,res) {
            cb(err,res);
          });
        };
      };
  
      // Issues API
      // ==========
  
      Github.Issue = function(options) {
        var path = "/repos/" + options.user + "/" + options.repo + "/issues";
  
        this.list = function(options, cb) {
          _request("GET", path, options, cb);
        };
      };
  
      // Rate Limit API
      // ==========
  
      Github.RateLimit = function(cb) {
        var path = "/rate_limit";
  
        _request("GET", path, options, cb);
      };
  
      // Top Level API
      // -------
  
      this.getRateLimit = function(cb) {
        return new Github.RateLimit(cb);
      };
  
      this.getIssues = function(user, repo) {
        return new Github.Issue({user: user, repo: repo});
      };
  
      this.getRepo = function(user, repo) {
        return new Github.Repository({user: user, name: repo});
      };
  
      this.getUser = function() {
        return new Github.User();
      };
  
      this.getGist = function(id) {
        return new Github.Gist({id: id});
      };
    };
  
  
    if (typeof exports !== 'undefined') {
      // Github = exports;
      module.exports = Github;
    } else {
      window.Github = Github;
    }
  }).call(this);
  