diff --git a/assets/js/gitter.js b/assets/js/gitter.js
index befb4a7..5bd2e95 100644
--- a/assets/js/gitter.js
+++ b/assets/js/gitter.js
@@ -2,644 +2,765 @@
/// http://github.com/samsonjs/gitter
/// @_sjs
///
-/// Copyright 2010 Sami Samhuri
+/// Copyright 2010 - 2012 Sami Samhuri
/// MIT License
-// TODO:
-// - authentication and write APIs
+(function() {
+ "use strict"
-;(function() {
- var global = this
- , isBrowser = 'document' in global
- , ie
+ var global = (function() { return this || (1, eval)('this') }())
+ , isBrowser = 'document' in global
+ , ie
- if (isBrowser) {
- ie = (function() {
- var undef
- , v = 3
- , div = document.createElement('div')
- , all = div.getElementsByTagName('i')
+ if (isBrowser) {
+ ie = (function() {
+ var undef
+ , v = 3
+ , div = document.createElement('div')
+ , all = div.getElementsByTagName('i')
- while (
- div.innerHTML = '',
- all[0]
- ){/* braces here to satifsy closure-compiler */};
+ while (
+ div.innerHTML = '',
+ all[0]
+ );
- return v > 4 ? v : undef
- }())
+ return v > 4 ? v : undef
+ }())
+ }
+
+ var inherits
+ if ('create' in Object) {
+ // util.inherits from node
+ inherits = function(ctor, superCtor) {
+ ctor.super_ = superCtor
+ ctor.prototype = Object.create(superCtor.prototype, {
+ constructor: {
+ value: ctor,
+ enumerable: false
+ }
+ })
+ }
+ }
+ else if ([].__proto__) {
+ inherits = function(ctor, superCtor) {
+ ctor.super_ = superCtor
+ ctor.prototype.__proto__ = superCtor.prototype
+ ctor.prototype.constructor = ctor
+ }
+ }
+ else { // ie8
+ var __hasProp = Object.prototype.hasOwnProperty
+ inherits = function(child, parent) {
+ for (var key in parent) {
+ if (__hasProp.call(parent, key)) child[key] = parent[key]
+ }
+ function ctor() { this.constructor = child }
+ ctor.prototype = parent.prototype
+ child.prototype = new ctor
+ child.__super__ = parent.prototype
+ return child
+ }
+ }
+
+ var api = {
+ // Blob
+ blob: function(user, repo, sha, cb) {
+ return new Blob(user, repo, sha, cb)
}
- var inherits
- if ('create' in Object) {
- // util.inherits from node
- inherits = function(ctor, superCtor) {
- ctor.super_ = superCtor
- ctor.prototype = Object.create(superCtor.prototype, {
- constructor: {
- value: ctor,
- enumerable: false
- }
- })
- }
- } else if ([].__proto__) {
- inherits = function(ctor, superCtor) {
- ctor.super_ = superCtor
- ctor.prototype.__proto__ = superCtor.prototype
- ctor.prototype.constructor = ctor
- }
- } else {
- // CoffeeScript's ultra-portable inheritance
- var __hasProp = Object.prototype.hasOwnProperty
- inherits = function(child, parent) {
- for (var key in parent) {
- if (__hasProp.call(parent, key)) child[key] = parent[key]
- }
- function ctor() { this.constructor = child }
- ctor.prototype = parent.prototype
- child.prototype = new ctor
- child.__super__ = parent.prototype
- return child
- }
+ // Branch
+ , branch: function(user, repo, name, cb) {
+ return new Branch(user, repo, name, cb)
}
- // when running in the browser request is set later
- var request = isBrowser ? null : require('request')
- , Blob, Branch, Commit, Raw, Repo, Tree, User
- , api
-
- api = {
- blob: function(repo, sha, path, cb) {
- return new Blob(repo, sha, path, cb)
- },
- branch: function(repo, branch, cb) {
- return new Branch(repo, branch, cb)
- },
- commits: function(repo, branch, cb) {
- return new Branch(repo, branch).getCommits(cb)
- },
- commit: function(repo, sha, cb) {
- return new Commit(repo, sha, cb)
- },
- // raw doesn't work with jsonp in the browser ... cors?
- raw: isBrowser ? undefined : function(repo, sha, cb) {
- return new Raw(repo, sha, cb)
- },
- repo: function(repo, cb) {
- return new Repo(repo, cb)
- },
- branches: function(repo, cb) {
- return new Repo(repo).getBranches(cb)
- },
- collaborators: function(repo, cb) {
- return new Repo(repo).getCollaborators(cb)
- },
- contributors: function(repo, cb) {
- return new Repo(repo).getContributors(cb)
- },
- languages: function(repo, cb) {
- return new Repo(repo).getLanguages(cb)
- },
- network: function(repo, cb) {
- return new Repo(repo).getNetwork(cb)
- },
- tags: function(repo, cb) {
- return new Repo(repo).getTags(cb)
- },
- watchers: function(repo, cb) {
- return new Repo(repo).getWatchers(cb)
- },
- tree: function(repo, sha, cb) {
- return new Tree(repo, sha, cb)
- },
- blobs: function(repo, sha, cb) {
- return new Tree(repo, sha).getBlobs(cb)
- },
- user: function(user, cb) {
- return new User(user, cb)
- },
- followers: function(user, cb) {
- return new User(user).getFollowers(cb)
- },
- following: function(user, cb) {
- return new User(user).getFollowing(cb)
- },
- repos: function(user, cb) {
- return new User(user).getRepos(cb)
- },
- watched: function(user, cb) {
- return new User(user).getWatched(cb)
- }
- }
- if (isBrowser) global.GITR = api
- else module.exports = api
-
- if (isBrowser) shim()
-
- // Define resources //
-
- Blob = createResource('blob/show/:repo/:tree/:path', {
- has: [ ['commits', 'commits/list/:repo/:tree/:path'] ]
- })
- Branch = createResource('commits/show/:repo/:branch', {
- has: [ ['commits', 'commits/list/:repo/:branch'] ]
- })
- Commit = createResource('commits/show/:repo/:sha')
- // raw doesn't work with jsonp ... cors?
- if (!isBrowser) Raw = createResource('blob/show/:repo/:sha')
- Repo = createResource('repos/show/:repo', {
- has: [ 'branches'
- , 'collaborators'
- , 'contributors'
- , 'languages'
- , 'network'
- , 'tags'
- , 'watchers'
- ]
- })
- Tree = createResource('tree/show/:repo/:sha', {
- has: [ ['blobs', 'blob/all/:repo/:sha']
- , ['fullBlobs', 'blob/full/:repo/:sha']
- , ['fullTree', 'tree/full/:repo/:sha']
- ]
- })
- Tree.prototype._processData = function(data) {
- var result = Resource.prototype._processData.call(this, data)
- this.blobs = this.data()
- return result
+ // Commit
+ , commit: function(user, repo, sha, cb) {
+ return new Commit(user, repo, sha, cb)
}
- User = createResource('user/show/:user', {
- has: [ 'followers'
- , 'following'
- , ['repos', 'repos/show/:user']
- , ['watched', 'repos/watched/:user']
- ]
- })
+ // Download
+ , download: function(user, repo, id, cb) {
+ return new Download(user, repo, id, cb)
+ }
- // Construct a new github resource.
- //
- // options:
- // - params: params for constructor (optional, inferred from route if missing)
- // - has: list of related resources, accessors are created for each item
- //
- // The members of the `has` list are arrays of the form [name, route, unpack].
- // The first member, name, is used to create an accessor (e.g. getName), and
- // is required.
- //
- // Route and unpack are optional. Route specifies the endpoint for this
- // resource and defaults to the name appended to the main resource's endpoint.
- //
- // Unpack is a function that extracts the desired value from the object fetched
- // for this resource. It defaults to a function that picks out the only property
- // from an object, or returns the entire walue if not an object or it contains
- // more than one property.
- //
- // When passing only the name you may pass it directly without wrapping it in
- // an array.
- function createResource(route, options) {
- if (!route) throw new Error('route is required')
- options = options || {}
+ // Issue
+ , issue: function(user, repo, id, cb) {
+ return new Issue(user, repo, id, cb)
+ }
- var resource = function() { Resource.apply(this, [].slice.call(arguments)) }
- inherits(resource, Resource)
+ // Organization
+ , org: function(name, cb) {
+ return new Org(name, cb)
+ }
+ , members: function(name, cb) {
+ return new Org(name).fetchMembers(cb)
+ }
- resource.prototype._route = route
- resource.prototype._params = options.params || paramsFromRoute(route)
+ // Repo
+ , repo: function(user, repo, cb) {
+ return new Repo(user, repo, cb)
+ }
+ , branches: function(user, repo, cb) {
+ return new Repo(user, repo).fetchBranches(cb)
+ }
+ , collaborators: function(user, repo, cb) {
+ return new Repo(user, repo).fetchCollaborators(cb)
+ }
+ , contributors: function(user, repo, cb) {
+ return new Repo(user, repo).fetchContributors(cb)
+ }
+ , downloads: function(user, repo, cb) {
+ return new Repo(user, repo).fetchDownloads(cb)
+ }
+ , forks: function(user, repo, cb) {
+ return new Repo(user, repo).fetchForks(cb)
+ }
+ , issues: function(user, repo, cb) {
+ return new Repo(user, repo).fetchIssues(cb)
+ }
+ , languages: function(user, repo, cb) {
+ return new Repo(user, repo).fetchLanguages(cb)
+ }
+ , tags: function(user, repo, cb) {
+ return new Repo(user, repo).fetchTags(cb)
+ }
+ , watchers: function(user, repo, cb) {
+ return new Repo(user, repo).fetchWatchers(cb)
+ }
- resource.has = function(prop, route, unpack) {
- unpack = unpack || onlyProp
- var dataProp = '_' + prop
- , fn = 'get' + titleCaseFirst(prop)
- , processData = function(d) {
- if (ie < 9) {
- this[dataProp] = camelize(unpack(d))
- } else {
- getter(this, dataProp, function() { return camelize(unpack(d))})
- }
- }
- , result = function(resource) { return resource[dataProp] }
- resource.prototype[fn] = function(cb, force) {
- return this._fetch({ prop: dataProp
- , route: route || this._route + '/' + prop
- , processData: processData.bind(this)
- , result: result
- }, cb.bind(this), force)
- }
- return resource
- }
- if (options.has) options.has.forEach(function(args) {
- resource.has.apply(resource, Array.isArray(args) ? args : [args])
+ , ref: function(user, repo, name, cb) {
+ return new Ref(user, repo, name, cb)
+ }
+
+ // Tag
+ , tag: function(user, repo, name, cb) {
+ return new Tag(user, repo, name, cb)
+ }
+
+ // Tree
+ , tree: function(user, repo, sha, cb) {
+ return new Tree(user, repo, sha, cb)
+ }
+
+ // User
+ , user: function(login, cb) {
+ return new User(login, cb)
+ }
+ , followers: function(login, cb) {
+ return new User(login).fetchFollowers(cb)
+ }
+ , following: function(login, cb) {
+ return new User(login).fetchFollowing(cb)
+ }
+ , repos: function(login, cb) {
+ return new User(login).fetchRepos(cb)
+ }
+ , watched: function(login, cb) {
+ return new User(login).fetchWatched(cb)
+ }
+
+ // Why not, expose the resources directly as well.
+ , Blob: Blob
+ , Branch: Branch
+ , Commit: Commit
+ , Download: Download
+ , Issue: Issue
+ , Org: Org
+ , Ref: Ref
+ , Repo: Repo
+ , Tree: Tree
+ , User: User
+ }
+
+ // when running in the browser request is set later, in shim()
+ var request
+
+ if (isBrowser) {
+ shim()
+ global.GITR = api
+ }
+ else {
+ var https = require('https')
+ request = function(options, cb) {
+ var req = https.request(options, function(response) {
+ var bodyParts = []
+ response.on('data', function(b) { bodyParts.push(b) })
+ response.on('end', function() {
+ var body = bodyParts.join('')
+ if (response.statusCode === 200) {
+ cb(null, body, response)
+ }
+ else {
+ console.dir(options, response, body)
+ var err = new Error('http error')
+ err.statusCode = response.statusCode
+ err.body = body
+ cb(err)
+ }
})
-
- return resource
+ })
+ req.end()
+ req.on('error', function(err) { cb(err) })
}
+ module.exports = api
+ }
- // Assigns the given resource args to the new instance. Sets the path to the
- // endpoint for main resource data.
- //
- // If the optional last arg is a function main data is fetched immediately,
- // and that function is used as the callback.
- //
- // If the optional last arg is an object then it is set as the main resource
- // data.
- function Resource(/* ...args, opt: data or callback */) {
- var args = [].slice.call(arguments)
- , last = args[args.length - 1]
- // assign params from args
- this._params.forEach(function(param, i) {
- this[param] = args[i]
- }.bind(this))
+ // Generic Resource //
+ //
+ // Used as the prototype by createResource. Provides
+ // methods for fetching the resource and related
+ // sub-resources.
+ function Resource() {}
- // set the resource path
- this.urlPath = this.resolve(this._route)
-
- if (typeof last === 'function') this.fetch(last)
- else if (typeof last === 'object') this.data(last)
+ // Fetch data for this resource and pass it to the
+ // callback after mixing the data into the object.
+ // Data is also available via the `data` property.
+ Resource.prototype.fetch = function(cb) {
+ if (this.data) {
+ cb(null, this.data)
}
-
- // Set or get main data for this resource, or fetch
- // a specific property from the data.
- //
- // When the data param is empty cached data is returned.
- //
- // When the data param is a string the property by that name
- // is looked up in the cached data.
- //
- // Otherwise cached data is set to the data param.
- Resource.prototype.data = function(data) {
- if (!data) return this._data
- if (typeof data === 'string' && typeof this._data === 'object') return this._data[data]
-
- getter(this, '_data', function() { return data }, {configurable: true})
- return this
+ else {
+ var self = this
+ fetch(this.path, function(err, data) {
+ // console.log('FETCH', self.path, err, data)
+ if (err) {
+ // console.log(err)
+ }
+ else {
+ self.data = data
+ mixin(self, data)
+ }
+ if (typeof cb === 'function') {
+ cb.call(self, err, data)
+ }
+ })
}
+ return this
+ }
- // Fetch the main data for this resource.
- //
- // cb: callback(err, data)
- // force: if true load data from github, bypassing the local cache
- Resource.prototype.fetch = function(cb, force) {
- return this._fetch({ prop: '_data'
- , route: this.urlPath
- , processData: this._processData.bind(this)
- , result: function(resource) { return resource }
- }, cb.bind(this), force)
+ Resource.prototype.fetchSubResource = function(thing, cb) {
+ if (this['_' + thing]) {
+ cb(null, this['_' + thing])
}
-
- // 'repos/show/:user/:repo/branches' -> 'repos/show/samsonjs/gitter
- Resource.prototype.resolve = function(route) { // based on crock's supplant
- if (route.indexOf(':') < 0) return route
- return route.replace(/:(\w+)\b/g, function (s, prop) {
- var val = this[prop]
- if (typeof val !== 'string' && typeof val !== 'number')
- throw new Error('no suitable property named "' + prop + '" (found ' + val + ')')
- return val
- }.bind(this))
+ else {
+ var self = this
+ fetch(this.path + '/' + thing, function(err, data) {
+ // console.log('FETCH SUBRESOURCE', self.path, thing, err, data)
+ if (err) {
+ // console.log(self.path, err)
+ }
+ else {
+ self['_' + thing] = data
+ }
+ if (typeof cb === 'function') {
+ cb.call(self, err, data)
+ }
+ })
}
+ return this
+ }
- // Fetch arbitrary data from github.
- //
- // options:
- // - prop: name of data cache property
- // - route: route to github endpoint (can contain resource params)
- // - processData: function that processes fetched data
- // - result: function to obtain the result passed to the callback
- // cb: callback(err, data)
- // force: if true load data from github, bypassing the local cache
- Resource.prototype._fetch = function(options, cb, force) {
- if (!force && this[options.prop]) {
- cb(null, options.result(this))
- return this
+ var __slice = Array.prototype.slice
+
+ // Create a resource w/ Resource as the prototype.
+ //
+ // spec: an object with the following properties:
+ //
+ // - constructor: a constructor function
+ // - has: a list of resources belonging to this resource
+ //
+ // Typically the constructor accepts one or more arguments specifying
+ // the name or pieces of info identifying the specific resource and
+ // used to build the URL to fetch it. It also accepts an optional
+ // callback as the last parameter.
+ //
+ // The constructor must set the `path` property which is used to
+ // fetch the resource.
+ //
+ // If a callback is provided then the resource is immediately
+ // fetched and the callback is threaded through to the `fetch`
+ // method. The callback function has the signature
+ // `function(err, data)`.
+ //
+ // The `has` list specifies sub-resources, e.g. a user has repos,
+ // followers, etc. An organization has members.
+ //
+ // Each related sub-resource gets a method named appropriately,
+ // e.g. the User resource has followers so User objects have a
+ // `fetchFollowers` method.
+ function createResource(spec) {
+ var subResources = spec.has ? __slice.call(spec.has) : null
+ , resource = function(/* ..., cb */) {
+ var args = __slice.call(arguments)
+ , lastArgIsCallback = typeof args[args.length - 1] === 'function'
+ , cb = lastArgIsCallback ? args.pop() : null
+ , result = spec.constructor.apply(this, args)
+
+ if (typeof cb === 'function') {
+ this.fetch(cb)
+ }
+
+ return result
}
- // Interpolate resource params
- var path = this.resolve(options.route)
+ inherits(resource, Resource)
- // Make the request
- return this._get(path, function(err, data) {
- if (err) {
- cb(err)
- return
+ if (subResources) {
+ subResources.forEach(function(thing) {
+ var fnName = 'fetch' + toTitleCase(thing)
+ resource.prototype[fnName] = function(cb) {
+ return this.fetchSubResource(thing, cb)
+ }
+ })
+ }
+
+ return resource
+ }
+
+
+ // Define Resources //
+
+ var Blob = createResource({
+ constructor: function(user, repo, sha) {
+ this.user = user
+ this.repo = repo
+ this.sha = sha
+ this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/') + '/git/blobs/' + sha
+ }
+ })
+
+ var Branch = createResource({
+ constructor: function (user, repo, name) {
+ this.user = user
+ this.repo = repo
+ this.name = name
+ this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/') + '/git/refs/heads/' + name
+ }
+ })
+
+ var Commit = createResource({
+ constructor: function Commit(user, repo, sha) {
+ this.user = user
+ this.repo = repo
+ this.sha = sha
+ this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/') + '/git/commits/' + sha
+ }
+ })
+
+ var Download = createResource({
+ constructor: function(user, repo, id) {
+ this.user = user
+ this.repo = repo
+ this.id = id
+ this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/') + '/downloads/' + id
+ }
+ })
+
+ var Issue = createResource({
+ constructor: function(user, repo, id) {
+ this.user = user
+ this.repo = repo
+ this.id = id
+ this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/') + '/issues/' + id
+ }
+ })
+
+ var Org = createResource({
+ constructor: function(name) {
+ this.name = name
+ this.path = '/orgs/' + encodeURIComponent(nam)
+ }
+
+ , has: 'members repos'.split(' ')
+ })
+
+ var Ref = createResource({
+ constructor: function (user, repo, name) {
+ this.user = user
+ this.repo = repo
+ this.name = name
+ this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/') + '/git/refs/' + name
+ }
+ })
+
+ var Repo = createResource({
+ constructor: function(user, repo) {
+ this.user = user
+ this.repo = repo
+ this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/')
+ }
+
+ , has: ('branches collaborators contributors downloads' +
+ ' forks languages tags teams watchers').split(' ')
+ })
+
+ var Tag = createResource({
+ constructor: function (user, repo, name) {
+ this.user = user
+ this.repo = repo
+ this.name = name
+ this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/') + '/git/refs/tags/' + name
+ }
+ })
+
+ var Tree = createResource({
+ constructor: function(user, repo, sha) {
+ this.user = user
+ this.repo = repo
+ this.sha = sha
+ this.path = '/repos/' + [user, repo].map(encodeURIComponent).join('/') + '/git/trees/' + sha
+ }
+ })
+
+ var User = createResource({
+ constructor: function(login) {
+ // Allow creating a user from an object returned by the API
+ if (login.login) {
+ login = login.login
+ }
+ this.login = login
+ this.path = '/users/' + encodeURIComponent(login)
+ }
+
+ , has: 'followers following repos watched'.split(' ')
+ })
+
+
+ // Fetch data from github. JSON is parsed and keys are camelized.
+ //
+ // path: the path to the resource
+ // cb: callback(err, data)
+ function fetch(path, cb) {
+ request({ host: 'api.github.com', path: path }, function(err, body, response) {
+ // JSONP requests in the browser return the object directly
+ var data = body
+
+ // Requests in Node return json text, try to parse it
+ if (response && /json/i.exec(response.headers['content-type'])) {
+ try {
+ data = JSON.parse(body)
+ }
+ catch (e) {
+ err = e
+ data = null
+ }
+ }
+
+ cb(err, camelize(data))
+ })
+ }
+
+ // created_at => createdAt
+ function camel(s) {
+ return s.replace(/_(.)/g, function(_, l) { return l.toUpperCase() })
+ }
+
+ // camelize all keys of an object, or all objects in an array
+ function camelize(obj) {
+ if (!obj || typeof obj === 'string') return obj
+ if (Array.isArray(obj)) return obj.map(camelize)
+ if (typeof obj === 'object') {
+ return Object.keys(obj).reduce(function(camelizedObj, k) {
+ camelizedObj[camel(k)] = camelize(obj[k])
+ return camelizedObj
+ }, {})
+ }
+ return obj
+ }
+
+ function toTitleCase(s) {
+ return s.charAt(0).toUpperCase() + s.slice(1)
+ }
+
+ function mixin(a, b) {
+ for (var k in b) {
+ if (b.hasOwnProperty(k)) a[k] = b[k]
+ }
+ }
+
+
+ // Browser Utilities //
+
+ function shim() {
+ shimBind()
+ shimES5()
+ shimRequest()
+ }
+
+ function shimBind() {
+ // bind from Prototype
+ if (!Function.prototype.bind) {
+ (function(){
+ function update(array, args) {
+ var arrayLength = array.length, length = args.length
+ while (length--) array[arrayLength + length] = args[length]
+ return array
+ }
+ function merge(array, args) {
+ array = __slice.call(array, 0)
+ return update(array, args)
+ }
+ Function.prototype.bind = function(context) {
+ if (arguments.length < 2 && typeof arguments[0] === 'undefined') return this
+ var __method = this, args = __slice.call(arguments, 1)
+ return function() {
+ var a = merge(args, arguments)
+ return __method.apply(context, a)
+ }
+ }
+ }())
+ }
+ }
+
+ // a few functions from Kris Kowal's es5-shim
+ // https://github.com/kriskowal/es5-shim
+ function shimES5() {
+ var has = Object.prototype.hasOwnProperty
+
+ // ES5 15.2.3.6
+ if (!Object.defineProperty || ie === 8) { // ie8
+ Object.defineProperty = function(object, property, descriptor) {
+ if (typeof descriptor == "object" && object.__defineGetter__) {
+ if (has.call(descriptor, "value")) {
+ if (!object.__lookupGetter__(property) && !object.__lookupSetter__(property)) {
+ // data property defined and no pre-existing accessors
+ object[property] = descriptor.value
}
- options.processData(data)
- cb(null, options.result(this))
- }.bind(this))
+ if (has.call(descriptor, "get") || has.call(descriptor, "set")) {
+ // descriptor has a value property but accessor already exists
+ throw new TypeError("Object doesn't support this action")
+ }
+ }
+ // fail silently if "writable", "enumerable", or "configurable"
+ // are requested but not supported
+ else if (typeof descriptor.get == "function") {
+ object.__defineGetter__(property, descriptor.get)
+ }
+ if (typeof descriptor.set == "function") {
+ object.__defineSetter__(property, descriptor.set)
+ }
+ }
+ return object
+ }
}
- // Fetch data from github. JSON responses are parsed.
+ // ES5 15.2.3.14
+ // http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation
+ if (!Object.keys) { // ie8
+ (function() {
+ var hasDontEnumBug = true,
+ dontEnums = [
+ 'toString',
+ 'toLocaleString',
+ 'valueOf',
+ 'hasOwnProperty',
+ 'isPrototypeOf',
+ 'propertyIsEnumerable',
+ 'constructor'
+ ],
+ dontEnumsLength = dontEnums.length
+
+ for (var key in {"toString": null}) {
+ hasDontEnumBug = false
+ }
+
+ Object.keys = function (object) {
+
+ if (
+ typeof object !== "object" && typeof object !== "function"
+ || object === null
+ )
+ throw new TypeError("Object.keys called on a non-object")
+
+ var keys = []
+ for (var name in object) {
+ if (has.call(object, name)) {
+ keys.push(name)
+ }
+ }
+
+ if (hasDontEnumBug) {
+ for (var i = 0, ii = dontEnumsLength; i < ii; i++) {
+ var dontEnum = dontEnums[i]
+ if (has.call(object, dontEnum)) {
+ keys.push(dontEnum)
+ }
+ }
+ }
+
+ return keys
+ }
+ }())
+ } // Object.keys
+
//
- // path: github endpoint
- // cb: callback(err, data)
- Resource.prototype._get = function(path, cb) {
- request({uri: 'http://github.com/api/v2/json/' + path}, function(err, response, body) {
- if (err)
- cb(err)
- else if (isBrowser)
- cb(null, body) // body is an object
- else if (response.statusCode !== 200)
- cb(new Error('failed to fetch ' + path + ': ' + response.statusCode))
- else if (response.headers['content-type'].match(/json/))
- cb(null, JSON.parse(body))
- else
- cb(null, body)
- })
- return this
+ // Array
+ // =====
+ //
+
+ // ES5 15.4.3.2
+ if (!Array.isArray) {
+ Array.isArray = function(obj) {
+ return Object.prototype.toString.call(obj) == "[object Array]"
+ }
}
- // Descendents of Resource can overwrite _processData and _unpack to process
- // the main resource data differently.
-
- Resource.prototype._processData = function(data) {
- return this.data(camelize(this._unpack(data)))
- }
- Resource.prototype._unpack = onlyProp
-
-
- // Utilities //
-
- function camel(s) { // created_at => createdAt
- return s.replace(/_(.)/g, function(_, l) { return l.toUpperCase() })
- }
- function camelize(obj) { // camelize all keys of an object, or all objects in an array
- if (!obj || typeof obj === 'string') return obj
- if (Array.isArray(obj)) return obj.map(camelize)
- return Object.keys(obj).reduce(function(newObj, k) {
- newObj[camel(k)] = obj[k]
- return newObj
- }, {})
+ // ES5 15.4.4.18
+ if (!Array.prototype.forEach) { // ie8
+ Array.prototype.forEach = function(block, thisObject) {
+ var len = this.length >>> 0
+ for (var i = 0; i < len; i++) {
+ if (i in this) {
+ block.call(thisObject, this[i], i, this)
+ }
+ }
+ }
}
- function getter(obj, prop, fn, opts) { // minor convenience
- opts = opts || {}
- opts.get = fn
- Object.defineProperty(obj, prop, opts)
+ // ES5 15.4.4.19
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map
+ if (!Array.prototype.map) { // ie8
+ Array.prototype.map = function(fun /*, thisp*/) {
+ var len = this.length >>> 0
+ if (typeof fun != "function") {
+ throw new TypeError()
+ }
+
+ var res = new Array(len)
+ var thisp = arguments[1]
+ for (var i = 0; i < len; i++) {
+ if (i in this) {
+ res[i] = fun.call(thisp, this[i], i, this)
+ }
+ }
+
+ return res
+ }
}
- // get an only property, if any
- function onlyProp(obj) {
- if (obj && typeof obj === 'object') {
- var keys = Object.keys(obj)
- if (keys.length === 1) return obj[keys[0]]
+ // ES5 15.4.4.20
+ if (!Array.prototype.filter) { // ie8
+ Array.prototype.filter = function (block /*, thisp */) {
+ var values = []
+ , thisp = arguments[1]
+ for (var i = 0; i < this.length; i++) {
+ if (block.call(thisp, this[i])) {
+ values.push(this[i])
+ }
}
- return obj
+ return values
+ }
}
- // 'repos/show/:user/:repo/branches' -> ['user', 'repo']
- function paramsFromRoute(route) {
- if (route.indexOf(':') === -1) return []
- return route.split('/')
- .filter(function(s) { return s.charAt(0) === ':' })
- .map(function(s) { return s.slice(1) })
- }
-
- function titleCaseFirst(s) { return s.charAt(0).toUpperCase() + s.slice(1) }
-
-
- // Browser Utilities //
-
- function shim() {
- // bind from Prototype
- if (!Function.prototype.bind) {
- (function(){
- function update(array, args) {
- var arrayLength = array.length, length = args.length
- while (length--) array[arrayLength + length] = args[length]
- return array
- }
- function merge(array, args) {
- array = [].slice.call(array, 0)
- return update(array, args)
- }
- Function.prototype.bind = function(context) {
- if (arguments.length < 2 && typeof arguments[0] === 'undefined') return this
- var __method = this, args = [].slice.call(arguments, 1)
- return function() {
- var a = merge(args, arguments)
- return __method.apply(context, a)
- }
- }
- }())
+ // ES5 15.4.4.21
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce
+ if (!Array.prototype.reduce) { // ie8
+ Array.prototype.reduce = function(fun /*, initial*/) {
+ var len = this.length >>> 0
+ if (typeof fun != "function") {
+ throw new TypeError()
}
- // a few functions from Kris Kowal's es5-shim
- // https://github.com/kriskowal/es5-shim
-
- var has = Object.prototype.hasOwnProperty;
-
- // ES5 15.2.3.6
- if (!Object.defineProperty || ie === 8) {
- Object.defineProperty = function(object, property, descriptor) {
- if (typeof descriptor == "object" && object.__defineGetter__) {
- if (has.call(descriptor, "value")) {
- if (!object.__lookupGetter__(property) && !object.__lookupSetter__(property))
- // data property defined and no pre-existing accessors
- object[property] = descriptor.value;
- if (has.call(descriptor, "get") || has.call(descriptor, "set"))
- // descriptor has a value property but accessor already exists
- throw new TypeError("Object doesn't support this action");
- }
- // fail silently if "writable", "enumerable", or "configurable"
- // are requested but not supported
- else if (typeof descriptor.get == "function")
- object.__defineGetter__(property, descriptor.get);
- if (typeof descriptor.set == "function")
- object.__defineSetter__(property, descriptor.set);
- }
- return object;
- };
+ // no value to return if no initial value and an empty array
+ if (len == 0 && arguments.length == 1) {
+ throw new TypeError()
}
- // ES5 15.2.3.14
- // http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation
- if (!Object.keys) {
- (function() {
- var hasDontEnumBug = true,
- dontEnums = [
- 'toString',
- 'toLocaleString',
- 'valueOf',
- 'hasOwnProperty',
- 'isPrototypeOf',
- 'propertyIsEnumerable',
- 'constructor'
- ],
- dontEnumsLength = dontEnums.length;
-
- for (var key in {"toString": null})
- hasDontEnumBug = false;
-
- Object.keys = function (object) {
-
- if (
- typeof object !== "object" && typeof object !== "function"
- || object === null
- )
- throw new TypeError("Object.keys called on a non-object");
-
- var keys = [];
- for (var name in object) {
- if (has.call(object, name)) {
- keys.push(name);
- }
- }
-
- if (hasDontEnumBug) {
- for (var i = 0, ii = dontEnumsLength; i < ii; i++) {
- var dontEnum = dontEnums[i];
- if (has.call(object, dontEnum)) {
- keys.push(dontEnum);
- }
- }
- }
-
- return keys;
- };
- }())
- }
-
- //
- // Array
- // =====
- //
-
- // ES5 15.4.3.2
- if (!Array.isArray) {
- Array.isArray = function(obj) {
- return Object.prototype.toString.call(obj) == "[object Array]";
- };
- }
-
- // ES5 15.4.4.18
- if (!Array.prototype.forEach) {
- Array.prototype.forEach = function(block, thisObject) {
- var len = this.length >>> 0;
- for (var i = 0; i < len; i++) {
- if (i in this) {
- block.call(thisObject, this[i], i, this);
- }
- }
- };
- }
-
- // ES5 15.4.4.19
- // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map
- if (!Array.prototype.map) {
- Array.prototype.map = function(fun /*, thisp*/) {
- var len = this.length >>> 0;
- if (typeof fun != "function")
- throw new TypeError();
-
- var res = new Array(len);
- var thisp = arguments[1];
- for (var i = 0; i < len; i++) {
- if (i in this)
- res[i] = fun.call(thisp, this[i], i, this);
- }
-
- return res;
- };
- }
-
- // ES5 15.4.4.20
- if (!Array.prototype.filter) {
- Array.prototype.filter = function (block /*, thisp */) {
- var values = [];
- var thisp = arguments[1];
- for (var i = 0; i < this.length; i++)
- if (block.call(thisp, this[i]))
- values.push(this[i]);
- return values;
- };
- }
-
- // ES5 15.4.4.21
- // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce
- if (!Array.prototype.reduce) {
- Array.prototype.reduce = function(fun /*, initial*/) {
- var len = this.length >>> 0;
- if (typeof fun != "function")
- throw new TypeError();
-
- // no value to return if no initial value and an empty array
- if (len == 0 && arguments.length == 1)
- throw new TypeError();
-
- var i = 0;
- if (arguments.length >= 2) {
- var rv = arguments[1];
- } else {
- do {
- if (i in this) {
- rv = this[i++];
- break;
- }
-
- // if array contains no values, no initial value to return
- if (++i >= len)
- throw new TypeError();
- } while (true);
- }
-
- for (; i < len; i++) {
- if (i in this)
- rv = fun.call(null, rv, this[i], i, this);
- }
-
- return rv;
- };
- }
-
- var load, _jsonpCounter = 1
- request = function(options, cb) { // jsonp request, quacks like mikeal's request module
- var jsonpCallbackName = '_jsonpCallback' + _jsonpCounter++
- , url = options.uri + '?callback=GITR.' + jsonpCallbackName
- GITR[jsonpCallbackName] = function(obj) {
- cb(null, null, obj)
- delete GITR[jsonpCallbackName]
+ var i = 0
+ if (arguments.length >= 2) {
+ var rv = arguments[1]
+ } else {
+ do {
+ if (i in this) {
+ rv = this[i++]
+ break
}
- load(url)
- }
- // bootstrap loader from LABjs
- load = function(url) {
- var oDOC = document
- , handler
- , head = oDOC.head || oDOC.getElementsByTagName("head")
-
- // loading code borrowed directly from LABjs itself
- // (now removes script elem when done and nullifies its reference --sjs)
- setTimeout(function () {
- if ("item" in head) { // check if ref is still a live node list
- if (!head[0]) { // append_to node not yet ready
- setTimeout(arguments.callee, 25)
- return
- }
- head = head[0]; // reassign from live node list ref to pure node ref -- avoids nasty IE bug where changes to DOM invalidate live node lists
- }
- var scriptElem = oDOC.createElement("script"),
- scriptdone = false
- scriptElem.onload = scriptElem.onreadystatechange = function () {
- if ((scriptElem.readyState && scriptElem.readyState !== "complete" && scriptElem.readyState !== "loaded") || scriptdone) {
- return false
- }
- scriptElem.onload = scriptElem.onreadystatechange = null
- scriptElem.parentNode.removeChild(scriptElem)
- scriptElem = null
- scriptdone = true
- };
- scriptElem.src = url
- head.insertBefore(scriptElem, head.firstChild)
- }, 0)
-
- // required: shim for FF <= 3.5 not having document.readyState
- if (oDOC.readyState == null && oDOC.addEventListener) {
- oDOC.readyState = "loading"
- oDOC.addEventListener("DOMContentLoaded", function handler() {
- oDOC.removeEventListener("DOMContentLoaded", handler, false)
- oDOC.readyState = "complete"
- }, false)
+ // if array contains no values, no initial value to return
+ if (++i >= len) {
+ throw new TypeError()
}
+ } while (true)
}
+
+ for (; i < len; i++) {
+ if (i in this) {
+ rv = fun.call(null, rv, this[i], i, this)
+ }
+ }
+
+ return rv
+ }
+ } // Array.prototype.reduce
+ } // function shimES5()
+
+ // jsonp request, quacks like node's http.request
+ function shimRequest() {
+ var load, _jsonpCounter = 1
+
+ // request is declared earlier
+ request = function(options, cb) {
+ var jsonpCallbackName = '_jsonpCallback' + _jsonpCounter++
+ , url = 'https://' + options.host + options.path + '?callback=GITR.' + jsonpCallbackName
+ GITR[jsonpCallbackName] = function(response) {
+ if (response.meta.status >= 200 && response.meta.status < 300) {
+ cb(null, response.data)
+ }
+ else {
+ var err = new Error('http error')
+ err.statusCode = response.meta.status
+ err.response = response
+ cb(err)
+ }
+ setTimeout(function() { delete GITR[jsonpCallbackName] }, 0)
+ }
+ load(url)
}
-}());
+
+ // bootstrap loader from LABjs (load is declared earlier)
+ load = function(url) {
+ var oDOC = document
+ , handler
+ , head = oDOC.head || oDOC.getElementsByTagName("head")
+
+ // loading code borrowed directly from LABjs itself
+ // (now removes script elem when done and nullifies its reference --sjs)
+ setTimeout(function () {
+ if ("item" in head) { // check if ref is still a live node list
+ if (!head[0]) { // append_to node not yet ready
+ setTimeout(arguments.callee, 25)
+ return
+ }
+ head = head[0]; // reassign from live node list ref to pure node ref -- avoids nasty IE bug where changes to DOM invalidate live node lists
+ }
+ var scriptElem = oDOC.createElement("script")
+ , scriptdone = false
+
+ scriptElem.onload = scriptElem.onreadystatechange = function () {
+ if ((scriptElem.readyState && scriptElem.readyState !== "complete" && scriptElem.readyState !== "loaded") || scriptdone) {
+ return false
+ }
+ scriptElem.onload = scriptElem.onreadystatechange = null
+ scriptElem.parentNode.removeChild(scriptElem)
+ scriptElem = null
+ scriptdone = true
+ }
+ scriptElem.src = url
+ head.insertBefore(scriptElem, head.firstChild)
+ }, 0) // setTimeout
+
+ // required: shim for FF <= 3.5 not having document.readyState
+ if (oDOC.readyState == null && oDOC.addEventListener) {
+ oDOC.readyState = "loading"
+ oDOC.addEventListener("DOMContentLoaded", function handler() {
+ oDOC.removeEventListener("DOMContentLoaded", handler, false)
+ oDOC.readyState = "complete"
+ }, false)
+ }
+
+ } // function load(url)
+
+ } // function shimRequest()
+
+}())
diff --git a/assets/js/proj.js b/assets/js/proj.js
index 1a66f5c..59fab02 100644
--- a/assets/js/proj.js
+++ b/assets/js/proj.js
@@ -57,9 +57,9 @@
function updateBranches(name, branches) {
function branchLink(b) {
- return '' + b + ''
+ return '' + b.name + ''
}
- html('branches', listify(Object.keys(branches).map(branchLink)))
+ html('branches', listify(branches.map(branchLink)))
}
function updateContributors(contributors) {
@@ -82,8 +82,8 @@
if (!t || +new Date() - t > 3600 * 1000) {
console.log('stale ' + String(t))
data.set('t-' + name, +new Date())
- GITR.repo('samsonjs/' + name)
- .getBranches(function(err, branches) {
+ GITR.repo('samsonjs', name)
+ .fetchBranches(function(err, branches) {
if (err) {
text('branches', '(oops)')
} else {
@@ -91,7 +91,7 @@
updateBranches(name, branches)
}
})
- .getLanguages(function(err, langs) {
+ .fetchLanguages(function(err, langs) {
if (err) {
text('langs', '(oops)')
return
@@ -99,7 +99,7 @@
data.set('langs', langs)
updateLangs(langs)
})
- .getContributors(function(err, users) {
+ .fetchContributors(function(err, users) {
if (err) {
text('contributors', '(oops)')
} else {
@@ -107,7 +107,7 @@
updateContributors(users)
}
})
- .getWatchers(function(err, users) {
+ .fetchWatchers(function(err, users) {
if (err) {
text('nwatchers', '?')
} else {
@@ -115,7 +115,7 @@
updateN('watchers', users)
}
})
- .getNetwork(function(err, repos) {
+ .fetchForks(function(err, repos) {
if (err) {
text('nforks', '?')
} else {
diff --git a/templates/proj/index.html b/templates/proj/index.html
index 25597eb..c7096d7 100644
--- a/templates/proj/index.html
+++ b/templates/proj/index.html
@@ -40,12 +40,12 @@
- projects |
+ projects |
— |
followers |
- watching
+ | gists
| — |
following  |
@@ -122,30 +122,32 @@ if (typeof console === 'undefined') {
document.getElementById(id).className = ' highlight'
}
function updateN(name, things) {
- textHighlight('n' + name, things.length)
+ textHighlight('n' + name, things)
}
var data = createObjectStore('proj')
, t = data.get('t-proj')
- , names = ['followers', 'following', 'repos', 'watched']
+ , user = GITR.user('samsonjs')
+ , names = ['followers', 'following', 'publicRepos', 'publicGists']
if (!t || +new Date() - t > 3600 * 1000) {
console.log('stale ' + String(t))
data.set('t-proj', +new Date())
- names.forEach(function(name) {
- GITR[name]('samsonjs', function(err, things) {
+ user.fetch(function(err, user) {
+ if (user) {
+ data.set('user', user)
+ }
+ names.forEach(function(name) {
if (err) {
- text('n' + name, '?')
- } else {
- data.set(name, things)
- updateN(name, things)
+ updateN(name, '?')
+ }
+ else {
+ updateN(name, user[name])
}
})
})
} else {
console.log('hit ' + t + ' (' + (+new Date() - t) + ')')
- names.forEach(function(name) {
- updateN(name, data.get(name))
- })
+ names.forEach(function(name) { updateN(name, data.get('user')[name]) })
}
}
}())