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 @@

- + - @@ -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]) }) } } }())
projects projects followers
watching + gists following