├── VERSION
├── .gitignore
├── Guardfile
├── app
├── tests
│ ├── mock_response_data
│ │ ├── repos
│ │ │ ├── nokinen
│ │ │ │ ├── fdc
│ │ │ │ │ ├── events?page=10.json
│ │ │ │ │ ├── events?page=9.json
│ │ │ │ │ └── events?page=8.json
│ │ │ │ └── fdc.json
│ │ │ └── pangratz
│ │ │ │ ├── dashboard
│ │ │ │ ├── events?page=10.json
│ │ │ │ ├── events?page=8.json
│ │ │ │ ├── events?page=9.json
│ │ │ │ └── events?page=7.json
│ │ │ │ ├── dashboard.json
│ │ │ │ ├── ember.js.json
│ │ │ │ └── ember.js
│ │ │ │ └── events?page=9.json
│ │ └── users
│ │ │ ├── nokinen.json
│ │ │ ├── pangratz.json
│ │ │ └── nokinen
│ │ │ └── repos.json
│ ├── unit
│ │ ├── core_test.js
│ │ ├── router_test.js
│ │ ├── store_test.js
│ │ ├── view_test.js
│ │ ├── model_test.js
│ │ ├── controller_test.js
│ │ ├── handlebars_helpers_test.js
│ │ └── github_adapter_test.js
│ ├── functional
│ │ ├── title_test.js
│ │ ├── show_user_test.js
│ │ ├── show_user_from_repository_test.js
│ │ └── show_repository_test.js
│ └── integration
│ │ └── store_adapter_test.js
├── templates
│ ├── events
│ │ ├── PublicEvent-template.handlebars
│ │ ├── WatchEvent-template.handlebars
│ │ ├── FollowEvent-template.handlebars
│ │ ├── DeleteEvent-template.handlebars
│ │ ├── ForkEvent-template.handlebars
│ │ ├── GistEvent-template.handlebars
│ │ ├── DownloadEvent-template.handlebars
│ │ ├── CommitCommentEvent-template.handlebars
│ │ ├── MemberEvent-template.handlebars
│ │ ├── actor.handlebars
│ │ ├── IssuesEvent-template.handlebars
│ │ ├── PushEvent-template.handlebars
│ │ ├── PullRequestEvent-template.handlebars
│ │ ├── CreateEvent-template.handlebars
│ │ ├── githubEvent.handlebars
│ │ ├── PullRequestReviewCommentEvent-template.handlebars
│ │ ├── IssueCommentEvent-template.handlebars
│ │ └── GollumEvent-template.handlebars
│ ├── events.handlebars
│ ├── loadMore.handlebars
│ ├── repositories.handlebars
│ ├── repository.handlebars
│ ├── application.handlebars
│ └── user.handlebars
├── static
│ └── img
│ │ ├── ajax-loader.gif
│ │ ├── glyphicons-halflings.png
│ │ └── glyphicons-halflings-white.png
├── css
│ ├── main.css
│ ├── links.css
│ └── github-events.css
├── lib
│ ├── main.js
│ ├── store.js
│ ├── core.js
│ ├── ext.js
│ ├── model.js
│ ├── handlebars_helpers.js
│ ├── controller.js
│ ├── view.js
│ ├── github_adapter.js
│ └── router.js
├── index.html
├── plugins
│ └── loader.js
└── vendor
│ ├── jquery.inview.js
│ └── moment.js
├── config.ru
├── Gemfile
├── LICENSE
├── tests
├── index.html
├── run-tests.js
└── qunit
│ └── qunit.css
├── Gemfile.lock
├── README.md
├── Assetfile
└── Rakefile
/VERSION:
--------------------------------------------------------------------------------
1 | 0.0.15
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | tmp/
3 |
4 | assets/
5 | test_assets/
6 |
--------------------------------------------------------------------------------
/Guardfile:
--------------------------------------------------------------------------------
1 | guard :rake, :task => :test do
2 | watch(%r{^app/.+\.js$})
3 | end
4 |
--------------------------------------------------------------------------------
/app/tests/mock_response_data/repos/nokinen/fdc/events?page=10.json:
--------------------------------------------------------------------------------
1 | [
2 |
3 | ]
4 |
--------------------------------------------------------------------------------
/app/tests/mock_response_data/repos/nokinen/fdc/events?page=9.json:
--------------------------------------------------------------------------------
1 | [
2 |
3 | ]
4 |
--------------------------------------------------------------------------------
/app/tests/mock_response_data/repos/pangratz/dashboard/events?page=10.json:
--------------------------------------------------------------------------------
1 | [
2 |
3 | ]
4 |
--------------------------------------------------------------------------------
/app/tests/mock_response_data/repos/pangratz/dashboard/events?page=8.json:
--------------------------------------------------------------------------------
1 | [
2 |
3 | ]
4 |
--------------------------------------------------------------------------------
/app/tests/mock_response_data/repos/pangratz/dashboard/events?page=9.json:
--------------------------------------------------------------------------------
1 | [
2 |
3 | ]
4 |
--------------------------------------------------------------------------------
/app/templates/events/PublicEvent-template.handlebars:
--------------------------------------------------------------------------------
1 | {{#view view.ActorView}} open sourced some stuff!!{{/view}}
--------------------------------------------------------------------------------
/app/static/img/ajax-loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pangratz/dashboard/develop/app/static/img/ajax-loader.gif
--------------------------------------------------------------------------------
/app/templates/events/WatchEvent-template.handlebars:
--------------------------------------------------------------------------------
1 | {{#view view.ActorView}} {{event.payload.action}} watching{{/view}}
--------------------------------------------------------------------------------
/app/static/img/glyphicons-halflings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pangratz/dashboard/develop/app/static/img/glyphicons-halflings.png
--------------------------------------------------------------------------------
/app/templates/events/FollowEvent-template.handlebars:
--------------------------------------------------------------------------------
1 | {{#view view.ActorView}} started following {{event.payload.target.login}}{{/view}}
--------------------------------------------------------------------------------
/app/static/img/glyphicons-halflings-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pangratz/dashboard/develop/app/static/img/glyphicons-halflings-white.png
--------------------------------------------------------------------------------
/app/templates/events/DeleteEvent-template.handlebars:
--------------------------------------------------------------------------------
1 | {{#view view.ActorView}}
2 | deleted {{event.payload.ref_type}} {{event.payload.ref}}
3 | {{/view}}
--------------------------------------------------------------------------------
/app/css/main.css:
--------------------------------------------------------------------------------
1 | html {
2 | overflow-y: scroll;
3 | }
4 |
5 | body {
6 | padding-top: 60px;
7 | }
8 |
9 | .navbar .brand {
10 | color: #fff;
11 | }
12 |
--------------------------------------------------------------------------------
/app/tests/unit/core_test.js:
--------------------------------------------------------------------------------
1 | require('dashboard/core');
2 |
3 | module("Dashboard");
4 |
5 | test("is defined", function () {
6 | ok(Dashboard, "is defined");
7 | });
8 |
--------------------------------------------------------------------------------
/app/lib/main.js:
--------------------------------------------------------------------------------
1 | require('dashboard/controller');
2 | require('dashboard/view');
3 | require('dashboard/store');
4 | require('dashboard/router');
5 |
6 | Dashboard.initialize();
--------------------------------------------------------------------------------
/app/tests/unit/router_test.js:
--------------------------------------------------------------------------------
1 | require('dashboard/router');
2 |
3 | module("Dashboard.Router");
4 |
5 | test("it exists", function() {
6 | ok(Dashboard.Router, "it exists");
7 | });
--------------------------------------------------------------------------------
/app/templates/events/ForkEvent-template.handlebars:
--------------------------------------------------------------------------------
1 | {{#view view.ActorView}}
2 | forked
3 | repository
4 | {{/view}}
--------------------------------------------------------------------------------
/app/lib/store.js:
--------------------------------------------------------------------------------
1 | require('dashboard/core');
2 | require('dashboard/github_adapter');
3 |
4 | Dashboard.Store = DS.Store.extend({
5 | adapter: Dashboard.GitHubAdpater.create(),
6 | revision: 4
7 | });
--------------------------------------------------------------------------------
/app/templates/events/GistEvent-template.handlebars:
--------------------------------------------------------------------------------
1 | {{#view view.ActorView}} {{event.payload.action}}d gist #{{event.payload.gist.id}}{{/view}}
--------------------------------------------------------------------------------
/app/templates/events/DownloadEvent-template.handlebars:
--------------------------------------------------------------------------------
1 | {{#view view.ActorView}}
2 | created download {{event.payload.download.name}}
3 | {{/view}}
--------------------------------------------------------------------------------
/app/templates/events/CommitCommentEvent-template.handlebars:
--------------------------------------------------------------------------------
1 | {{#view view.ActorView actorBinding="event.actor"}}
2 | commented on commit
3 | {{/view}}
--------------------------------------------------------------------------------
/app/templates/events/MemberEvent-template.handlebars:
--------------------------------------------------------------------------------
1 | {{#view view.ActorView}}
2 | added {{event.payload.member.login}} as a collaborator
3 | {{/view}}
--------------------------------------------------------------------------------
/app/templates/events/actor.handlebars:
--------------------------------------------------------------------------------
1 |
2 | {{event.actor.login}}
3 | {{yield}}
4 | - ( {{event.repo.name}} )
5 |
--------------------------------------------------------------------------------
/app/templates/events/IssuesEvent-template.handlebars:
--------------------------------------------------------------------------------
1 | {{#view view.ActorView}}
2 | {{event.payload.action}} issue
3 | #{{event.payload.issue.number}}
4 | {{/view}}
--------------------------------------------------------------------------------
/app/tests/unit/store_test.js:
--------------------------------------------------------------------------------
1 | require('dashboard/store');
2 |
3 | module("Dashboard.Store");
4 |
5 | test("it exists", function() {
6 | ok(Dashboard.Store, "it exists");
7 | ok(DS.Store.detect(Dashboard.Store), "it is a DS.Store");
8 | });
--------------------------------------------------------------------------------
/app/templates/events/PushEvent-template.handlebars:
--------------------------------------------------------------------------------
1 | {{#view view.ActorView}}
2 | pushed to
3 | {{event.payload.ref}}
4 | {{/view}}
--------------------------------------------------------------------------------
/app/templates/events/PullRequestEvent-template.handlebars:
--------------------------------------------------------------------------------
1 | {{#view view.ActorView}}
2 | {{event.payload.action}} Pull Request
3 | #{{event.payload.number}}
4 | {{/view}}
--------------------------------------------------------------------------------
/app/lib/core.js:
--------------------------------------------------------------------------------
1 | require('jquery');
2 | require('handlebars');
3 | require('ember');
4 | require('ember-data');
5 | require('moment');
6 | require('dashboard/ext');
7 |
8 | Dashboard = Ember.Application.create({
9 | VERSION: '0.0.0'
10 | });
11 |
--------------------------------------------------------------------------------
/app/templates/events.handlebars:
--------------------------------------------------------------------------------
1 |
Events
2 |
3 | {{#each event in controller}}
4 | {{view view.EventView eventBinding="event"}}
5 | {{/each}}
6 | {{view Dashboard.LoadMoreView controllerBinding="view.controller" }}
7 |
8 |
--------------------------------------------------------------------------------
/app/templates/events/CreateEvent-template.handlebars:
--------------------------------------------------------------------------------
1 | {{#view view.ActorView}}
2 | created {{event.payload.ref_type}}
3 | {{#if event.payload.ref}}
4 | {{event.payload.ref}}
5 | {{/if}}
6 | {{/view}}
--------------------------------------------------------------------------------
/app/css/links.css:
--------------------------------------------------------------------------------
1 | a {
2 | cursor: pointer;
3 | }
4 |
5 | a[href^="http"] {
6 | color: green;
7 | }
8 |
9 | a[href^="https://github.com"] {
10 | display: inline-block;
11 | line-height: 18px;
12 | background:url('https://github.com/favicon.ico') center right no-repeat;
13 | padding-right: 18px;
14 | }
15 |
--------------------------------------------------------------------------------
/app/templates/events/githubEvent.handlebars:
--------------------------------------------------------------------------------
1 | {{view.timeAgoString}}
2 |
3 |
4 |
5 |

6 |
7 |
8 | {{view view.DetailView eventBinding="event" }}
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/templates/loadMore.handlebars:
--------------------------------------------------------------------------------
1 | {{#if isLoading}}
2 | fetching some more stuff
3 | {{else}}
4 | {{#if canLoadMore}}
5 | click to load more
6 | {{else}}
7 | no more items
8 | {{/if}}
9 | {{/if}}
10 |
--------------------------------------------------------------------------------
/app/templates/events/PullRequestReviewCommentEvent-template.handlebars:
--------------------------------------------------------------------------------
1 | {{#view view.ActorView}}
2 | reviewed on commit
3 |
4 | {{trim event.payload.comment.commit_id length=7}}
5 |
6 | {{/view}}
7 |
8 | {{event.payload.comment.body}}
9 |
--------------------------------------------------------------------------------
/app/templates/events/IssueCommentEvent-template.handlebars:
--------------------------------------------------------------------------------
1 | {{#view view.ActorView}}
2 | commented on issue
3 |
4 | #{{event.payload.issue.number}}
5 |
6 | {{/view}}
7 |
8 | {{event.payload.issue.title}}
9 |
--------------------------------------------------------------------------------
/app/templates/events/GollumEvent-template.handlebars:
--------------------------------------------------------------------------------
1 | {{#view view.ActorView}}
2 | modified the wiki
3 | {{/view}}
4 |
5 | {{#each event.payload.pages }}
6 |
{{echo "action"}} {{page_name}}
7 | {{/each}}
8 |
9 |
--------------------------------------------------------------------------------
/app/tests/functional/title_test.js:
--------------------------------------------------------------------------------
1 | casper.test.comment('Intial routing');
2 |
3 | casper.start('http://localhost:9292', function() {
4 | this.test.assertTitle('GitHub Dashboard', 'title is the one expected');
5 | this.test.assertEquals(this.getCurrentUrl(), 'http://localhost:9292/#/pangratz', 'routes to default');
6 | });
7 |
8 | casper.run(function() {
9 | this.test.done();
10 | });
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | require 'rake-pipeline'
2 | require 'rake-pipeline/middleware'
3 | use Rake::Pipeline::Middleware, 'Assetfile'
4 |
5 | # require 'rack/streaming_proxy'
6 | # use Rack::StreamingProxy do |request|
7 | # if request.path.start_with?('/proxy')
8 | # "http://127.0.0.1:8080#{request.path}"
9 | # end
10 | # end
11 |
12 | require 'rack-rewrite'
13 | use Rack::Rewrite do
14 | rewrite %r{^(.*)\/$}, '$1/index.html'
15 | end
16 |
17 | run Rack::Directory.new('.')
18 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source :rubygems
2 |
3 | gem 'colored'
4 | gem 'versionomy'
5 |
6 | gem 'casperjs'
7 |
8 | gem 'guard'
9 | gem 'guard-rake'
10 |
11 | gem 'rack'
12 | gem 'rack-rewrite'
13 | # gem 'rack-streaming-proxy'
14 |
15 | gem 'sass'
16 | gem 'compass'
17 |
18 | gem 'uglifier'
19 | gem 'yui-compressor'
20 |
21 | gem 'rake-pipeline', :git => 'https://github.com/livingsocial/rake-pipeline.git'
22 | gem 'rake-pipeline-web-filters', :git => 'https://github.com/wycats/rake-pipeline-web-filters.git'
23 |
--------------------------------------------------------------------------------
/app/templates/repositories.handlebars:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/css/github-events.css:
--------------------------------------------------------------------------------
1 | .profilePic {
2 | float: left;
3 | margin: 3px;
4 | margin-right: 10px;
5 | }
6 |
7 | .profilePic > img {
8 | border-radius: 3px;
9 | }
10 |
11 | .info {
12 | float: none;
13 | }
14 |
15 | div.additional-info {
16 | margin-left: 5px;
17 | margin-top: 5px;
18 | }
19 |
20 | div.event {
21 | background-color: #F7F7F9;
22 | -webkit-border-radius: 5px;
23 | -moz-border-radius: 5px;
24 | border-radius: 5px;
25 | margin-bottom: 1em;
26 | padding: 0.5em;
27 | border: 1px solid #E1E1E8;
28 | }
--------------------------------------------------------------------------------
/app/lib/ext.js:
--------------------------------------------------------------------------------
1 | var get = Ember.get, fmt = Ember.String.fmt;
2 |
3 | Ember.View.reopen({
4 | templateForName: function(name, type) {
5 | if (!name) { return; }
6 |
7 | var templates = get(this, 'templates'),
8 | template = get(templates, name);
9 |
10 | if (!template) {
11 | try {
12 | template = require('dashboard/~templates/' + name);
13 | } catch (e) {
14 | throw new Ember.Error(fmt('%@ - Unable to find %@ "%@".', [this, type, name]));
15 | }
16 | }
17 |
18 | return template;
19 | }
20 | });
21 |
--------------------------------------------------------------------------------
/app/tests/functional/show_user_test.js:
--------------------------------------------------------------------------------
1 | var x = require('casper').selectXPath;
2 |
3 | casper.test.comment('Clicking on a user routes to this user');
4 |
5 | casper.start('http://localhost:9292/#/nokinen');
6 |
7 | var nokinen = x('//a[text()="nokinen"]');
8 | casper.waitUntilVisible(nokinen, function() {
9 | casper.click(nokinen);
10 | });
11 |
12 | casper.then(function() {
13 | this.test.assertEquals(this.getCurrentUrl(), 'http://localhost:9292/#/nokinen', 'routes to clicked repository');
14 | });
15 |
16 | casper.run(function() {
17 | this.test.done();
18 | });
--------------------------------------------------------------------------------
/app/tests/unit/view_test.js:
--------------------------------------------------------------------------------
1 | require('dashboard/view');
2 |
3 | module("Dashboard.ApplicationView");
4 |
5 | test("it exists", function() {
6 | ok(Dashboard.ApplicationView, "it exists");
7 | });
8 |
9 | module("Dashboard.RepositoryView");
10 |
11 | test("it exists", function() {
12 | ok(Dashboard.RepositoryView, "it exists");
13 | });
14 |
15 | module("Dashboard.RepositoriesView");
16 |
17 | test("it exists", function() {
18 | ok(Dashboard.RepositoriesView, "it exists");
19 | });
20 |
21 | module("Dashboard.EventsView");
22 |
23 | test("it exists", function() {
24 | ok(Dashboard.EventsView, "it exists");
25 | });
--------------------------------------------------------------------------------
/app/templates/repository.handlebars:
--------------------------------------------------------------------------------
1 |
10 |
11 | {{outlet "events"}}
12 |
--------------------------------------------------------------------------------
/app/tests/functional/show_user_from_repository_test.js:
--------------------------------------------------------------------------------
1 | var x = require('casper').selectXPath;
2 |
3 | casper.test.comment('Clicking on a user when watching a specific repository changes route to user');
4 |
5 | casper.start('http://localhost:9292/#/nokinen/fdc');
6 |
7 | var nokinen = x('//a[text()="nokinen"]');
8 | casper.waitUntilVisible(nokinen, function() {
9 | casper.click(nokinen);
10 | });
11 |
12 | casper.then(function() {
13 | this.test.assertEquals(this.getCurrentUrl(), 'http://localhost:9292/#/nokinen', 'routes to clicked repository');
14 | });
15 |
16 | casper.run(function() {
17 | this.test.done();
18 | });
--------------------------------------------------------------------------------
/app/tests/unit/model_test.js:
--------------------------------------------------------------------------------
1 | require('dashboard/model');
2 |
3 | module("Dashboard.Repository");
4 |
5 | test("it exists", function() {
6 | ok(Dashboard.Repository, "it exists");
7 | ok(DS.Model.detect(Dashboard.Repository), "it is a DS.Model");
8 | });
9 |
10 | module("Dashboard.Event");
11 |
12 | test("it exists", function() {
13 | ok(Dashboard.Event, "it exists");
14 | ok(DS.Model.detect(Dashboard.Event), "it is a DS.Model");
15 | });
16 |
17 | module("Dashboard.User");
18 |
19 | test("it exists", function() {
20 | ok(Dashboard.User, "it exists");
21 | ok(DS.Model.detect(Dashboard.User), "it is a DS.Model");
22 | });
--------------------------------------------------------------------------------
/app/templates/application.handlebars:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 | {{outlet}}
18 |
19 |
--------------------------------------------------------------------------------
/app/tests/functional/show_repository_test.js:
--------------------------------------------------------------------------------
1 | var x = require('casper').selectXPath;
2 |
3 | casper.test.comment('Clicking on a repository shows repository page');
4 |
5 | casper.start('http://localhost:9292/#/pangratz');
6 |
7 | // change this to check for availability of repository which shall be shown
8 | var dashboard = x('//a[text()="dashboard"]');
9 | casper.waitUntilVisible(dashboard, function() {
10 | casper.click(dashboard);
11 | });
12 |
13 | casper.then(function() {
14 | this.test.assertEquals(this.getCurrentUrl(), 'http://localhost:9292/#/pangratz/dashboard', 'routes to clicked repository');
15 | });
16 |
17 | casper.run(function() {
18 | this.test.done();
19 | });
--------------------------------------------------------------------------------
/app/tests/mock_response_data/users/nokinen.json:
--------------------------------------------------------------------------------
1 | {
2 | "created_at": "2010-10-11T19:55:19Z",
3 | "type": "User",
4 | "login": "nokinen",
5 | "avatar_url": "https://secure.gravatar.com/avatar/3f60ae2a5febcca386fa08d6b4af43f0?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
6 | "email": "tobias@noig.es",
7 | "public_repos": 9,
8 | "followers": 5,
9 | "html_url": "https://github.com/nokinen",
10 | "blog": "twitter.com/tnugg",
11 | "hireable": false,
12 | "public_gists": 0,
13 | "name": "Tobias Noiges",
14 | "company": null,
15 | "url": "https://api.github.com/users/nokinen",
16 | "gravatar_id": "3f60ae2a5febcca386fa08d6b4af43f0",
17 | "bio": null,
18 | "id": 435811,
19 | "following": 11,
20 | "location": "Central Alps"
21 | }
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) Clemens Müller <@pangratz>
2 |
3 | Permission to use, copy, modify, and/or distribute this software for any
4 | purpose with or without fee is hereby granted, provided that the above
5 | copyright notice and this permission notice appear in all copies.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 |
--------------------------------------------------------------------------------
/app/tests/mock_response_data/users/pangratz.json:
--------------------------------------------------------------------------------
1 | {
2 | "created_at": "2010-07-23T12:46:08Z",
3 | "type": "User",
4 | "login": "pangratz",
5 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
6 | "email": "cmueller.418@gmail.com",
7 | "public_repos": 78,
8 | "followers": 17,
9 | "html_url": "https://github.com/pangratz",
10 | "blog": "http://code418.com",
11 | "hireable": true,
12 | "public_gists": 7,
13 | "name": "Clemens Müller",
14 | "company": null,
15 | "url": "https://api.github.com/users/pangratz",
16 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab",
17 | "bio": "",
18 | "id": 341877,
19 | "following": 28,
20 | "location": "localhost, linz, austria"
21 | }
22 |
--------------------------------------------------------------------------------
/app/templates/user.handlebars:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |
5 |
6 |
7 |
{{login}} - {{name}}
8 | {{#if blog}}
9 | -
{{blog}}
10 | {{/if}}
11 |
12 |
13 | {{public_repos}} public repos
14 |
15 |
16 |
17 |
18 |
19 |
20 |
Repositories
21 | {{outlet "repositories"}}
22 |
23 |
24 | {{outlet "events"}}
25 |
26 |
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | GitHub Dashboard
6 |
7 |
20 |
21 |
22 |
23 |
24 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Dashboard Test Suite
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/lib/model.js:
--------------------------------------------------------------------------------
1 | require('dashboard/core');
2 |
3 | Dashboard.Repository = DS.Model.extend({
4 | primaryKey: 'full_name',
5 | name: DS.attr('string'),
6 | full_name: DS.attr('string'),
7 | description: DS.attr('string'),
8 | html_url: DS.attr('string'),
9 | homepage: DS.attr('string'),
10 | watchers: DS.attr('number'),
11 | forks: DS.attr('number'),
12 | language: DS.attr('string'),
13 | updated_at: DS.attr('date'),
14 | owner: function() {
15 | return this.get('data.owner')
16 | }.property('data')
17 | });
18 |
19 | Dashboard.Event = DS.Model.extend({
20 | type: DS.attr('string'),
21 | created_at: DS.attr('string'),
22 | actor: function() { return this.get('data.actor'); }.property('data.actor'),
23 | repo: function() { return this.get('data.repo'); }.property('data.repo'),
24 | org: function() { return this.get('data.org'); }.property('data.org'),
25 | payload: function() { return this.get('data.payload'); }.property('data.payload')
26 | });
27 |
28 | Dashboard.User = DS.Model.extend({
29 | primaryKey: 'login',
30 | login: DS.attr('string'),
31 | avatar_url: DS.attr('string'),
32 | name: DS.attr('string'),
33 | blog: DS.attr('string'),
34 | email: DS.attr('string'),
35 | bio: DS.attr('string'),
36 | public_repos: DS.attr('number'),
37 | followers: DS.attr('number'),
38 | following: DS.attr('number'),
39 | html_url: DS.attr('string'),
40 | created_at: DS.attr('string')
41 | });
--------------------------------------------------------------------------------
/app/tests/unit/controller_test.js:
--------------------------------------------------------------------------------
1 | require('dashboard/controller');
2 |
3 | var controller;
4 |
5 | module("Dashboard.ApplicationController");
6 |
7 | test("it exists", function() {
8 | ok(Dashboard.ApplicationController, "it exists");
9 | });
10 |
11 | module("Dashboard.UserController");
12 |
13 | test("it exists", function() {
14 | ok(Dashboard.UserController, "it exists");
15 | });
16 |
17 | module("Dashboard.EventsController");
18 |
19 | test("it exists", function() {
20 | ok(Dashboard.EventsController, "it exists");
21 | });
22 |
23 | module("Dashboard.RepositoriesController", {
24 | setup: function() {
25 | controller = Dashboard.RepositoriesController.create();
26 | },
27 | teardowm: function() {
28 | controller.destroy();
29 | }
30 | });
31 |
32 | test("it exists", function() {
33 | ok(Dashboard.RepositoriesController, "it exists");
34 | });
35 |
36 | test("loadWatchedRepositories invokes watchedRepositories on dataSource", function() {
37 | var watchedRepositoriesCalled, usernameParameter;
38 | var dataSource = {
39 | watchedRepositories: function(username) {
40 | watchedRepositoriesCalled = true;
41 | usernameParameter = username;
42 | }
43 | };
44 |
45 | Ember.run(function() { controller.set('dataSource', dataSource); });
46 | controller.loadWatchedRepositories('buster');
47 |
48 | ok(watchedRepositoriesCalled, "loadWatchedRepositories on dataSource has been called");
49 | equal(usernameParameter, 'buster', "supplied username has been passed to dataSource");
50 | });
--------------------------------------------------------------------------------
/app/tests/mock_response_data/repos/pangratz/dashboard.json:
--------------------------------------------------------------------------------
1 | {
2 | "forks": 3,
3 | "language": "JavaScript",
4 | "git_url": "git://github.com/pangratz/dashboard.git",
5 | "created_at": "2012-03-05T09:21:46Z",
6 | "master_branch": "develop",
7 | "description": "GitHub Dashboard using Ember.js and Twitter Bootstrap",
8 | "ssh_url": "git@github.com:pangratz/dashboard.git",
9 | "owner": {
10 | "login": "pangratz",
11 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
12 | "url": "https://api.github.com/users/pangratz",
13 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab",
14 | "id": 341877
15 | },
16 | "mirror_url": null,
17 | "has_downloads": true,
18 | "watchers_count": 11,
19 | "updated_at": "2012-08-09T12:57:34Z",
20 | "open_issues_count": 13,
21 | "forks_count": 3,
22 | "svn_url": "https://github.com/pangratz/dashboard",
23 | "network_count": 3,
24 | "has_wiki": false,
25 | "html_url": "https://github.com/pangratz/dashboard",
26 | "watchers": 11,
27 | "size": 660,
28 | "fork": false,
29 | "full_name": "pangratz/dashboard",
30 | "clone_url": "https://github.com/pangratz/dashboard.git",
31 | "name": "dashboard",
32 | "url": "https://api.github.com/repos/pangratz/dashboard",
33 | "open_issues": 13,
34 | "has_issues": true,
35 | "homepage": "http://pangratz.github.com/dashboard",
36 | "private": false,
37 | "id": 3625527,
38 | "pushed_at": "2012-07-27T10:49:23Z"
39 | }
40 |
--------------------------------------------------------------------------------
/app/lib/handlebars_helpers.js:
--------------------------------------------------------------------------------
1 | require('dashboard/core');
2 |
3 | var getPath = Ember.Handlebars.getPath, normalizePath = Ember.Handlebars.normalizePath;
4 |
5 | var getValue = function(property, options, context) {
6 | var context = (options.contexts && options.contexts[0]) || context,
7 | normalized = normalizePath(context, property, options.data),
8 | pathRoot = normalized.root,
9 | path = normalized.path,
10 | value = (path === 'this') ? pathRoot : getPath(pathRoot, path, options);
11 |
12 | return value;
13 | };
14 |
15 | // this helper is needed to print out properties which are called "action"
16 | Ember.Handlebars.registerHelper('echo', function(property, options) {
17 | return getValue(property, options, this);
18 | });
19 |
20 | // trim a given property to specific length
21 | Ember.Handlebars.registerHelper('trim', function(property, options) {
22 | var length = options.hash.length;
23 | var string = getValue(property, options, this);
24 | if (string && length) {
25 | string = string.substring(0, length);
26 | }
27 | return new Handlebars.SafeString(string || '');
28 | });
29 |
30 | // output timestamp property as something like `1h ago`
31 | Ember.Handlebars.registerHelper('ago', function(property, options) {
32 | var timestamp = getValue(property, options, this);
33 | if (options.hash.isSeconds) {
34 | // the given property represents seconds since UNIX epoch, so we multiply
35 | // by 1000 to get the date in milliseconds since UNIX epoch
36 | timestamp *= 1000;
37 | }
38 |
39 | return moment(new Date(timestamp)).fromNow();
40 | });
--------------------------------------------------------------------------------
/app/tests/mock_response_data/repos/nokinen/fdc.json:
--------------------------------------------------------------------------------
1 | {
2 | "forks": 2,
3 | "language": "Ruby",
4 | "git_url": "git://github.com/nokinen/fdc.git",
5 | "created_at": "2012-05-14T22:32:28Z",
6 | "master_branch": "master",
7 | "description": "A platform independent command-line utility written in Ruby for converting files in the avionics flight recorder data format (IGC) to the keyhole markup language (KML) for their display in applications such as Google Earth.",
8 | "ssh_url": "git@github.com:nokinen/fdc.git",
9 | "owner": {
10 | "login": "nokinen",
11 | "avatar_url": "https://secure.gravatar.com/avatar/3f60ae2a5febcca386fa08d6b4af43f0?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
12 | "url": "https://api.github.com/users/nokinen",
13 | "gravatar_id": "3f60ae2a5febcca386fa08d6b4af43f0",
14 | "id": 435811
15 | },
16 | "mirror_url": null,
17 | "has_downloads": true,
18 | "watchers_count": 2,
19 | "updated_at": "2012-06-21T18:23:39Z",
20 | "open_issues_count": 1,
21 | "forks_count": 2,
22 | "svn_url": "https://github.com/nokinen/fdc",
23 | "network_count": 2,
24 | "has_wiki": false,
25 | "html_url": "https://github.com/nokinen/fdc",
26 | "watchers": 2,
27 | "size": 296,
28 | "fork": false,
29 | "full_name": "nokinen/fdc",
30 | "clone_url": "https://github.com/nokinen/fdc.git",
31 | "name": "fdc",
32 | "url": "https://api.github.com/repos/nokinen/fdc",
33 | "open_issues": 1,
34 | "has_issues": true,
35 | "homepage": "https://rubygems.org/gems/fdc",
36 | "private": false,
37 | "id": 4329788,
38 | "pushed_at": "2012-06-21T18:23:39Z"
39 | }
40 |
--------------------------------------------------------------------------------
/app/plugins/loader.js:
--------------------------------------------------------------------------------
1 | (function(window) {
2 | function requireWrapper(self) {
3 | var require = function() {
4 | return self.require.apply(self, arguments);
5 | };
6 | require.exists = function() {
7 | return self.exists.apply(self, arguments);
8 | };
9 | return require;
10 | }
11 |
12 | var Loader = function() {
13 | this.modules = {};
14 | this.loaded = {};
15 | this.exports = {};
16 | return this;
17 | };
18 |
19 | Loader.prototype.require = function(name) {
20 | if (!this.loaded[name]) {
21 | var module = this.modules[name];
22 | if (module) {
23 | var require = requireWrapper(this);
24 | try {
25 | this.exports[name] = module.call(window, require);
26 | return this.exports[name];
27 | } finally {
28 | this.loaded[name] = true;
29 | }
30 | } else {
31 | throw "The module '" + name + "' has not been registered";
32 | }
33 | }
34 | return this.exports[name];
35 | };
36 |
37 | Loader.prototype.register = function(name, module) {
38 | if (this.exists(name)) {
39 | throw "The module '" + name + "' has already been registered";
40 | }
41 | this.modules[name] = module;
42 | return true;
43 | };
44 |
45 | Loader.prototype.unregister = function(name) {
46 | var loaded = !!this.loaded[name];
47 | if (loaded) {
48 | delete this.exports[name];
49 | delete this.modules[name];
50 | delete this.loaded[name];
51 | }
52 | return loaded;
53 | };
54 |
55 | Loader.prototype.exists = function(name) {
56 | return name in this.modules;
57 | };
58 |
59 | window.loader = new Loader();
60 | })(this);
61 |
--------------------------------------------------------------------------------
/app/lib/controller.js:
--------------------------------------------------------------------------------
1 | require('dashboard/core');
2 |
3 | Dashboard.LoadMoreMixin = Ember.Mixin.create(Ember.Evented, {
4 | canLoadMore: true,
5 | autoFetch: true,
6 | currentPage: 1,
7 | resetLoadMore: function() {
8 | this.set('currentPage', 1);
9 | },
10 | loadMore: Ember.K
11 | });
12 |
13 | Dashboard.ApplicationController = Ember.Controller.extend();
14 |
15 | Dashboard.UserController = Ember.ObjectController.extend();
16 |
17 | Dashboard.RepositoryController = Ember.ObjectController.extend();
18 |
19 | Dashboard.EventsController = Ember.ArrayController.extend(Dashboard.LoadMoreMixin, {
20 | canLoadMore: function() {
21 | // github only allows 10 pages for events, see http://developer.github.com/v3/events/
22 | return this.get('currentPage') < 10;
23 | }.property('currentPage'),
24 |
25 | loadMore: function() {
26 | if (this.get('canLoadMore')) {
27 | var page = this.incrementProperty('currentPage');
28 | this.get('target').send('loadMoreEvents', page);
29 | }
30 | }
31 | });
32 |
33 | Dashboard.RepositoriesController = Ember.ArrayController.extend(Dashboard.LoadMoreMixin, {
34 | sortProperties: 'updated_at'.w(),
35 | sortAscending: false,
36 | loadWatchedRepositories: function(username) {
37 | this.get('dataSource').watchedRepositories(username, this, 'addObjects');
38 | },
39 |
40 | canLoadMore: function() {
41 | var reposCount = this.get('owner.publicRepos');
42 | var length = this.get('length');
43 | return true || (length < reposCount);
44 | }.property('owner.publicRepos', 'length'),
45 |
46 | loadMore: function() {
47 | if (this.get('canLoadMore')) {
48 | var page = this.incrementProperty('currentPage');
49 | this.get('target').send('loadMoreRepos', page);
50 | }
51 | }
52 | });
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GIT
2 | remote: https://github.com/livingsocial/rake-pipeline.git
3 | revision: 543f4322fe70facee9572d29ddabf7f090dad68a
4 | specs:
5 | rake-pipeline (0.6.0)
6 | rake (~> 0.9.0)
7 | thor
8 |
9 | GIT
10 | remote: https://github.com/wycats/rake-pipeline-web-filters.git
11 | revision: 81a22fb0808dfdeab8ed92d5d8c898ad198b9938
12 | specs:
13 | rake-pipeline-web-filters (0.6.0)
14 | rack
15 | rake-pipeline (~> 0.6)
16 |
17 | GEM
18 | remote: http://rubygems.org/
19 | specs:
20 | POpen4 (0.1.4)
21 | Platform (>= 0.4.0)
22 | open4
23 | Platform (0.4.0)
24 | blockenspiel (0.4.5)
25 | casperjs (1.0.0.RC1)
26 | chunky_png (1.2.5)
27 | colored (1.2)
28 | compass (0.12.1)
29 | chunky_png (~> 1.2)
30 | fssm (>= 0.2.7)
31 | sass (~> 3.1)
32 | execjs (1.4.0)
33 | multi_json (~> 1.0)
34 | ffi (1.0.11)
35 | fssm (0.2.9)
36 | guard (1.1.1)
37 | listen (>= 0.4.2)
38 | thor (>= 0.14.6)
39 | guard-rake (0.0.5)
40 | guard
41 | rake
42 | listen (0.4.3)
43 | rb-fchange (~> 0.0.5)
44 | rb-fsevent (~> 0.9.1)
45 | rb-inotify (~> 0.8.8)
46 | multi_json (1.3.6)
47 | open4 (1.3.0)
48 | rack (1.4.1)
49 | rack-rewrite (1.2.1)
50 | rake (0.9.2.2)
51 | rb-fchange (0.0.5)
52 | ffi
53 | rb-fsevent (0.9.1)
54 | rb-inotify (0.8.8)
55 | ffi (>= 0.5.0)
56 | sass (3.1.19)
57 | thor (0.15.2)
58 | uglifier (1.2.4)
59 | execjs (>= 0.3.0)
60 | multi_json (>= 1.0.2)
61 | versionomy (0.4.4)
62 | blockenspiel (>= 0.4.5)
63 | yui-compressor (0.9.6)
64 | POpen4 (>= 0.1.4)
65 |
66 | PLATFORMS
67 | ruby
68 |
69 | DEPENDENCIES
70 | casperjs
71 | colored
72 | compass
73 | guard
74 | guard-rake
75 | rack
76 | rack-rewrite
77 | rake-pipeline!
78 | rake-pipeline-web-filters!
79 | sass
80 | uglifier
81 | versionomy
82 | yui-compressor
83 |
--------------------------------------------------------------------------------
/app/lib/view.js:
--------------------------------------------------------------------------------
1 | require('dashboard/core');
2 | require('dashboard/handlebars_helpers');
3 | require('jquery.inview');
4 |
5 | Dashboard.ApplicationView = Ember.View.extend({
6 | templateName: 'application'
7 | });
8 |
9 | Dashboard.LoadMoreView = Ember.View.extend({
10 | templateName: 'loadMore',
11 | didInsertElement: function() {
12 | if (this.get('controller.autoFetch')) {
13 | var view = this;
14 | this.$().bind('inview', function(event, isInView, visiblePartX, visiblePartY) {
15 | if (isInView) Ember.tryInvoke(view.get('controller'), 'loadMore');
16 | });
17 | }
18 | }
19 | });
20 |
21 | Dashboard.EventsView = Ember.View.extend({
22 | templateName: 'events',
23 | EventView: Ember.View.extend({
24 | templateName: 'events/githubEvent',
25 |
26 | timeAgoString: function() {
27 | var timeAgo = this.get('event.created_at');
28 | return moment(timeAgo).fromNow();
29 | }.property('timeAgo'),
30 |
31 | avatarUrl: function() {
32 | var gravatarId = this.get('event.actor.gravatar_id');
33 | return 'http://www.gravatar.com/avatar/%@'.fmt(gravatarId);
34 | }.property('event.actor.gravatar_id'),
35 |
36 | DetailView: Ember.View.extend({
37 | className: 'info'.w(),
38 |
39 | templateName: function() {
40 | var type = this.get('event.type');
41 | return 'events/%@-template'.fmt(type);
42 | }.property('event.type'),
43 |
44 | ActorView: Ember.View.extend({
45 | layoutName: 'events/actor',
46 | defaultTemplate: Ember.Handlebars.compile(''),
47 | tagName: '',
48 | eventBinding: 'parentView.event',
49 |
50 | href: function() {
51 | var login = this.get('event.actor.login');
52 | return 'https://github.com/%@'.fmt(login);
53 | }.property('event.actor.login')
54 | })
55 | })
56 | })
57 | });
58 |
59 | Dashboard.UserView = Ember.View.extend({
60 | templateName: 'user'
61 | });
62 |
63 | Dashboard.RepositoryView = Ember.View.extend({
64 | templateName: 'repository'
65 | });
66 |
67 | Dashboard.RepositoriesView = Ember.View.extend({
68 | templateName: 'repositories'
69 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [GitHub Dashboard](http://pangratz.github.com/dashboard)
2 | ========================================================
3 |
4 | GitHub Dashboard using Ember.js and Twitter Bootstrap. This repo is developed in the course of the accompanying blog posts at [code418.com](http://code418.com/blog/categories/github-dashboard/).
5 |
6 | Running
7 | -------
8 |
9 | $ bundle install
10 | $ bundle exec rackup
11 |
12 | App Structure
13 | -------------
14 |
15 | dashboard
16 | ├── Assetfile - App build file
17 | ├── Gemfile - Package dependencies for rakep/rack
18 | ├── Gemfile.lock - Here be dragons: don't touch, always include
19 | ├── app - App specific code
20 | │ ├── index.html - The app entry point
21 | │ ├── css - App CSS or SCSS (.scss)
22 | │ ├── lib - App code, *modularized during build*
23 | │ ├── modules - Module code, *already modularized*
24 | │ ├── plugins - Plugins (e.g. jquery.jsonrpc.js)
25 | │ │ └── loader.js - JS module loader
26 | │ ├── static - Static files, never touched, copied over during build
27 | │ ├── templates - Handlebars templates, *modularized during build*
28 | │ ├── tests - QUnit application tests
29 | │ └── vendor - Vendor code, *modularized during build*
30 | ├── assets - Built out asset files, minified in production
31 | │ ├── app.css - Built out app CSS/SCSS
32 | │ ├── app.js - Built out app JS
33 | │ └── loader.js - Built out JS module loader
34 | ├── config.ru - Rack development web server configuration
35 | ├── tests - QUnit testing files
36 | │ ├── index.html - The testing entry point
37 | │ ├── qunit - Testing support files
38 | │ └── run-tests.js - The PhantomJS QUnit test runner
39 | ├── test_assets - Built out test asset files
40 | │ ├── app-tests.js - App test files
41 | └── tmp - Temporary build files used by rakep
42 |
43 | Testing
44 | -------
45 |
46 | You can test the app by invoking:
47 |
48 | $ bundle exec rake test
49 |
50 | This executes the tests by using [PhantomJS](http://www.phantomjs.org/),
51 | which you need to have installed.
52 |
53 | Or you can run the tests via:
54 |
55 | $ bundle exec rackup
56 | $ open http://localhost:9292/tests/index.html
57 |
58 | If you want to continuously run the tests every time a file changes, invoke:
59 |
60 | $ bundle exec guard
61 |
--------------------------------------------------------------------------------
/tests/run-tests.js:
--------------------------------------------------------------------------------
1 | // PhantomJS QUnit Test Runner
2 |
3 | var args = phantom.args;
4 | if (args.length < 1 || args.length > 2) {
5 | console.log("Usage: " + phantom.scriptName + " ");
6 | phantom.exit(1);
7 | }
8 |
9 | var page = require('webpage').create();
10 |
11 | var depRe = /^DEPRECATION:/;
12 | page.onConsoleMessage = function(msg) {
13 | if (!depRe.test(msg)) console.log(msg);
14 | };
15 |
16 | var uri = args[0];
17 | page.open(uri, function(status) {
18 | if (status !== 'success') {
19 | console.error("Unable to access: " + uri + " [" + status + "]");
20 | phantom.exit(1);
21 | } else {
22 | page.evaluate(addLogging);
23 |
24 | var timeout = parseInt(args[1] || 30000, 10);
25 | var start = Date.now();
26 | var interval = setInterval(function() {
27 | if (Date.now() > start + timeout) {
28 | console.error("Tests timed out");
29 | phantom.exit(1);
30 | } else {
31 | var qunitDone = page.evaluate(function() {
32 | return window.qunitDone;
33 | });
34 |
35 | if (qunitDone) {
36 | clearInterval(interval);
37 | if (qunitDone.failed > 0) {
38 | phantom.exit(1);
39 | } else {
40 | phantom.exit();
41 | }
42 | }
43 | }
44 | }, 500);
45 | }
46 | });
47 |
48 | function addLogging() {
49 | var testErrors = [];
50 | var assertionErrors = [];
51 |
52 | QUnit.moduleDone(function(context) {
53 | if (context.failed) {
54 | var msg = "Module Failed: " + context.name + "\n" + testErrors.join("\n");
55 | console.error(msg);
56 | testErrors = [];
57 | }
58 | });
59 |
60 | QUnit.testDone(function(context) {
61 | if (context.failed) {
62 | var msg = " Test Failed: " + context.name + assertionErrors.join(" ");
63 | testErrors.push(msg);
64 | assertionErrors = [];
65 | }
66 | });
67 |
68 | QUnit.log(function(context) {
69 | if (context.result) return;
70 |
71 | var msg = "\n Assertion Failed:";
72 | if (context.message) {
73 | msg += " " + context.message;
74 | }
75 |
76 | if (context.expected) {
77 | msg += "\n Expected: " + context.expected + ", Actual: " + context.actual;
78 | }
79 |
80 | assertionErrors.push(msg);
81 | });
82 |
83 | QUnit.done(function(context) {
84 | var stats = [
85 | "Time: " + context.runtime + "ms",
86 | "Total: " + context.total,
87 | "Passed: " + context.passed,
88 | "Failed: " + context.failed
89 | ];
90 | console.log(stats.join(", "));
91 | window.qunitDone = context;
92 | });
93 | }
94 |
--------------------------------------------------------------------------------
/app/lib/github_adapter.js:
--------------------------------------------------------------------------------
1 | require('dashboard/core');
2 | require('dashboard/model');
3 |
4 | Dashboard.GitHubAdpater = DS.Adapter.extend({
5 | PREFIX: 'https://api.github.com',
6 |
7 | _ajax: function(url, callback) {
8 | Ember.$.ajax({
9 | url: this.PREFIX + url,
10 | dataType: 'jsonp',
11 | context: this,
12 | success: callback
13 | });
14 | },
15 |
16 | ajax: function(url, callback) {
17 | this._ajax(url,
18 | function(response) {
19 | this._updateLimits(response);
20 | callback.call(this, response.data);
21 | });
22 | },
23 |
24 | _updateLimits: function(response) {
25 | if (response.meta) {
26 | this.set('remaining', response.meta['X-RateLimit-Remaining']);
27 | this.set('limit', response.meta['X-RateLimit-Limit']);
28 | }
29 | },
30 |
31 | _invoke: function(target, callback, query) {
32 | return function(data) {
33 | Ember.tryInvoke(target, callback, [data]);
34 | Ember.tryInvoke(query, 'isLoadedCallback');
35 | };
36 | },
37 |
38 | _storeLoad: function(store, type, id) {
39 | return function(data) {
40 | store.load(type, id, data);
41 | };
42 | },
43 |
44 | find: function(store, type, id) {
45 | if (Dashboard.Repository.detect(type)) {
46 | this.ajax('/repos/%@'.fmt(id), this._storeLoad(store, type, id));
47 | } else if (Dashboard.User.detect(type)) {
48 | this.ajax('/users/%@'.fmt(id), this._storeLoad(store, type, id));
49 | }
50 | },
51 |
52 | findQuery: function(store, type, query, modelArray) {
53 | // watched repositories for a user
54 | if (Dashboard.Repository.detect(type) && query.username && 'watched' === query.type) {
55 | this.ajax('/users/%@/watched'.fmt(query.username), this._invoke(modelArray, 'load'));
56 |
57 | // repositories for a user
58 | } else if (Dashboard.Repository.detect(type) && query.username) {
59 | this.ajax('/users/%@/repos'.fmt(query.username), this._invoke(modelArray, 'load'));
60 |
61 | // events for a repository
62 | } else if (Dashboard.Event.detect(type) && query.repoName) {
63 | this.ajax('/repos/%@/events?page=%@'.fmt(query.repoName, query.page || 1), this._invoke(modelArray, 'load', query));
64 |
65 | // events for a user
66 | } else if (Dashboard.Event.detect(type) && query.username) {
67 | this.ajax('/users/%@/events?page=%@'.fmt(query.username, query.page || 1), this._invoke(modelArray, 'load', query));
68 | }
69 | }
70 | });
71 |
72 | Dashboard.GitHubFixtureAdpater = Dashboard.GitHubAdpater.extend({
73 | PREFIX: 'http://localhost:9292/app/tests/mock_response_data',
74 | _ajax: function(url, callback) {
75 | var encodedUrl = url.replace(/\?/, '%3F').replace(/\=/, '%3D');
76 | Ember.run.later(this, function() {
77 | Ember.$.ajax({
78 | url: this.PREFIX + encodedUrl + '.json',
79 | context: this,
80 | success: function(data) {
81 | callback.call(this, {meta: {}, data: data});
82 | }
83 | });
84 | }, 500);
85 | }
86 | });
--------------------------------------------------------------------------------
/app/tests/unit/handlebars_helpers_test.js:
--------------------------------------------------------------------------------
1 | require('dashboard/handlebars_helpers');
2 |
3 | module("{{echo}}");
4 |
5 | test("helper is available", function() {
6 | ok(Ember.Handlebars.helpers.echo, "echo helper is available");
7 | });
8 |
9 | test("returns the specified property", function() {
10 | var view = Ember.View.create({
11 | obj: Ember.Object.create({
12 | action: 'dingdong'
13 | }),
14 | template: Ember.Handlebars.compile('{{#with obj}}{{echo "action"}}{{/with}}')
15 | });
16 |
17 | Ember.run(function() {
18 | view.appendTo('#qunit-fixture');
19 | });
20 |
21 | equal(Ember.$('#qunit-fixture').text(), "dingdong", "echo helper outputs correct value");
22 | });
23 |
24 | module("{{trim}}");
25 |
26 | test("trim is available", function() {
27 | ok(Ember.Handlebars.helpers.trim, "trim helper is available");
28 | });
29 |
30 | test("outputs by default given string", function() {
31 | var view = Ember.View.create({
32 | property: 'mystring',
33 | template: Ember.Handlebars.compile('{{trim property}}')
34 | });
35 |
36 | Ember.run(function() {
37 | view.appendTo('#qunit-fixture');
38 | });
39 |
40 | equal(Ember.$('#qunit-fixture').text(), 'mystring', "by default doesn't trim anything");
41 | });
42 |
43 | test("trims string to specific length", function() {
44 | var view = Ember.View.create({
45 | property: 'mystring',
46 | template: Ember.Handlebars.compile('{{trim property length=2}}')
47 | });
48 |
49 | Ember.run(function() {
50 | view.appendTo('#qunit-fixture');
51 | });
52 |
53 | equal(Ember.$('#qunit-fixture').text(), 'my', "it trims the string to given length");
54 | });
55 |
56 | test("exceeding length does not blow up the handler",
57 | function() {
58 | var view = Ember.View.create({
59 | property: 'mystring',
60 | template: Ember.Handlebars.compile('{{trim property length=100}}')
61 | });
62 |
63 | Ember.run(function() {
64 | view.appendTo('#qunit-fixture');
65 | });
66 |
67 | equal(Ember.$('#qunit-fixture').text(), 'mystring', "it trims the string to given length");
68 | });
69 |
70 | module("{{ago}}");
71 |
72 | test("helper is available", function() {
73 | ok(Ember.Handlebars.helpers.ago, "ago helper is availbale");
74 | });
75 |
76 | test("ago can handle Date objects", function() {
77 | var view = Ember.View.create({
78 | time: new Date(),
79 | template: Ember.Handlebars.compile('{{ago time}}')
80 | });
81 |
82 | Ember.run(function() {
83 | view.appendTo('#qunit-fixture');
84 | });
85 |
86 | equal(Ember.$('#qunit-fixture').text(), "a few seconds ago", "ago helper outputs correct value");
87 | });
88 |
89 | test("ago can handle numbers", function() {
90 | var view = Ember.View.create({
91 | time: new Date().getTime(),
92 | template: Ember.Handlebars.compile('{{ago time}}')
93 | });
94 |
95 | Ember.run(function() {
96 | view.appendTo('#qunit-fixture');
97 | });
98 |
99 | equal(Ember.$('#qunit-fixture').text(), "a few seconds ago", "ago helper outputs correct value");
100 | });
101 |
102 | test("ago respects 'isSeconds' parameter", function() {
103 | var nowInSeconds = new Date().getTime() / 1000;
104 | var view = Ember.View.create({
105 | time: nowInSeconds,
106 | template: Ember.Handlebars.compile('{{ago time isSeconds=true}}')
107 | });
108 |
109 | Ember.run(function() {
110 | view.appendTo('#qunit-fixture');
111 | });
112 |
113 | equal(Ember.$('#qunit-fixture').text(), "a few seconds ago", "ago helper outputs correct value");
114 | });
--------------------------------------------------------------------------------
/app/tests/integration/store_adapter_test.js:
--------------------------------------------------------------------------------
1 | require('dashboard/store');
2 | require('dashboard/model');
3 | require('dashboard/github_adapter');
4 |
5 | var store, adapter;
6 |
7 | var callLater = function(func) {
8 | stop();
9 | var args = arguments;
10 | setTimeout(function() {
11 | args = Array.prototype.slice.call(args, 1);
12 | func.apply(this, args);
13 | start();
14 | }, 50);
15 | };
16 |
17 | module("Dashboard.Store and Dashboard.GitHubAdpater integration tests", {
18 | setup: function() {
19 | adapter = Dashboard.GitHubAdpater.create();
20 | store = Dashboard.Store.create({ adapter: adapter });
21 | },
22 | teardown: function() {
23 | adapter.destroy();
24 | store.destroy();
25 | }
26 | });
27 |
28 | test("find works for getting a repository", 4, function() {
29 | // it is expected that a find for Dashboard.Repository
30 | // invokes ajax
31 | adapter.reopen({
32 | ajax: function(url, callback) {
33 | ok(true, "ajax has been called");
34 | equal(url, '/repos/pangratz/ember.js', 'correct url has been passed');
35 |
36 | callback({full_name: 'pangratz/ember.js'});
37 | }
38 | });
39 |
40 | var repo = store.find(Dashboard.Repository, 'pangratz/ember.js');
41 | ok(repo);
42 | equal(repo.get('id'), 'pangratz/ember.js', 'id is set immediately');
43 | });
44 |
45 | test("find works for getting a user", 4, function() {
46 | // it is expected that a find for Dashboard.User
47 | // invokes ajax
48 | adapter.reopen({
49 | ajax: function(url, callback) {
50 | ok(true, "ajax has been called");
51 | equal(url, '/users/pangratz', 'correct url has been passed');
52 |
53 | callback({login: 'pangratz'});
54 | }
55 | });
56 |
57 | var user = store.find(Dashboard.User, 'pangratz');
58 | ok(user, 'user is found');
59 | ok(user.get('id'), 'pangratz', 'id is set immediately');
60 | });
61 |
62 | test("findQuery works for watched repositories", 4, function() {
63 | // it is expected that a findQuery for Dashboard.Repository
64 | // invokes `watchedRepositories` on adapter when type in the query is 'watched'
65 | adapter.reopen({
66 | ajax: function(url, callback) {
67 | ok(true, "watched repositories has been called");
68 | equal(url, '/users/pangratz/watched', 'correct url has been passed');
69 |
70 | callLater(callback, [{full_name: 'first/repo'}, {full_name: 'second/repo'}]);
71 | }
72 | });
73 |
74 | var watchedRepos = store.findQuery(Dashboard.Repository, {
75 | username: 'pangratz',
76 | type: 'watched'
77 | });
78 | equal(watchedRepos.get('length'), 0, 'length is initially 0');
79 |
80 | watchedRepos.addArrayObserver(this, {
81 | didChange: function() {
82 | equal(watchedRepos.get('length'), 2);
83 | },
84 | willChange: Ember.K
85 | });
86 | });
87 |
88 | test("findQuery works for repositories", 4, function() {
89 | // it is expected that a findQuery for Dashboard.Repository
90 | // invokes `repositories` on adapter when there is just a username
91 | adapter.reopen({
92 | ajax: function(url, callback) {
93 | ok(true, "repositories has been called");
94 | equal(url, '/users/pangratz/repos', 'correct username has been passed');
95 |
96 | callLater(callback, [{full_name: 'pangratz/first'}, {full_name: 'pangratz/second'}]);
97 | }
98 | });
99 |
100 | var repos = store.findQuery(Dashboard.Repository, { username: 'pangratz' });
101 | equal(repos.get('length'), 0, 'length is initially 0');
102 |
103 | repos.addArrayObserver(this, {
104 | didChange: function(target, start, removed, added) {
105 | equal(repos.get('length'), 2, 'the array is populated');
106 | },
107 | willChange: Ember.K
108 | });
109 | });
--------------------------------------------------------------------------------
/app/tests/unit/github_adapter_test.js:
--------------------------------------------------------------------------------
1 | require('dashboard/github_adapter');
2 |
3 | var get = Ember.get;
4 |
5 | var ajaxCalled, ajaxParams;
6 | var ajaxUrl, ajaxCallback;
7 | var dataSource, store;
8 |
9 | var setupDataSource = {
10 | setup: function() {
11 | dataSource = Dashboard.GitHubAdpater.create({
12 | _ajax: function(url, callback) {
13 | ajaxCalled = true;
14 | ajaxUrl = url;
15 | ajaxCallback = callback;
16 | }
17 | });
18 | store = DS.Store.create({
19 | adapter: dataSource,
20 | revision: 4
21 | });
22 | },
23 | teardowm: function() {
24 | ajaxCallback = ajaxParams = null;
25 | ajaxUrl = ajaxCallback = null;
26 | dataSource.destroy();
27 | store.destroy();
28 | }
29 | };
30 | var callAjaxCallback = function(response) {
31 | ajaxCallback.call(dataSource, response);
32 | };
33 |
34 | module("Dashboard.GitHubAdpater", setupDataSource);
35 |
36 | test("is defined", function() {
37 | ok(Dashboard.GitHubAdpater, "is defined");
38 | ok(DS.Adapter.detect(Dashboard.GitHubAdpater), "it is a DS.Adapter");
39 | });
40 |
41 | test("ajax invokes _updateLimits", function() {
42 | var callbackCalled, callbackThis, callbackResponse;
43 |
44 | dataSource.ajax('/url', function(data) {
45 | callbackCalled = true;
46 | callbackThis = this;
47 | callbackResponse = data;
48 | });
49 |
50 | callAjaxCallback({
51 | meta: {
52 | 'X-RateLimit-Remaining': 42,
53 | 'X-RateLimit-Limit': 100
54 | },
55 | data: {
56 | a: 1,
57 | b: 'hello'
58 | }
59 | });
60 |
61 | equal(get(dataSource, 'remaining'), 42, "remaining has been read from repsonse.meta and set on the dataSource");
62 | equal(get(dataSource, 'limit'), 100, "limit has been read from repsonse.meta and set on the dataSource");
63 |
64 | ok(callbackCalled, 'callback has been called');
65 | deepEqual(callbackThis, dataSource, "this inside called callback is set to the adapter");
66 | deepEqual(callbackResponse, {a: 1, b: 'hello'}, "data of response is passed to callback");
67 | });
68 |
69 | module("Dashboard.GitHubAdpater#find for Dashboard.User calls ajax", setupDataSource);
70 |
71 | test("invokes ajax", function() {
72 | store.find(Dashboard.User, 'buster');
73 |
74 | ok(ajaxCalled, 'ajax has been called');
75 | deepEqual(ajaxUrl, '/users/buster', "the passed url is correct");
76 | });
77 |
78 | module("Dashboard.GitHubAdpater#findQuery for a user's watched repositories", setupDataSource);
79 |
80 | test("invokes ajax", function() {
81 | store.findQuery(Dashboard.Repository, { type: 'watched', username: 'buster' });
82 |
83 | ok(ajaxCalled, 'ajax has been called');
84 | deepEqual(ajaxUrl, '/users/buster/watched', "the passed url is correct");
85 | });
86 |
87 |
88 | module("Dashboard.GitHubAdpater#findQuery for a user's repositories calls ajax", setupDataSource);
89 |
90 | test("invokes ajax", function() {
91 | store.findQuery(Dashboard.Repository, {username: 'buster'});
92 |
93 | ok(ajaxCalled, 'ajax has been called');
94 | deepEqual(ajaxUrl, '/users/buster/repos', "the passed url is correct");
95 | });
96 |
97 | module("Dashboard.GitHubAdpater#findQuery for events for a repository calls ajax", setupDataSource);
98 |
99 | test("invokes ajax", function() {
100 | store.findQuery(Dashboard.Event, {username: 'buster', repository: 'bluth'});
101 |
102 | ok(ajaxCalled, 'ajax has been called');
103 | deepEqual(ajaxUrl, '/repos/buster/bluth/events', "the passed url is correct");
104 | });
105 |
106 | module("Dashboard.GitHubAdpater#findQuery for events for a user calls ajax", setupDataSource);
107 |
108 | test("invokes ajax", function() {
109 | store.findQuery(Dashboard.Event, {username: 'buster'});
110 |
111 | ok(ajaxCalled, 'ajax has been called');
112 | deepEqual(ajaxUrl, '/users/buster/events', "the passed url is correct");
113 | });
--------------------------------------------------------------------------------
/Assetfile:
--------------------------------------------------------------------------------
1 | APPNAME = 'dashboard'
2 |
3 | require 'json'
4 | require 'rake-pipeline-web-filters'
5 |
6 | WebFilters = Rake::Pipeline::Web::Filters
7 |
8 | class LoaderFilter < WebFilters::MinispadeFilter
9 | def generate_output(inputs, output)
10 | inputs.each do |input|
11 | code = input.read
12 | module_id = @module_id_generator.call(input)
13 | contents = "function(require) {\n#{code}\n}"
14 | ret = "\nloader.register('#{module_id}', #{contents});\n"
15 | output.write ret
16 | end
17 | end
18 | end
19 |
20 | class EmberAssertFilter < Filter
21 | def generate_output(inputs, output)
22 | inputs.each do |input|
23 | result = input.read
24 | result.gsub!(/ember_assert\((.*)\);/, '')
25 | output.write(result)
26 | end
27 | end
28 | end
29 |
30 | class HandlebarsFilter < Filter
31 | def generate_output(inputs, output)
32 | inputs.each do |input|
33 | code = input.read.to_json
34 | name = File.basename(input.path, '.handlebars')
35 | output.write "\nreturn Ember.Handlebars.compile(#{code});\n"
36 | end
37 | end
38 | end
39 |
40 | class VersionFilter < Filter
41 | def generate_output(inputs, output)
42 | version = File.read("VERSION").strip
43 | inputs.each do |input|
44 | result = input.read
45 | result.gsub!(/VERSION: '.*'/, "VERSION: '#{version}'")
46 | output.write(result)
47 | end
48 | end
49 | end
50 |
51 | class GitHubFixtureAdpater < Filter
52 | def generate_output(inputs, output)
53 | inputs.each do |input|
54 | result = input.read
55 | result.gsub!("adapter: Dashboard.GitHubAdpater.create()", "adapter: Dashboard.GitHubFixtureAdpater.create()")
56 | output.write(result)
57 | end
58 | end
59 | end
60 |
61 | output 'assets'
62 | input 'app' do
63 | match 'index.html' do
64 | copy 'index.html'
65 | end
66 |
67 | match 'lib/**/*.js' do
68 | if ENV["TEST_MODE"] == "functional"
69 | filter GitHubFixtureAdpater
70 | end
71 | filter VersionFilter
72 | filter LoaderFilter,
73 | :module_id_generator => proc { |input|
74 | input.path.sub(/^lib\//, "#{APPNAME}/").sub(/\.js$/, '')
75 | }
76 |
77 | if ENV['RAKEP_MODE'] == 'production'
78 | filter EmberAssertFilter
79 | uglify {|input| input}
80 | end
81 | concat 'js/app.js'
82 | end
83 |
84 | match 'vendor/**/*.js' do
85 | filter LoaderFilter,
86 | :module_id_generator => proc { |input|
87 | input.path.sub(/^vendor\//, '').sub(/\.js$/, '')
88 | }
89 |
90 | if ENV['RAKEP_MODE'] == 'production'
91 | filter EmberAssertFilter
92 | uglify {|input| input}
93 | end
94 | concat %w[
95 | vendor/jquery.js
96 | vendor/ember.js
97 | vendor/ember-data.js
98 | ], 'js/app.js'
99 | end
100 |
101 | match 'modules/**/*.js' do
102 | if ENV['RAKEP_MODE'] == 'production'
103 | filter EmberAssertFilter
104 | uglify {|input| input}
105 | end
106 | concat 'js/app.js'
107 | end
108 |
109 | match 'plugins/**/*.js' do
110 | if ENV['RAKEP_MODE'] == 'production'
111 | uglify {|input| input}
112 | end
113 | concat do |input|
114 | input.sub(/plugins\//, 'js/')
115 | end
116 | end
117 |
118 | match 'templates/**/*.handlebars' do
119 | filter HandlebarsFilter
120 | filter LoaderFilter,
121 | :module_id_generator => proc { |input|
122 | input.path.sub(/^templates\//, "#{APPNAME}/~templates/").sub(/\.handlebars$/, '')
123 | }
124 | if ENV['RAKEP_MODE'] == 'production'
125 | uglify {|input| input}
126 | end
127 | concat 'js/app.js'
128 | end
129 |
130 | match 'css/**/*.css' do
131 | if ENV['RAKEP_MODE'] == 'production'
132 | yui_css
133 | end
134 | concat ['bootstrap.css', 'main.css'], 'css/app.css'
135 | end
136 |
137 | match 'css/**/*.scss' do
138 | sass
139 | if ENV['RAKEP_MODE'] == 'production'
140 | yui_css
141 | end
142 | concat 'css/app.css'
143 | end
144 |
145 | match "static/**/*" do
146 | concat do |input|
147 | input.sub(/static\//, '')
148 | end
149 | end
150 | end
151 |
152 | output 'test_assets'
153 | input 'app' do
154 | match 'tests/{unit,integration}/**/*.js' do
155 | filter LoaderFilter,
156 | :module_id_generator => proc { |input|
157 | input.path.sub(/^lib\//, "#{APPNAME}/").sub(/\.js$/, '')
158 | }
159 | concat 'app-tests.js'
160 | end
161 | end
162 |
163 | # vim: filetype=ruby
164 |
--------------------------------------------------------------------------------
/app/tests/mock_response_data/repos/pangratz/ember.js.json:
--------------------------------------------------------------------------------
1 | {
2 | "clone_url": "https://github.com/pangratz/ember.js.git",
3 | "open_issues": 0,
4 | "has_issues": false,
5 | "owner": {
6 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab",
7 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
8 | "login": "pangratz",
9 | "url": "https://api.github.com/users/pangratz",
10 | "id": 341877
11 | },
12 | "pushed_at": "2012-08-09T06:08:27Z",
13 | "forks": 0,
14 | "language": "JavaScript",
15 | "full_name": "pangratz/ember.js",
16 | "parent": {
17 | "clone_url": "https://github.com/emberjs/ember.js.git",
18 | "open_issues": 112,
19 | "has_issues": true,
20 | "owner": {
21 | "gravatar_id": "792333d2bad390e8a2e23f5e2f41f214",
22 | "avatar_url": "https://secure.gravatar.com/avatar/792333d2bad390e8a2e23f5e2f41f214?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-orgs.png",
23 | "login": "emberjs",
24 | "url": "https://api.github.com/users/emberjs",
25 | "id": 1253363
26 | },
27 | "pushed_at": "2012-08-10T22:03:48Z",
28 | "forks": 530,
29 | "language": "JavaScript",
30 | "full_name": "emberjs/ember.js",
31 | "ssh_url": "git@github.com:emberjs/ember.js.git",
32 | "git_url": "git://github.com/emberjs/ember.js.git",
33 | "has_downloads": true,
34 | "homepage": "http://www.emberjs.com",
35 | "watchers_count": 4235,
36 | "forks_count": 530,
37 | "size": 11549,
38 | "fork": false,
39 | "svn_url": "https://github.com/emberjs/ember.js",
40 | "created_at": "2011-05-25T23:39:40Z",
41 | "open_issues_count": 112,
42 | "description": "Ember.js - A JavaScript framework for creating ambitious web applications",
43 | "name": "ember.js",
44 | "url": "https://api.github.com/repos/emberjs/ember.js",
45 | "mirror_url": null,
46 | "has_wiki": true,
47 | "html_url": "https://github.com/emberjs/ember.js",
48 | "private": false,
49 | "id": 1801829,
50 | "watchers": 4235,
51 | "updated_at": "2012-08-12T11:32:29Z"
52 | },
53 | "master_branch": "master",
54 | "ssh_url": "git@github.com:pangratz/ember.js.git",
55 | "git_url": "git://github.com/pangratz/ember.js.git",
56 | "has_downloads": true,
57 | "homepage": "http://www.emberjs.com",
58 | "source": {
59 | "clone_url": "https://github.com/emberjs/ember.js.git",
60 | "open_issues": 112,
61 | "has_issues": true,
62 | "owner": {
63 | "gravatar_id": "792333d2bad390e8a2e23f5e2f41f214",
64 | "avatar_url": "https://secure.gravatar.com/avatar/792333d2bad390e8a2e23f5e2f41f214?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-orgs.png",
65 | "login": "emberjs",
66 | "url": "https://api.github.com/users/emberjs",
67 | "id": 1253363
68 | },
69 | "pushed_at": "2012-08-10T22:03:48Z",
70 | "forks": 530,
71 | "language": "JavaScript",
72 | "full_name": "emberjs/ember.js",
73 | "ssh_url": "git@github.com:emberjs/ember.js.git",
74 | "git_url": "git://github.com/emberjs/ember.js.git",
75 | "has_downloads": true,
76 | "homepage": "http://www.emberjs.com",
77 | "watchers_count": 4235,
78 | "forks_count": 530,
79 | "size": 11549,
80 | "fork": false,
81 | "svn_url": "https://github.com/emberjs/ember.js",
82 | "created_at": "2011-05-25T23:39:40Z",
83 | "open_issues_count": 112,
84 | "description": "Ember.js - A JavaScript framework for creating ambitious web applications",
85 | "name": "ember.js",
86 | "url": "https://api.github.com/repos/emberjs/ember.js",
87 | "mirror_url": null,
88 | "has_wiki": true,
89 | "html_url": "https://github.com/emberjs/ember.js",
90 | "private": false,
91 | "id": 1801829,
92 | "watchers": 4235,
93 | "updated_at": "2012-08-12T11:32:29Z"
94 | },
95 | "watchers_count": 1,
96 | "forks_count": 0,
97 | "size": 2211,
98 | "fork": true,
99 | "svn_url": "https://github.com/pangratz/ember.js",
100 | "created_at": "2011-12-16T20:12:37Z",
101 | "open_issues_count": 0,
102 | "description": "Ember.js - A JavaScript framework for creating ambitious web applications",
103 | "name": "ember.js",
104 | "url": "https://api.github.com/repos/pangratz/ember.js",
105 | "network_count": 530,
106 | "mirror_url": null,
107 | "has_wiki": true,
108 | "html_url": "https://github.com/pangratz/ember.js",
109 | "private": false,
110 | "id": 2997378,
111 | "watchers": 1,
112 | "updated_at": "2012-08-09T06:08:29Z"
113 | }
114 |
--------------------------------------------------------------------------------
/app/vendor/jquery.inview.js:
--------------------------------------------------------------------------------
1 | /**
2 | * author Christopher Blum
3 | * - based on the idea of Remy Sharp, http://remysharp.com/2009/01/26/element-in-view-event-plugin/
4 | * - forked from http://github.com/zuk/jquery.inview/
5 | */
6 | (function ($) {
7 | var inviewObjects = {}, viewportSize, viewportOffset,
8 | d = document, w = window, documentElement = d.documentElement, expando = $.expando;
9 |
10 | $.event.special.inview = {
11 | add: function(data) {
12 | inviewObjects[data.guid + "-" + this[expando]] = { data: data, $element: $(this) };
13 | },
14 |
15 | remove: function(data) {
16 | try { delete inviewObjects[data.guid + "-" + this[expando]]; } catch(e) {}
17 | }
18 | };
19 |
20 | function getViewportSize() {
21 | var mode, domObject, size = { height: w.innerHeight, width: w.innerWidth };
22 |
23 | // if this is correct then return it. iPad has compat Mode, so will
24 | // go into check clientHeight/clientWidth (which has the wrong value).
25 | if (!size.height) {
26 | mode = d.compatMode;
27 | if (mode || !$.support.boxModel) { // IE, Gecko
28 | domObject = mode === 'CSS1Compat' ?
29 | documentElement : // Standards
30 | d.body; // Quirks
31 | size = {
32 | height: domObject.clientHeight,
33 | width: domObject.clientWidth
34 | };
35 | }
36 | }
37 |
38 | return size;
39 | }
40 |
41 | function getViewportOffset() {
42 | return {
43 | top: w.pageYOffset || documentElement.scrollTop || d.body.scrollTop,
44 | left: w.pageXOffset || documentElement.scrollLeft || d.body.scrollLeft
45 | };
46 | }
47 |
48 | function checkInView() {
49 | var $elements = $(), elementsLength, i = 0;
50 |
51 | $.each(inviewObjects, function(i, inviewObject) {
52 | var selector = inviewObject.data.selector,
53 | $element = inviewObject.$element;
54 | $elements = $elements.add(selector ? $element.find(selector) : $element);
55 | });
56 |
57 | elementsLength = $elements.length;
58 | if (elementsLength) {
59 | viewportSize = viewportSize || getViewportSize();
60 | viewportOffset = viewportOffset || getViewportOffset();
61 |
62 | for (; i viewportOffset.top &&
86 | elementOffset.top < viewportOffset.top + viewportSize.height &&
87 | elementOffset.left + elementSize.width > viewportOffset.left &&
88 | elementOffset.left < viewportOffset.left + viewportSize.width) {
89 | visiblePartX = (viewportOffset.left > elementOffset.left ?
90 | 'right' : (viewportOffset.left + viewportSize.width) < (elementOffset.left + elementSize.width) ?
91 | 'left' : 'both');
92 | visiblePartY = (viewportOffset.top > elementOffset.top ?
93 | 'bottom' : (viewportOffset.top + viewportSize.height) < (elementOffset.top + elementSize.height) ?
94 | 'top' : 'both');
95 | visiblePartsMerged = visiblePartX + "-" + visiblePartY;
96 | if (!inView || inView !== visiblePartsMerged) {
97 | $element.data('inview', visiblePartsMerged).trigger('inview', [true, visiblePartX, visiblePartY]);
98 | }
99 | } else if (inView) {
100 | $element.data('inview', false).trigger('inview', [false]);
101 | }
102 | }
103 | }
104 | }
105 |
106 | $(w).bind("scroll resize", function() {
107 | viewportSize = viewportOffset = null;
108 | });
109 |
110 | // Use setInterval in order to also make sure this captures elements within
111 | // "overflow:scroll" elements or elements that appeared in the dom tree due to
112 | // dom manipulation and reflow
113 | // old: $(window).scroll(checkInView);
114 | //
115 | // By the way, iOS (iPad, iPhone, ...) seems to not execute, or at least delays
116 | // intervals while the user scrolls. Therefore the inview event might fire a bit late there
117 | setInterval(checkInView, 250);
118 | })(jQuery);
--------------------------------------------------------------------------------
/app/lib/router.js:
--------------------------------------------------------------------------------
1 | require('dashboard/core');
2 | require('dashboard/model');
3 |
4 | Dashboard.Router = Ember.Router.extend({
5 | root: Ember.Route.extend({
6 |
7 | gotoIndex: function(router) {
8 | // FIXME: somehow router.transitionTo('root.index') doesn't work
9 | router.route('/');
10 | },
11 |
12 | index: Ember.Route.extend({
13 | route: '/',
14 | connectOutlets: function(router) {
15 | router.route('/pangratz');
16 | }
17 | }),
18 |
19 | user: Ember.Route.extend({
20 | route: '/:user_id',
21 | connectOutlets: function(router, user) {
22 | router.set('userController.content', user);
23 | },
24 |
25 | index: Ember.Route.extend({
26 | route: '/',
27 | connectOutlets: function(router) {
28 | var username = router.get('userController.id');
29 | var store = router.get('store');
30 |
31 | router.get('eventsController').resetLoadMore();
32 | router.get('repositoriesController').resetLoadMore();
33 |
34 | // set current query
35 | var query = { username: username, isLoadedCallback: function() {
36 | router.set('eventsController.isLoading', false);
37 | }};
38 | router.set('eventsController.query', query);
39 | router.set('eventsController.isLoading', true);
40 |
41 | // get repositories for user
42 | var userRepositories = store.findQuery(Dashboard.Repository, { username: username });
43 |
44 | // get events performed by user
45 | var filter = function(data) {
46 | if (Ember.get(data, 'savedData.org.login') === username) { return true; }
47 | return Ember.get(data, 'savedData.actor.login') === username;
48 | };
49 | var userEvents = store.filter(Dashboard.Event, query, filter);
50 |
51 | // connect user with events and watched repositories
52 | router.get('applicationController').connectOutlet('user');
53 | router.get('userController').connectOutlet('repositories', 'repositories', userRepositories);
54 | router.get('userController').connectOutlet('events', 'events', userEvents);
55 | }
56 | }),
57 |
58 | repository: Ember.Route.extend({
59 | route: '/:repository',
60 | connectOutlets: function(router, context) {
61 | var username = router.get('userController.id');
62 | var repoName = context.repository;
63 | var name = '%@/%@'.fmt(username, repoName);
64 |
65 | router.get('eventsController').resetLoadMore();
66 |
67 | // set current query
68 | var query = {
69 | repoName: name,
70 | isLoadedCallback: function() {
71 | router.set('eventsController.isLoading', false);
72 | }
73 | };
74 | router.set('eventsController.query', query);
75 | router.set('eventsController.isLoading', true);
76 |
77 | // fetch repo for current user
78 | var repository = router.get('store').find(Dashboard.Repository, name);
79 |
80 | // get all events for this repository
81 | var filter = function(data) {
82 | return Ember.get(data, 'savedData.repo.name') === name;
83 | };
84 | var events = router.get('store').filter(Dashboard.Event, query, filter);
85 |
86 | // connect repository and events
87 | router.set('repositoryController.content', repository);
88 | router.get('applicationController').connectOutlet('repository');
89 | router.get('repositoryController').connectOutlet('events', 'events', events);
90 | }
91 | }),
92 |
93 | loadMoreEvents: function(router, page) {
94 | var query = router.get('eventsController.query');
95 | query.page = page;
96 | router.get('store').findQuery(Dashboard.Event, query);
97 | router.set('eventsController.isLoading', true);
98 | },
99 | loadMoreRepos: function(router, page) {
100 | var username = router.get('userController.id');
101 | var query = { username: username };
102 | var store = router.get('store').findQuery(Dashboard.Repository, query);
103 | },
104 |
105 | showUserOfEvent: function(router, evt) {
106 | var e = evt.context;
107 | var username = e.get('actor.login');
108 | router.route('/%@'.fmt(username));
109 | },
110 | showUserOfRepository: function(router, evt) {
111 | var repository = evt.context;
112 | var username = repository.get('owner.login');
113 | router.route('/%@'.fmt(username));
114 | },
115 |
116 | showRepository: function(router, evt) {
117 | var repository = evt.context;
118 | var full_name = repository.get('full_name');
119 | router.route('/%@'.fmt(full_name));
120 | },
121 | showRepositoryOfEvent: function(router, evt) {
122 | var e = evt.context;
123 | var full_name = e.get('repo.name');
124 | router.route('/%@'.fmt(full_name));
125 | }
126 | })
127 | })
128 | });
129 |
--------------------------------------------------------------------------------
/tests/qunit/qunit.css:
--------------------------------------------------------------------------------
1 | /**
2 | * QUnit v1.9.0 - A JavaScript Unit Testing Framework
3 | *
4 | * http://docs.jquery.com/QUnit
5 | *
6 | * Copyright (c) 2012 John Resig, Jörn Zaefferer
7 | * Dual licensed under the MIT (MIT-LICENSE.txt)
8 | * or GPL (GPL-LICENSE.txt) licenses.
9 | */
10 |
11 | /** Font Family and Sizes */
12 |
13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
15 | }
16 |
17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
18 | #qunit-tests { font-size: smaller; }
19 |
20 |
21 | /** Resets */
22 |
23 | #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
24 | margin: 0;
25 | padding: 0;
26 | }
27 |
28 |
29 | /** Header */
30 |
31 | #qunit-header {
32 | padding: 0.5em 0 0.5em 1em;
33 |
34 | color: #8699a4;
35 | background-color: #0d3349;
36 |
37 | font-size: 1.5em;
38 | line-height: 1em;
39 | font-weight: normal;
40 |
41 | border-radius: 5px 5px 0 0;
42 | -moz-border-radius: 5px 5px 0 0;
43 | -webkit-border-top-right-radius: 5px;
44 | -webkit-border-top-left-radius: 5px;
45 | }
46 |
47 | #qunit-header a {
48 | text-decoration: none;
49 | color: #c2ccd1;
50 | }
51 |
52 | #qunit-header a:hover,
53 | #qunit-header a:focus {
54 | color: #fff;
55 | }
56 |
57 | #qunit-testrunner-toolbar label {
58 | display: inline-block;
59 | padding: 0 .5em 0 .1em;
60 | }
61 |
62 | #qunit-banner {
63 | height: 5px;
64 | }
65 |
66 | #qunit-testrunner-toolbar {
67 | padding: 0.5em 0 0.5em 2em;
68 | color: #5E740B;
69 | background-color: #eee;
70 | }
71 |
72 | #qunit-userAgent {
73 | padding: 0.5em 0 0.5em 2.5em;
74 | background-color: #2b81af;
75 | color: #fff;
76 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
77 | }
78 |
79 |
80 | /** Tests: Pass/Fail */
81 |
82 | #qunit-tests {
83 | list-style-position: inside;
84 | }
85 |
86 | #qunit-tests li {
87 | padding: 0.4em 0.5em 0.4em 2.5em;
88 | border-bottom: 1px solid #fff;
89 | list-style-position: inside;
90 | }
91 |
92 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
93 | display: none;
94 | }
95 |
96 | #qunit-tests li strong {
97 | cursor: pointer;
98 | }
99 |
100 | #qunit-tests li a {
101 | padding: 0.5em;
102 | color: #c2ccd1;
103 | text-decoration: none;
104 | }
105 | #qunit-tests li a:hover,
106 | #qunit-tests li a:focus {
107 | color: #000;
108 | }
109 |
110 | #qunit-tests ol {
111 | margin-top: 0.5em;
112 | padding: 0.5em;
113 |
114 | background-color: #fff;
115 |
116 | border-radius: 5px;
117 | -moz-border-radius: 5px;
118 | -webkit-border-radius: 5px;
119 | }
120 |
121 | #qunit-tests table {
122 | border-collapse: collapse;
123 | margin-top: .2em;
124 | }
125 |
126 | #qunit-tests th {
127 | text-align: right;
128 | vertical-align: top;
129 | padding: 0 .5em 0 0;
130 | }
131 |
132 | #qunit-tests td {
133 | vertical-align: top;
134 | }
135 |
136 | #qunit-tests pre {
137 | margin: 0;
138 | white-space: pre-wrap;
139 | word-wrap: break-word;
140 | }
141 |
142 | #qunit-tests del {
143 | background-color: #e0f2be;
144 | color: #374e0c;
145 | text-decoration: none;
146 | }
147 |
148 | #qunit-tests ins {
149 | background-color: #ffcaca;
150 | color: #500;
151 | text-decoration: none;
152 | }
153 |
154 | /*** Test Counts */
155 |
156 | #qunit-tests b.counts { color: black; }
157 | #qunit-tests b.passed { color: #5E740B; }
158 | #qunit-tests b.failed { color: #710909; }
159 |
160 | #qunit-tests li li {
161 | padding: 5px;
162 | background-color: #fff;
163 | border-bottom: none;
164 | list-style-position: inside;
165 | }
166 |
167 | /*** Passing Styles */
168 |
169 | #qunit-tests li li.pass {
170 | color: #3c510c;
171 | background-color: #fff;
172 | border-left: 10px solid #C6E746;
173 | }
174 |
175 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
176 | #qunit-tests .pass .test-name { color: #366097; }
177 |
178 | #qunit-tests .pass .test-actual,
179 | #qunit-tests .pass .test-expected { color: #999999; }
180 |
181 | #qunit-banner.qunit-pass { background-color: #C6E746; }
182 |
183 | /*** Failing Styles */
184 |
185 | #qunit-tests li li.fail {
186 | color: #710909;
187 | background-color: #fff;
188 | border-left: 10px solid #EE5757;
189 | white-space: pre;
190 | }
191 |
192 | #qunit-tests > li:last-child {
193 | border-radius: 0 0 5px 5px;
194 | -moz-border-radius: 0 0 5px 5px;
195 | -webkit-border-bottom-right-radius: 5px;
196 | -webkit-border-bottom-left-radius: 5px;
197 | }
198 |
199 | #qunit-tests .fail { color: #000000; background-color: #EE5757; }
200 | #qunit-tests .fail .test-name,
201 | #qunit-tests .fail .module-name { color: #000000; }
202 |
203 | #qunit-tests .fail .test-actual { color: #EE5757; }
204 | #qunit-tests .fail .test-expected { color: green; }
205 |
206 | #qunit-banner.qunit-fail { background-color: #EE5757; }
207 |
208 |
209 | /** Result */
210 |
211 | #qunit-testresult {
212 | padding: 0.5em 0.5em 0.5em 2.5em;
213 |
214 | color: #2b81af;
215 | background-color: #D2E0E6;
216 |
217 | border-bottom: 1px solid white;
218 | }
219 | #qunit-testresult .module-name {
220 | font-weight: bold;
221 | }
222 |
223 | /** Fixture */
224 |
225 | #qunit-fixture {
226 | position: absolute;
227 | top: -10000px;
228 | left: -10000px;
229 | width: 1000px;
230 | height: 1000px;
231 | }
232 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | APPNAME = 'dashboard'
2 |
3 | require 'colored'
4 | require 'rake-pipeline'
5 | require 'versionomy'
6 | require 'rack'
7 |
8 | def pipeline
9 | Rake::Pipeline::Project.new('Assetfile')
10 | end
11 |
12 | def version
13 | File.read("VERSION").strip
14 | end
15 |
16 | desc "Clean #{APPNAME}"
17 | task :clean do
18 | pipeline.clean
19 | end
20 |
21 | desc "Build #{APPNAME}"
22 | task :build => :clean do
23 | pipeline.invoke
24 | end
25 |
26 | def download(path)
27 | last_slash = path.rindex("/")
28 | system "mkdir -p app/tests/mock_response_data/#{path[0..last_slash]}"
29 | system "wget https://api.github.com/#{path} -O app/tests/mock_response_data/#{path}.json"
30 | end
31 |
32 | def download_event(url, page = 1)
33 | folder = "app/tests/mock_response_data/#{url}"
34 | system "mkdir -p #{folder}"
35 | Dir.chdir folder do
36 | system "wget https://api.github.com/#{url}?page=#{page}.json"
37 | end
38 | end
39 |
40 | def download_user_event(user, page = 1)
41 | download_event("users/#{user}/events", page)
42 | end
43 |
44 | def download_repo_event(user, repo, page = 1)
45 | download_event("repos/#{user}/#{repo}/events", page)
46 | end
47 |
48 | task :download_mock_responses do
49 | download "users/pangratz"
50 | download "users/nokinen"
51 |
52 | download "users/pangratz/repos"
53 | download "users/nokinen/repos"
54 |
55 | (1..10).each {|page| download_user_event("pangratz", page)}
56 | (1..10).each {|page| download_user_event("nokinen", page)}
57 |
58 | download "repos/pangratz/ember.js"
59 | download "repos/pangratz/dashboard"
60 | download "repos/nokinen/fdc"
61 |
62 | (1..10).each {|page| download_repo_event("pangratz", "ember.js", page)}
63 | (1..10).each {|page| download_repo_event("pangratz", "dashboard", page)}
64 | (1..10).each {|page| download_repo_event("nokinen", "fdc", page)}
65 | end
66 |
67 | task :test => ["test:all"]
68 | namespace :test do
69 | task :all => [:unit, :functional]
70 |
71 | desc "Run casper.js tests"
72 | task :functional do
73 | ENV["TEST_MODE"] = "functional"
74 | Rake::Task["build"].invoke
75 |
76 | unless system("which casperjs > /dev/null 2>&1")
77 | abort "Casper.js is not installed. Download from http://casperjs.org/"
78 | end
79 |
80 | puts "start Rack server"
81 | pid = Thread.new do
82 | server = Rack::Server.new({
83 | :config => "config.ru",
84 | :Port => 9292,
85 | :Logger => nil
86 | })
87 | server.start
88 | end
89 |
90 | puts "Running #{APPNAME} Casper.js tests"
91 | success = system("casperjs test app/tests/functional/")
92 |
93 | pid.kill
94 |
95 | if success
96 | puts "Tests Passed".green
97 | else
98 | puts "Tests Failed".red
99 | exit(1)
100 | end
101 | end
102 |
103 | desc "Run Unit and Integration test"
104 | task :unit => :build do
105 | unless system("which phantomjs > /dev/null 2>&1")
106 | abort "PhantomJS is not installed. Download from http://phantomjs.org/"
107 | end
108 |
109 | cmd = "phantomjs tests/run-tests.js \"file://#{File.dirname(__FILE__)}/tests/index.html\""
110 |
111 | # Run the tests
112 | puts "Running #{APPNAME} tests"
113 | success = system(cmd)
114 |
115 | if success
116 | puts "Tests Passed".green
117 | else
118 | puts "Tests Failed".red
119 | exit(1)
120 | end
121 | end
122 | end
123 |
124 | desc "Bumps current version"
125 | task :bump_version do
126 | next_version = Versionomy.parse(version).bump(:tiny)
127 |
128 | print "Enter next version (#{next_version}): "
129 | user_input = STDIN.gets.chomp
130 | next_version = user_input unless user_input.empty?
131 |
132 | File.open('VERSION', 'w') { |f| f.write( next_version ) }
133 | system "git commit VERSION -m 'Bump to new version #{next_version}'"
134 | end
135 |
136 | desc "Tag current code with current version"
137 | task :release => :deploy do
138 | system "git tag -a v#{version}"
139 | system "git push --tag"
140 |
141 | Rake::Task["bump_version"].invoke
142 | end
143 |
144 | desc "deploy app"
145 | task :deploy => :clean do
146 | FileUtils.rm_rf "assets"
147 |
148 | ENV['RAKEP_MODE'] = "production"
149 | Rake::Task["build"].invoke
150 |
151 | origin = `git config remote.origin.url`.chomp
152 | username = `git config user.name`.chomp
153 | cd "assets" do
154 | system "git init"
155 | system "git remote add origin #{origin}"
156 | system "git checkout -b gh-pages"
157 | system "git add ."
158 | puts "\n## Commiting: Site updated at #{Time.now.utc}"
159 | message = "Site updated at #{Time.now.utc}"
160 | system "git commit -m \"#{message}\""
161 | puts "\n## Pushing generated website"
162 | system "git push origin gh-pages --force"
163 | puts "\n## Github Pages deploy complete -- http://#{username}.github.com/dashboard"
164 | end
165 | end
166 |
167 | namespace :upgrade do
168 | def download_ember(repo_name, source = repo_name, target = source)
169 | FileUtils.rm_rf "tmp/#{repo_name}"
170 | `git clone https://github.com/emberjs/#{repo_name} tmp/#{repo_name}`
171 | Dir.chdir("tmp/#{repo_name}") do
172 | `bundle install`
173 | `rake dist`
174 | end
175 | FileUtils.copy "tmp/#{repo_name}/dist/#{source}", "app/vendor/#{target}"
176 | FileUtils.rm_rf "tmp/#{repo_name}"
177 | end
178 |
179 | task :ember do
180 | download_ember("ember.js")
181 | end
182 |
183 | task :data do
184 | download_ember("data", "ember-data.js")
185 | end
186 |
187 | task :qunit do
188 | FileUtils.rm_rf "tmp/qunit"
189 | `git clone https://github.com/jquery/qunit tmp/qunit`
190 | Dir.chdir("tmp/qunit") do
191 | latest_tag = `git describe --abbrev=0 --tags`
192 | system "git checkout #{latest_tag}"
193 | end
194 | FileUtils.cp_r "tmp/qunit/qunit/.", "tests/qunit"
195 | FileUtils.rm_rf "tmp/qunit"
196 | end
197 |
198 | task :all => [:ember, :data, :qunit]
199 | end
200 |
201 | task :upgrade => ["upgrade:all"]
202 |
--------------------------------------------------------------------------------
/app/tests/mock_response_data/users/nokinen/repos.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "forks": 2,
4 | "language": "Ruby",
5 | "git_url": "git://github.com/nokinen/fdc.git",
6 | "created_at": "2012-05-14T22:32:28Z",
7 | "description": "A platform independent command-line utility written in Ruby for converting files in the avionics flight recorder data format (IGC) to the keyhole markup language (KML) for their display in applications such as Google Earth.",
8 | "ssh_url": "git@github.com:nokinen/fdc.git",
9 | "owner": {
10 | "login": "nokinen",
11 | "avatar_url": "https://secure.gravatar.com/avatar/3f60ae2a5febcca386fa08d6b4af43f0?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
12 | "url": "https://api.github.com/users/nokinen",
13 | "gravatar_id": "3f60ae2a5febcca386fa08d6b4af43f0",
14 | "id": 435811
15 | },
16 | "mirror_url": null,
17 | "has_downloads": true,
18 | "watchers_count": 2,
19 | "updated_at": "2012-06-21T18:23:39Z",
20 | "open_issues_count": 1,
21 | "forks_count": 2,
22 | "svn_url": "https://github.com/nokinen/fdc",
23 | "has_wiki": false,
24 | "html_url": "https://github.com/nokinen/fdc",
25 | "watchers": 2,
26 | "size": 296,
27 | "fork": false,
28 | "full_name": "nokinen/fdc",
29 | "clone_url": "https://github.com/nokinen/fdc.git",
30 | "name": "fdc",
31 | "url": "https://api.github.com/repos/nokinen/fdc",
32 | "open_issues": 1,
33 | "has_issues": true,
34 | "homepage": "https://rubygems.org/gems/fdc",
35 | "private": false,
36 | "id": 4329788,
37 | "pushed_at": "2012-06-21T18:23:39Z"
38 | },
39 | {
40 | "forks": 1,
41 | "language": "Ruby",
42 | "git_url": "git://github.com/nokinen/gamsjaga.git",
43 | "created_at": "2012-06-22T11:51:02Z",
44 | "master_branch": "master",
45 | "description": "A modern CMS for associations, clubs or school classes that is implemented with Ruby on Rails and Twitter Bootstrap.",
46 | "ssh_url": "git@github.com:nokinen/gamsjaga.git",
47 | "owner": {
48 | "login": "nokinen",
49 | "avatar_url": "https://secure.gravatar.com/avatar/3f60ae2a5febcca386fa08d6b4af43f0?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
50 | "url": "https://api.github.com/users/nokinen",
51 | "gravatar_id": "3f60ae2a5febcca386fa08d6b4af43f0",
52 | "id": 435811
53 | },
54 | "mirror_url": null,
55 | "has_downloads": true,
56 | "watchers_count": 2,
57 | "updated_at": "2012-08-06T21:22:44Z",
58 | "open_issues_count": 11,
59 | "forks_count": 1,
60 | "svn_url": "https://github.com/nokinen/gamsjaga",
61 | "has_wiki": false,
62 | "html_url": "https://github.com/nokinen/gamsjaga",
63 | "watchers": 2,
64 | "size": 944,
65 | "fork": false,
66 | "full_name": "nokinen/gamsjaga",
67 | "clone_url": "https://github.com/nokinen/gamsjaga.git",
68 | "name": "gamsjaga",
69 | "url": "https://api.github.com/repos/nokinen/gamsjaga",
70 | "open_issues": 11,
71 | "has_issues": true,
72 | "homepage": "",
73 | "private": false,
74 | "id": 4750558,
75 | "pushed_at": "2012-08-06T21:22:43Z"
76 | },
77 | {
78 | "forks": 2,
79 | "language": "Java",
80 | "git_url": "git://github.com/nokinen/lunch-oracle.git",
81 | "created_at": "2011-06-02T11:05:22Z",
82 | "description": "Oracle that decides where to have lunch on and near campus of JKU Linz",
83 | "ssh_url": "git@github.com:nokinen/lunch-oracle.git",
84 | "owner": {
85 | "login": "nokinen",
86 | "avatar_url": "https://secure.gravatar.com/avatar/3f60ae2a5febcca386fa08d6b4af43f0?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
87 | "url": "https://api.github.com/users/nokinen",
88 | "gravatar_id": "3f60ae2a5febcca386fa08d6b4af43f0",
89 | "id": 435811
90 | },
91 | "mirror_url": null,
92 | "has_downloads": true,
93 | "watchers_count": 2,
94 | "updated_at": "2011-10-04T15:35:44Z",
95 | "open_issues_count": 0,
96 | "forks_count": 2,
97 | "svn_url": "https://github.com/nokinen/lunch-oracle",
98 | "has_wiki": true,
99 | "html_url": "https://github.com/nokinen/lunch-oracle",
100 | "watchers": 2,
101 | "size": 120,
102 | "fork": false,
103 | "full_name": "nokinen/lunch-oracle",
104 | "clone_url": "https://github.com/nokinen/lunch-oracle.git",
105 | "name": "lunch-oracle",
106 | "url": "https://api.github.com/repos/nokinen/lunch-oracle",
107 | "open_issues": 0,
108 | "has_issues": true,
109 | "homepage": "",
110 | "private": false,
111 | "id": 1836682,
112 | "pushed_at": "2011-06-07T13:37:31Z"
113 | },
114 | {
115 | "forks": 0,
116 | "language": "Shell",
117 | "git_url": "git://github.com/nokinen/macosx-backup.git",
118 | "created_at": "2011-05-04T10:22:04Z",
119 | "description": "script for mac os x backup (Mail, Safari, Address Book, iCal, Dashboard, ...)",
120 | "ssh_url": "git@github.com:nokinen/macosx-backup.git",
121 | "owner": {
122 | "login": "nokinen",
123 | "avatar_url": "https://secure.gravatar.com/avatar/3f60ae2a5febcca386fa08d6b4af43f0?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
124 | "url": "https://api.github.com/users/nokinen",
125 | "gravatar_id": "3f60ae2a5febcca386fa08d6b4af43f0",
126 | "id": 435811
127 | },
128 | "mirror_url": null,
129 | "has_downloads": true,
130 | "watchers_count": 2,
131 | "updated_at": "2011-10-04T14:45:45Z",
132 | "open_issues_count": 0,
133 | "forks_count": 0,
134 | "svn_url": "https://github.com/nokinen/macosx-backup",
135 | "has_wiki": true,
136 | "html_url": "https://github.com/nokinen/macosx-backup",
137 | "watchers": 2,
138 | "size": 356,
139 | "fork": true,
140 | "full_name": "nokinen/macosx-backup",
141 | "clone_url": "https://github.com/nokinen/macosx-backup.git",
142 | "name": "macosx-backup",
143 | "url": "https://api.github.com/repos/nokinen/macosx-backup",
144 | "open_issues": 0,
145 | "has_issues": false,
146 | "homepage": "",
147 | "private": false,
148 | "id": 1700625,
149 | "pushed_at": "2011-05-10T08:43:55Z"
150 | },
151 | {
152 | "forks": 2,
153 | "language": "Objective-C",
154 | "git_url": "git://github.com/nokinen/minilogo-cocoa.git",
155 | "created_at": "2010-12-13T10:59:07Z",
156 | "description": "Minimal LOGO interpreter written in Objective-C with COCOA",
157 | "ssh_url": "git@github.com:nokinen/minilogo-cocoa.git",
158 | "owner": {
159 | "login": "nokinen",
160 | "avatar_url": "https://secure.gravatar.com/avatar/3f60ae2a5febcca386fa08d6b4af43f0?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
161 | "url": "https://api.github.com/users/nokinen",
162 | "gravatar_id": "3f60ae2a5febcca386fa08d6b4af43f0",
163 | "id": 435811
164 | },
165 | "mirror_url": null,
166 | "has_downloads": true,
167 | "watchers_count": 2,
168 | "updated_at": "2012-03-07T07:38:12Z",
169 | "open_issues_count": 4,
170 | "forks_count": 2,
171 | "svn_url": "https://github.com/nokinen/minilogo-cocoa",
172 | "has_wiki": true,
173 | "html_url": "https://github.com/nokinen/minilogo-cocoa",
174 | "watchers": 2,
175 | "size": 488,
176 | "fork": false,
177 | "full_name": "nokinen/minilogo-cocoa",
178 | "clone_url": "https://github.com/nokinen/minilogo-cocoa.git",
179 | "name": "minilogo-cocoa",
180 | "url": "https://api.github.com/repos/nokinen/minilogo-cocoa",
181 | "open_issues": 4,
182 | "has_issues": true,
183 | "homepage": "",
184 | "private": false,
185 | "id": 1164392,
186 | "pushed_at": "2011-04-05T20:11:09Z"
187 | },
188 | {
189 | "forks": 1,
190 | "language": "R",
191 | "git_url": "git://github.com/nokinen/msc-analysis.git",
192 | "created_at": "2012-01-19T09:07:55Z",
193 | "description": "R project to analyze data gathered from my msc evaluation",
194 | "ssh_url": "git@github.com:nokinen/msc-analysis.git",
195 | "owner": {
196 | "login": "nokinen",
197 | "avatar_url": "https://secure.gravatar.com/avatar/3f60ae2a5febcca386fa08d6b4af43f0?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
198 | "url": "https://api.github.com/users/nokinen",
199 | "gravatar_id": "3f60ae2a5febcca386fa08d6b4af43f0",
200 | "id": 435811
201 | },
202 | "mirror_url": null,
203 | "has_downloads": true,
204 | "watchers_count": 1,
205 | "updated_at": "2012-04-02T19:31:18Z",
206 | "open_issues_count": 0,
207 | "forks_count": 1,
208 | "svn_url": "https://github.com/nokinen/msc-analysis",
209 | "has_wiki": true,
210 | "html_url": "https://github.com/nokinen/msc-analysis",
211 | "watchers": 1,
212 | "size": 216,
213 | "fork": false,
214 | "full_name": "nokinen/msc-analysis",
215 | "clone_url": "https://github.com/nokinen/msc-analysis.git",
216 | "name": "msc-analysis",
217 | "url": "https://api.github.com/repos/nokinen/msc-analysis",
218 | "open_issues": 0,
219 | "has_issues": true,
220 | "homepage": "",
221 | "private": false,
222 | "id": 3216304,
223 | "pushed_at": "2012-04-02T19:26:55Z"
224 | },
225 | {
226 | "forks": 1,
227 | "language": "R",
228 | "git_url": "git://github.com/nokinen/msc-datagen.git",
229 | "created_at": "2011-10-21T14:34:47Z",
230 | "description": "R evaluation tool for evaluation of my master's thesis",
231 | "ssh_url": "git@github.com:nokinen/msc-datagen.git",
232 | "owner": {
233 | "login": "nokinen",
234 | "avatar_url": "https://secure.gravatar.com/avatar/3f60ae2a5febcca386fa08d6b4af43f0?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
235 | "url": "https://api.github.com/users/nokinen",
236 | "gravatar_id": "3f60ae2a5febcca386fa08d6b4af43f0",
237 | "id": 435811
238 | },
239 | "mirror_url": null,
240 | "has_downloads": true,
241 | "watchers_count": 1,
242 | "updated_at": "2012-06-10T19:36:49Z",
243 | "open_issues_count": 0,
244 | "forks_count": 1,
245 | "svn_url": "https://github.com/nokinen/msc-datagen",
246 | "has_wiki": true,
247 | "html_url": "https://github.com/nokinen/msc-datagen",
248 | "watchers": 1,
249 | "size": 228,
250 | "fork": false,
251 | "full_name": "nokinen/msc-datagen",
252 | "clone_url": "https://github.com/nokinen/msc-datagen.git",
253 | "name": "msc-datagen",
254 | "url": "https://api.github.com/repos/nokinen/msc-datagen",
255 | "open_issues": 0,
256 | "has_issues": true,
257 | "homepage": "",
258 | "private": false,
259 | "id": 2620805,
260 | "pushed_at": "2012-01-07T20:19:43Z"
261 | },
262 | {
263 | "forks": 0,
264 | "language": "Shell",
265 | "git_url": "git://github.com/nokinen/oh-my-zsh.git",
266 | "created_at": "2012-05-14T09:13:41Z",
267 | "description": "A community-driven framework for managing your zsh configuration. Includes 40+ optional plugins (rails, git, OSX, hub, capistrano, brew, ant, macports, etc), over 80 terminal themes to spice up your morning, and an auto-update tool so that makes it easy to keep up with the latest updates from the community.",
268 | "ssh_url": "git@github.com:nokinen/oh-my-zsh.git",
269 | "owner": {
270 | "login": "nokinen",
271 | "avatar_url": "https://secure.gravatar.com/avatar/3f60ae2a5febcca386fa08d6b4af43f0?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
272 | "url": "https://api.github.com/users/nokinen",
273 | "gravatar_id": "3f60ae2a5febcca386fa08d6b4af43f0",
274 | "id": 435811
275 | },
276 | "mirror_url": null,
277 | "has_downloads": true,
278 | "watchers_count": 1,
279 | "updated_at": "2012-05-14T09:21:25Z",
280 | "open_issues_count": 0,
281 | "forks_count": 0,
282 | "svn_url": "https://github.com/nokinen/oh-my-zsh",
283 | "has_wiki": true,
284 | "html_url": "https://github.com/nokinen/oh-my-zsh",
285 | "watchers": 1,
286 | "size": 112,
287 | "fork": true,
288 | "full_name": "nokinen/oh-my-zsh",
289 | "clone_url": "https://github.com/nokinen/oh-my-zsh.git",
290 | "name": "oh-my-zsh",
291 | "url": "https://api.github.com/repos/nokinen/oh-my-zsh",
292 | "open_issues": 0,
293 | "has_issues": false,
294 | "homepage": "http://twitter.com/ohmyzsh",
295 | "private": false,
296 | "id": 4321968,
297 | "pushed_at": "2012-05-14T09:21:25Z"
298 | },
299 | {
300 | "forks": 2,
301 | "language": null,
302 | "git_url": "git://github.com/nokinen/randi.git",
303 | "created_at": "2011-06-17T23:03:15Z",
304 | "description": "Random file selection in Mac OS X Finder",
305 | "ssh_url": "git@github.com:nokinen/randi.git",
306 | "owner": {
307 | "login": "nokinen",
308 | "avatar_url": "https://secure.gravatar.com/avatar/3f60ae2a5febcca386fa08d6b4af43f0?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
309 | "url": "https://api.github.com/users/nokinen",
310 | "gravatar_id": "3f60ae2a5febcca386fa08d6b4af43f0",
311 | "id": 435811
312 | },
313 | "mirror_url": null,
314 | "has_downloads": true,
315 | "watchers_count": 1,
316 | "updated_at": "2011-10-04T16:05:36Z",
317 | "open_issues_count": 1,
318 | "forks_count": 2,
319 | "svn_url": "https://github.com/nokinen/randi",
320 | "has_wiki": true,
321 | "html_url": "https://github.com/nokinen/randi",
322 | "watchers": 1,
323 | "size": 175,
324 | "fork": false,
325 | "full_name": "nokinen/randi",
326 | "clone_url": "https://github.com/nokinen/randi.git",
327 | "name": "randi",
328 | "url": "https://api.github.com/repos/nokinen/randi",
329 | "open_issues": 1,
330 | "has_issues": true,
331 | "homepage": "",
332 | "private": false,
333 | "id": 1913745,
334 | "pushed_at": "2011-09-27T07:22:35Z"
335 | }
336 | ]
337 |
--------------------------------------------------------------------------------
/app/tests/mock_response_data/repos/nokinen/fdc/events?page=8.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "created_at": "2012-05-16T11:02:45Z",
4 | "type": "IssuesEvent",
5 | "actor": {
6 | "login": "nokinen",
7 | "gravatar_id": "b72c639457d872c7c65edb4bb94d8003",
8 | "id": 435811,
9 | "url": "https://api.github.com/users/nokinen",
10 | "avatar_url": "https://secure.gravatar.com/avatar/b72c639457d872c7c65edb4bb94d8003?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png"
11 | },
12 | "payload": {
13 | "action": "opened",
14 | "issue": {
15 | "body": "",
16 | "number": 4,
17 | "updated_at": "2012-05-16T11:02:45Z",
18 | "created_at": "2012-05-16T11:02:45Z",
19 | "labels": [
20 |
21 | ],
22 | "assignee": null,
23 | "title": "Command line capabilities",
24 | "state": "open",
25 | "milestone": null,
26 | "user": {
27 | "login": "nokinen",
28 | "gravatar_id": "b72c639457d872c7c65edb4bb94d8003",
29 | "avatar_url": "https://secure.gravatar.com/avatar/b72c639457d872c7c65edb4bb94d8003?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
30 | "id": 435811,
31 | "url": "https://api.github.com/users/nokinen"
32 | },
33 | "id": 4602158,
34 | "closed_at": null,
35 | "comments": 0,
36 | "url": "https://api.github.com/repos/nokinen/igc-kml/issues/4",
37 | "html_url": "https://github.com/nokinen/igc-kml/issues/4",
38 | "pull_request": {
39 | "patch_url": null,
40 | "diff_url": null,
41 | "html_url": null
42 | }
43 | }
44 | },
45 | "public": true,
46 | "repo": {
47 | "id": 4329788,
48 | "url": "https://api.github.com/repos/nokinen/igc-kml",
49 | "name": "nokinen/igc-kml"
50 | },
51 | "id": "1552653720"
52 | },
53 | {
54 | "created_at": "2012-05-16T11:02:16Z",
55 | "type": "IssuesEvent",
56 | "actor": {
57 | "login": "nokinen",
58 | "gravatar_id": "b72c639457d872c7c65edb4bb94d8003",
59 | "id": 435811,
60 | "url": "https://api.github.com/users/nokinen",
61 | "avatar_url": "https://secure.gravatar.com/avatar/b72c639457d872c7c65edb4bb94d8003?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png"
62 | },
63 | "payload": {
64 | "action": "closed",
65 | "issue": {
66 | "body": "",
67 | "number": 3,
68 | "updated_at": "2012-05-16T11:02:15Z",
69 | "created_at": "2012-05-14T23:26:30Z",
70 | "labels": [
71 |
72 | ],
73 | "assignee": null,
74 | "title": "Make use of regular expressions for parsing .icg",
75 | "state": "closed",
76 | "milestone": null,
77 | "user": {
78 | "login": "nokinen",
79 | "gravatar_id": "b72c639457d872c7c65edb4bb94d8003",
80 | "avatar_url": "https://secure.gravatar.com/avatar/b72c639457d872c7c65edb4bb94d8003?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
81 | "id": 435811,
82 | "url": "https://api.github.com/users/nokinen"
83 | },
84 | "id": 4574881,
85 | "closed_at": "2012-05-16T11:02:15Z",
86 | "comments": 0,
87 | "url": "https://api.github.com/repos/nokinen/igc-kml/issues/3",
88 | "html_url": "https://github.com/nokinen/igc-kml/issues/3",
89 | "pull_request": {
90 | "patch_url": null,
91 | "diff_url": null,
92 | "html_url": null
93 | }
94 | }
95 | },
96 | "public": true,
97 | "repo": {
98 | "id": 4329788,
99 | "url": "https://api.github.com/repos/nokinen/igc-kml",
100 | "name": "nokinen/igc-kml"
101 | },
102 | "id": "1552653603"
103 | },
104 | {
105 | "created_at": "2012-05-16T11:02:15Z",
106 | "type": "IssuesEvent",
107 | "actor": {
108 | "login": "nokinen",
109 | "gravatar_id": "b72c639457d872c7c65edb4bb94d8003",
110 | "id": 435811,
111 | "url": "https://api.github.com/users/nokinen",
112 | "avatar_url": "https://secure.gravatar.com/avatar/b72c639457d872c7c65edb4bb94d8003?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png"
113 | },
114 | "payload": {
115 | "action": "closed",
116 | "issue": {
117 | "body": "Supports timestamps through ",
118 | "number": 1,
119 | "updated_at": "2012-05-16T11:02:15Z",
120 | "created_at": "2012-05-14T23:22:37Z",
121 | "labels": [
122 |
123 | ],
124 | "assignee": null,
125 | "title": "Use for KLM",
126 | "state": "closed",
127 | "milestone": null,
128 | "user": {
129 | "login": "nokinen",
130 | "gravatar_id": "b72c639457d872c7c65edb4bb94d8003",
131 | "avatar_url": "https://secure.gravatar.com/avatar/b72c639457d872c7c65edb4bb94d8003?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
132 | "id": 435811,
133 | "url": "https://api.github.com/users/nokinen"
134 | },
135 | "id": 4574838,
136 | "closed_at": "2012-05-16T11:02:15Z",
137 | "comments": 0,
138 | "url": "https://api.github.com/repos/nokinen/igc-kml/issues/1",
139 | "html_url": "https://github.com/nokinen/igc-kml/issues/1",
140 | "pull_request": {
141 | "patch_url": null,
142 | "diff_url": null,
143 | "html_url": null
144 | }
145 | }
146 | },
147 | "public": true,
148 | "repo": {
149 | "id": 4329788,
150 | "url": "https://api.github.com/repos/nokinen/igc-kml",
151 | "name": "nokinen/igc-kml"
152 | },
153 | "id": "1552653602"
154 | },
155 | {
156 | "created_at": "2012-05-16T11:00:51Z",
157 | "type": "PushEvent",
158 | "actor": {
159 | "login": "nokinen",
160 | "gravatar_id": "b72c639457d872c7c65edb4bb94d8003",
161 | "id": 435811,
162 | "url": "https://api.github.com/users/nokinen",
163 | "avatar_url": "https://secure.gravatar.com/avatar/b72c639457d872c7c65edb4bb94d8003?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png"
164 | },
165 | "payload": {
166 | "ref": "refs/heads/master",
167 | "size": 1,
168 | "commits": [
169 | {
170 | "author": {
171 | "email": "tobias.noiges@gmail.com",
172 | "name": "Tobias Noiges"
173 | },
174 | "sha": "a1e373a0c66706dd27bf1c732e92114f5e4e95ed",
175 | "message": "Refactor Converter\nImprove API to load igc files\nImprove API to write kml files\nModify constructor",
176 | "url": "https://api.github.com/repos/nokinen/igc-kml/commits/a1e373a0c66706dd27bf1c732e92114f5e4e95ed",
177 | "distinct": true
178 | }
179 | ],
180 | "push_id": 78703515,
181 | "head": "a1e373a0c66706dd27bf1c732e92114f5e4e95ed"
182 | },
183 | "public": true,
184 | "repo": {
185 | "id": 4329788,
186 | "url": "https://api.github.com/repos/nokinen/igc-kml",
187 | "name": "nokinen/igc-kml"
188 | },
189 | "id": "1552653224"
190 | },
191 | {
192 | "created_at": "2012-05-16T09:14:26Z",
193 | "type": "PushEvent",
194 | "actor": {
195 | "login": "nokinen",
196 | "gravatar_id": "b72c639457d872c7c65edb4bb94d8003",
197 | "id": 435811,
198 | "url": "https://api.github.com/users/nokinen",
199 | "avatar_url": "https://secure.gravatar.com/avatar/b72c639457d872c7c65edb4bb94d8003?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png"
200 | },
201 | "payload": {
202 | "ref": "refs/heads/master",
203 | "size": 1,
204 | "commits": [
205 | {
206 | "author": {
207 | "email": "tobias.noiges@gmail.com",
208 | "name": "Tobias Noiges"
209 | },
210 | "sha": "3584409aa3c6c482532c2f1d649d70b0e2e234cc",
211 | "message": "Fix wrong track tag case",
212 | "url": "https://api.github.com/repos/nokinen/igc-kml/commits/3584409aa3c6c482532c2f1d649d70b0e2e234cc",
213 | "distinct": true
214 | }
215 | ],
216 | "push_id": 78686043,
217 | "head": "3584409aa3c6c482532c2f1d649d70b0e2e234cc"
218 | },
219 | "public": true,
220 | "repo": {
221 | "id": 4329788,
222 | "url": "https://api.github.com/repos/nokinen/igc-kml",
223 | "name": "nokinen/igc-kml"
224 | },
225 | "id": "1552619622"
226 | },
227 | {
228 | "created_at": "2012-05-15T22:32:32Z",
229 | "type": "PushEvent",
230 | "actor": {
231 | "login": "nokinen",
232 | "gravatar_id": "b72c639457d872c7c65edb4bb94d8003",
233 | "id": 435811,
234 | "url": "https://api.github.com/users/nokinen",
235 | "avatar_url": "https://secure.gravatar.com/avatar/b72c639457d872c7c65edb4bb94d8003?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png"
236 | },
237 | "payload": {
238 | "ref": "refs/heads/master",
239 | "size": 1,
240 | "commits": [
241 | {
242 | "author": {
243 | "email": "tobias.noiges@gmail.com",
244 | "name": "Tobias Noiges"
245 | },
246 | "sha": "d6da6c0985e020597886aa2017546f68cfaf570b",
247 | "message": "Implement Converter and Location",
248 | "url": "https://api.github.com/repos/nokinen/igc-kml/commits/d6da6c0985e020597886aa2017546f68cfaf570b",
249 | "distinct": true
250 | }
251 | ],
252 | "push_id": 78609756,
253 | "head": "d6da6c0985e020597886aa2017546f68cfaf570b"
254 | },
255 | "public": true,
256 | "repo": {
257 | "id": 4329788,
258 | "url": "https://api.github.com/repos/nokinen/igc-kml",
259 | "name": "nokinen/igc-kml"
260 | },
261 | "id": "1552491937"
262 | },
263 | {
264 | "created_at": "2012-05-14T23:26:30Z",
265 | "type": "IssuesEvent",
266 | "actor": {
267 | "login": "nokinen",
268 | "gravatar_id": "b72c639457d872c7c65edb4bb94d8003",
269 | "id": 435811,
270 | "url": "https://api.github.com/users/nokinen",
271 | "avatar_url": "https://secure.gravatar.com/avatar/b72c639457d872c7c65edb4bb94d8003?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png"
272 | },
273 | "payload": {
274 | "action": "opened",
275 | "issue": {
276 | "body": "",
277 | "number": 3,
278 | "updated_at": "2012-05-14T23:26:30Z",
279 | "created_at": "2012-05-14T23:26:30Z",
280 | "labels": [
281 |
282 | ],
283 | "assignee": null,
284 | "title": "Make use of regular expressions for parsing .icg",
285 | "state": "open",
286 | "milestone": null,
287 | "user": {
288 | "login": "nokinen",
289 | "gravatar_id": "b72c639457d872c7c65edb4bb94d8003",
290 | "avatar_url": "https://secure.gravatar.com/avatar/b72c639457d872c7c65edb4bb94d8003?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
291 | "id": 435811,
292 | "url": "https://api.github.com/users/nokinen"
293 | },
294 | "id": 4574881,
295 | "closed_at": null,
296 | "comments": 0,
297 | "url": "https://api.github.com/repos/nokinen/igc-kml/issues/3",
298 | "html_url": "https://github.com/nokinen/igc-kml/issues/3",
299 | "pull_request": {
300 | "patch_url": null,
301 | "diff_url": null,
302 | "html_url": null
303 | }
304 | }
305 | },
306 | "public": true,
307 | "repo": {
308 | "id": 4329788,
309 | "url": "https://api.github.com/repos/nokinen/igc-kml",
310 | "name": "nokinen/igc-kml"
311 | },
312 | "id": "1552081698"
313 | },
314 | {
315 | "created_at": "2012-05-14T23:23:25Z",
316 | "type": "IssuesEvent",
317 | "actor": {
318 | "login": "nokinen",
319 | "gravatar_id": "b72c639457d872c7c65edb4bb94d8003",
320 | "id": 435811,
321 | "url": "https://api.github.com/users/nokinen",
322 | "avatar_url": "https://secure.gravatar.com/avatar/b72c639457d872c7c65edb4bb94d8003?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png"
323 | },
324 | "payload": {
325 | "action": "opened",
326 | "issue": {
327 | "body": "",
328 | "number": 2,
329 | "updated_at": "2012-05-14T23:23:23Z",
330 | "created_at": "2012-05-14T23:23:23Z",
331 | "labels": [
332 |
333 | ],
334 | "assignee": null,
335 | "title": "Sync .igc files on device with rsync",
336 | "state": "open",
337 | "milestone": null,
338 | "user": {
339 | "login": "nokinen",
340 | "gravatar_id": "b72c639457d872c7c65edb4bb94d8003",
341 | "avatar_url": "https://secure.gravatar.com/avatar/b72c639457d872c7c65edb4bb94d8003?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
342 | "id": 435811,
343 | "url": "https://api.github.com/users/nokinen"
344 | },
345 | "id": 4574846,
346 | "closed_at": null,
347 | "comments": 0,
348 | "url": "https://api.github.com/repos/nokinen/igc-kml/issues/2",
349 | "html_url": "https://github.com/nokinen/igc-kml/issues/2",
350 | "pull_request": {
351 | "patch_url": null,
352 | "diff_url": null,
353 | "html_url": null
354 | }
355 | }
356 | },
357 | "public": true,
358 | "repo": {
359 | "id": 4329788,
360 | "url": "https://api.github.com/repos/nokinen/igc-kml",
361 | "name": "nokinen/igc-kml"
362 | },
363 | "id": "1552080912"
364 | },
365 | {
366 | "created_at": "2012-05-14T23:22:38Z",
367 | "type": "IssuesEvent",
368 | "actor": {
369 | "login": "nokinen",
370 | "gravatar_id": "b72c639457d872c7c65edb4bb94d8003",
371 | "id": 435811,
372 | "url": "https://api.github.com/users/nokinen",
373 | "avatar_url": "https://secure.gravatar.com/avatar/b72c639457d872c7c65edb4bb94d8003?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png"
374 | },
375 | "payload": {
376 | "action": "opened",
377 | "issue": {
378 | "body": "Supports timestamps through ",
379 | "number": 1,
380 | "updated_at": "2012-05-14T23:22:37Z",
381 | "created_at": "2012-05-14T23:22:37Z",
382 | "labels": [
383 |
384 | ],
385 | "assignee": null,
386 | "title": "Use for KLM",
387 | "state": "open",
388 | "milestone": null,
389 | "user": {
390 | "login": "nokinen",
391 | "gravatar_id": "b72c639457d872c7c65edb4bb94d8003",
392 | "avatar_url": "https://secure.gravatar.com/avatar/b72c639457d872c7c65edb4bb94d8003?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
393 | "id": 435811,
394 | "url": "https://api.github.com/users/nokinen"
395 | },
396 | "id": 4574838,
397 | "closed_at": null,
398 | "comments": 0,
399 | "url": "https://api.github.com/repos/nokinen/igc-kml/issues/1",
400 | "html_url": "https://github.com/nokinen/igc-kml/issues/1",
401 | "pull_request": {
402 | "patch_url": null,
403 | "diff_url": null,
404 | "html_url": null
405 | }
406 | }
407 | },
408 | "public": true,
409 | "repo": {
410 | "id": 4329788,
411 | "url": "https://api.github.com/repos/nokinen/igc-kml",
412 | "name": "nokinen/igc-kml"
413 | },
414 | "id": "1552080732"
415 | },
416 | {
417 | "created_at": "2012-05-14T22:42:48Z",
418 | "type": "PushEvent",
419 | "actor": {
420 | "login": "nokinen",
421 | "gravatar_id": "b72c639457d872c7c65edb4bb94d8003",
422 | "id": 435811,
423 | "url": "https://api.github.com/users/nokinen",
424 | "avatar_url": "https://secure.gravatar.com/avatar/b72c639457d872c7c65edb4bb94d8003?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png"
425 | },
426 | "payload": {
427 | "ref": "refs/heads/master",
428 | "size": 1,
429 | "commits": [
430 | {
431 | "author": {
432 | "email": "tobias.noiges@gmail.com",
433 | "name": "Tobias Noiges"
434 | },
435 | "sha": "8c90c8509937891a8db11c970b3ce17d911f3b84",
436 | "message": "Add .rb and sample .igc files",
437 | "url": "https://api.github.com/repos/nokinen/igc-kml/commits/8c90c8509937891a8db11c970b3ce17d911f3b84",
438 | "distinct": true
439 | }
440 | ],
441 | "push_id": 78363549,
442 | "head": "8c90c8509937891a8db11c970b3ce17d911f3b84"
443 | },
444 | "public": true,
445 | "repo": {
446 | "id": 4329788,
447 | "url": "https://api.github.com/repos/nokinen/igc-kml",
448 | "name": "nokinen/igc-kml"
449 | },
450 | "id": "1552069606"
451 | },
452 | {
453 | "created_at": "2012-05-14T22:32:29Z",
454 | "type": "CreateEvent",
455 | "actor": {
456 | "login": "nokinen",
457 | "gravatar_id": "b72c639457d872c7c65edb4bb94d8003",
458 | "id": 435811,
459 | "url": "https://api.github.com/users/nokinen",
460 | "avatar_url": "https://secure.gravatar.com/avatar/b72c639457d872c7c65edb4bb94d8003?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png"
461 | },
462 | "payload": {
463 | "ref": null,
464 | "master_branch": "master",
465 | "ref_type": "repository",
466 | "description": "Ruby tool to convert IGC files to KML"
467 | },
468 | "public": true,
469 | "repo": {
470 | "id": 4329788,
471 | "url": "https://api.github.com/repos/nokinen/igc-kml",
472 | "name": "nokinen/igc-kml"
473 | },
474 | "id": "1552066543"
475 | }
476 | ]
477 |
--------------------------------------------------------------------------------
/app/tests/mock_response_data/repos/pangratz/dashboard/events?page=7.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "type": "PushEvent",
4 | "actor": {
5 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab",
6 | "login": "pangratz",
7 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
8 | "id": 341877,
9 | "url": "https://api.github.com/users/pangratz"
10 | },
11 | "public": true,
12 | "payload": {
13 | "before": "905f82d4f56901a91ffad5b7eaac9310857cde63",
14 | "push_id": 91895857,
15 | "size": 1,
16 | "ref": "refs/heads/dev",
17 | "commits": [
18 | {
19 | "message": "Update README",
20 | "author": {
21 | "email": "cmueller.418@gmail.com",
22 | "name": "pangratz"
23 | },
24 | "distinct": true,
25 | "sha": "9e6536acc7c5d15abdc597783d2bc872d3c2a9a6",
26 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/9e6536acc7c5d15abdc597783d2bc872d3c2a9a6"
27 | }
28 | ],
29 | "head": "9e6536acc7c5d15abdc597783d2bc872d3c2a9a6"
30 | },
31 | "created_at": "2012-07-21T21:16:07Z",
32 | "repo": {
33 | "id": 3625527,
34 | "url": "https://api.github.com/repos/pangratz/dashboard",
35 | "name": "pangratz/dashboard"
36 | },
37 | "id": "1576373209"
38 | },
39 | {
40 | "type": "PushEvent",
41 | "actor": {
42 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab",
43 | "login": "pangratz",
44 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
45 | "id": 341877,
46 | "url": "https://api.github.com/users/pangratz"
47 | },
48 | "public": true,
49 | "payload": {
50 | "before": "0cb88f4a69a42bf45cd66b134a2f74808e1fec06",
51 | "size": 1,
52 | "push_id": 91895578,
53 | "commits": [
54 | {
55 | "message": "Add link to dashboard",
56 | "author": {
57 | "email": "cmueller.418@gmail.com",
58 | "name": "pangratz"
59 | },
60 | "distinct": true,
61 | "sha": "905f82d4f56901a91ffad5b7eaac9310857cde63",
62 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/905f82d4f56901a91ffad5b7eaac9310857cde63"
63 | }
64 | ],
65 | "ref": "refs/heads/dev",
66 | "head": "905f82d4f56901a91ffad5b7eaac9310857cde63"
67 | },
68 | "created_at": "2012-07-21T21:12:19Z",
69 | "repo": {
70 | "id": 3625527,
71 | "url": "https://api.github.com/repos/pangratz/dashboard",
72 | "name": "pangratz/dashboard"
73 | },
74 | "id": "1576372694"
75 | },
76 | {
77 | "type": "PushEvent",
78 | "actor": {
79 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab",
80 | "login": "pangratz",
81 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
82 | "id": 341877,
83 | "url": "https://api.github.com/users/pangratz"
84 | },
85 | "public": true,
86 | "payload": {
87 | "before": "8c34b210f3dc0e677d7e86b8eddc937500eb4c04",
88 | "push_id": 91895426,
89 | "size": 1,
90 | "commits": [
91 | {
92 | "message": "Update link to blog post",
93 | "author": {
94 | "email": "cmueller.418@gmail.com",
95 | "name": "pangratz"
96 | },
97 | "distinct": true,
98 | "sha": "0cb88f4a69a42bf45cd66b134a2f74808e1fec06",
99 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/0cb88f4a69a42bf45cd66b134a2f74808e1fec06"
100 | }
101 | ],
102 | "ref": "refs/heads/dev",
103 | "head": "0cb88f4a69a42bf45cd66b134a2f74808e1fec06"
104 | },
105 | "created_at": "2012-07-21T21:10:25Z",
106 | "repo": {
107 | "id": 3625527,
108 | "url": "https://api.github.com/repos/pangratz/dashboard",
109 | "name": "pangratz/dashboard"
110 | },
111 | "id": "1576372439"
112 | },
113 | {
114 | "type": "PushEvent",
115 | "actor": {
116 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab",
117 | "login": "pangratz",
118 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
119 | "id": 341877,
120 | "url": "https://api.github.com/users/pangratz"
121 | },
122 | "public": true,
123 | "payload": {
124 | "before": "50056241432c4c593b982b371f3af69bf6b4fcb5",
125 | "push_id": 91895201,
126 | "size": 1,
127 | "commits": [
128 | {
129 | "message": "Site updated at 2012-07-21 21:07:11 UTC",
130 | "author": {
131 | "email": "cmueller.418@gmail.com",
132 | "name": "pangratz"
133 | },
134 | "distinct": true,
135 | "sha": "0f153aadfd65dc6a64419585783a373992acbe51",
136 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/0f153aadfd65dc6a64419585783a373992acbe51"
137 | }
138 | ],
139 | "ref": "refs/heads/gh-pages",
140 | "head": "0f153aadfd65dc6a64419585783a373992acbe51"
141 | },
142 | "created_at": "2012-07-21T21:07:18Z",
143 | "repo": {
144 | "id": 3625527,
145 | "url": "https://api.github.com/repos/pangratz/dashboard",
146 | "name": "pangratz/dashboard"
147 | },
148 | "id": "1576372086"
149 | },
150 | {
151 | "type": "CreateEvent",
152 | "actor": {
153 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab",
154 | "login": "pangratz",
155 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
156 | "id": 341877,
157 | "url": "https://api.github.com/users/pangratz"
158 | },
159 | "public": true,
160 | "payload": {
161 | "master_branch": "master",
162 | "ref_type": "branch",
163 | "ref": "master",
164 | "description": ""
165 | },
166 | "created_at": "2012-07-21T21:06:54Z",
167 | "repo": {
168 | "id": 3625527,
169 | "url": "https://api.github.com/repos/pangratz/dashboard",
170 | "name": "pangratz/dashboard"
171 | },
172 | "id": "1576372034"
173 | },
174 | {
175 | "type": "PushEvent",
176 | "actor": {
177 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab",
178 | "login": "pangratz",
179 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
180 | "id": 341877,
181 | "url": "https://api.github.com/users/pangratz"
182 | },
183 | "public": true,
184 | "payload": {
185 | "before": "1e7f5ddf8265e8706b2009b22da9aa439fc851be",
186 | "size": 8,
187 | "push_id": 91895058,
188 | "commits": [
189 | {
190 | "message": "Rename app to dashboard",
191 | "author": {
192 | "email": "cmueller.418@gmail.com",
193 | "name": "pangratz"
194 | },
195 | "distinct": true,
196 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/df7b88820e5a61e8bc355b791be4d9c4d64c1893",
197 | "sha": "df7b88820e5a61e8bc355b791be4d9c4d64c1893"
198 | },
199 | {
200 | "message": "Add upgrade tasks",
201 | "author": {
202 | "email": "cmueller.418@gmail.com",
203 | "name": "pangratz"
204 | },
205 | "distinct": true,
206 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/c5b0d769c1251b869bff4efe1abcfe185caec1ec",
207 | "sha": "c5b0d769c1251b869bff4efe1abcfe185caec1ec"
208 | },
209 | {
210 | "message": "Update Ember.js to latest version",
211 | "author": {
212 | "email": "cmueller.418@gmail.com",
213 | "name": "pangratz"
214 | },
215 | "distinct": true,
216 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/3180a0517dfac9fb0516ab2826657dc4099381b6",
217 | "sha": "3180a0517dfac9fb0516ab2826657dc4099381b6"
218 | },
219 | {
220 | "message": "Update Ember-Data to latest version",
221 | "author": {
222 | "email": "cmueller.418@gmail.com",
223 | "name": "pangratz"
224 | },
225 | "distinct": true,
226 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/0cf72317b77f3e0be9751ef41920eab1594320b3",
227 | "sha": "0cf72317b77f3e0be9751ef41920eab1594320b3"
228 | },
229 | {
230 | "message": "Update QUnit to version 1.9.0",
231 | "author": {
232 | "email": "cmueller.418@gmail.com",
233 | "name": "pangratz"
234 | },
235 | "distinct": true,
236 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/39cb423ecc321fb4186aa06ff68fd042bf2b45e0",
237 | "sha": "39cb423ecc321fb4186aa06ff68fd042bf2b45e0"
238 | },
239 | {
240 | "message": "Add handlebars to app/vendor since it's needed by latest Ember.js",
241 | "author": {
242 | "email": "cmueller.418@gmail.com",
243 | "name": "pangratz"
244 | },
245 | "distinct": true,
246 | "sha": "751dae14dabac2572d0f6770e73cdd6345e4a2ab",
247 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/751dae14dabac2572d0f6770e73cdd6345e4a2ab"
248 | },
249 | {
250 | "message": "Update LICENSE",
251 | "author": {
252 | "email": "cmueller.418@gmail.com",
253 | "name": "pangratz"
254 | },
255 | "distinct": true,
256 | "sha": "51507a6878e9194fe12f57a4d1f4ce5fd8d736cf",
257 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/51507a6878e9194fe12f57a4d1f4ce5fd8d736cf"
258 | },
259 | {
260 | "message": "Update README",
261 | "author": {
262 | "email": "cmueller.418@gmail.com",
263 | "name": "pangratz"
264 | },
265 | "distinct": true,
266 | "sha": "8c34b210f3dc0e677d7e86b8eddc937500eb4c04",
267 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/8c34b210f3dc0e677d7e86b8eddc937500eb4c04"
268 | }
269 | ],
270 | "ref": "refs/heads/dev",
271 | "head": "8c34b210f3dc0e677d7e86b8eddc937500eb4c04"
272 | },
273 | "created_at": "2012-07-21T21:05:22Z",
274 | "repo": {
275 | "id": 3625527,
276 | "url": "https://api.github.com/repos/pangratz/dashboard",
277 | "name": "pangratz/dashboard"
278 | },
279 | "id": "1576371828"
280 | },
281 | {
282 | "type": "PushEvent",
283 | "actor": {
284 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab",
285 | "login": "pangratz",
286 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
287 | "id": 341877,
288 | "url": "https://api.github.com/users/pangratz"
289 | },
290 | "public": true,
291 | "payload": {
292 | "before": "5f4a902232496ebb30503c9a952aca8a2545eb25",
293 | "push_id": 91892463,
294 | "size": 1,
295 | "commits": [
296 | {
297 | "message": "Site updated at 2012-07-21 20:29:40 UTC",
298 | "author": {
299 | "email": "cmueller.418@gmail.com",
300 | "name": "pangratz"
301 | },
302 | "distinct": true,
303 | "sha": "50056241432c4c593b982b371f3af69bf6b4fcb5",
304 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/50056241432c4c593b982b371f3af69bf6b4fcb5"
305 | }
306 | ],
307 | "ref": "refs/heads/gh-pages",
308 | "head": "50056241432c4c593b982b371f3af69bf6b4fcb5"
309 | },
310 | "created_at": "2012-07-21T20:29:45Z",
311 | "repo": {
312 | "id": 3625527,
313 | "url": "https://api.github.com/repos/pangratz/dashboard",
314 | "name": "pangratz/dashboard"
315 | },
316 | "id": "1576366959"
317 | },
318 | {
319 | "type": "PushEvent",
320 | "actor": {
321 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab",
322 | "login": "pangratz",
323 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
324 | "id": 341877,
325 | "url": "https://api.github.com/users/pangratz"
326 | },
327 | "public": true,
328 | "payload": {
329 | "before": "a1db8456696dcd97cc343144c7d192f10c050d7e",
330 | "size": 5,
331 | "push_id": 91892429,
332 | "commits": [
333 | {
334 | "message": "Add upgrade tasks",
335 | "author": {
336 | "email": "cmueller.418@gmail.com",
337 | "name": "pangratz"
338 | },
339 | "distinct": true,
340 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/d31d7140550e3d67fbf6b9175f35133b351f3da2",
341 | "sha": "d31d7140550e3d67fbf6b9175f35133b351f3da2"
342 | },
343 | {
344 | "message": "Update Ember.js to latest version",
345 | "author": {
346 | "email": "cmueller.418@gmail.com",
347 | "name": "pangratz"
348 | },
349 | "distinct": true,
350 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/859c18ac1a8067e30db499f0074eca2c607975dd",
351 | "sha": "859c18ac1a8067e30db499f0074eca2c607975dd"
352 | },
353 | {
354 | "message": "Update Ember-Data to latest version",
355 | "author": {
356 | "email": "cmueller.418@gmail.com",
357 | "name": "pangratz"
358 | },
359 | "distinct": true,
360 | "sha": "c140ad15965424cbd9ecadc32174a3d5b0860046",
361 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/c140ad15965424cbd9ecadc32174a3d5b0860046"
362 | },
363 | {
364 | "message": "Update QUnit to version 1.9.0",
365 | "author": {
366 | "email": "cmueller.418@gmail.com",
367 | "name": "pangratz"
368 | },
369 | "distinct": true,
370 | "sha": "bbff2cb9279c7a47c212467f786e9ef04b5d6518",
371 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/bbff2cb9279c7a47c212467f786e9ef04b5d6518"
372 | },
373 | {
374 | "message": "Add handlebars to app/vendor since it's needed by latest Ember.js",
375 | "author": {
376 | "email": "cmueller.418@gmail.com",
377 | "name": "pangratz"
378 | },
379 | "distinct": true,
380 | "sha": "1e7f5ddf8265e8706b2009b22da9aa439fc851be",
381 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/1e7f5ddf8265e8706b2009b22da9aa439fc851be"
382 | }
383 | ],
384 | "ref": "refs/heads/dev",
385 | "head": "1e7f5ddf8265e8706b2009b22da9aa439fc851be"
386 | },
387 | "created_at": "2012-07-21T20:29:22Z",
388 | "repo": {
389 | "id": 3625527,
390 | "url": "https://api.github.com/repos/pangratz/dashboard",
391 | "name": "pangratz/dashboard"
392 | },
393 | "id": "1576366909"
394 | },
395 | {
396 | "type": "CreateEvent",
397 | "actor": {
398 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab",
399 | "login": "pangratz",
400 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
401 | "id": 341877,
402 | "url": "https://api.github.com/users/pangratz"
403 | },
404 | "public": true,
405 | "payload": {
406 | "master_branch": "dev",
407 | "ref_type": "branch",
408 | "ref": "dev",
409 | "description": ""
410 | },
411 | "created_at": "2012-07-21T20:10:31Z",
412 | "repo": {
413 | "id": 3625527,
414 | "url": "https://api.github.com/repos/pangratz/dashboard",
415 | "name": "pangratz/dashboard"
416 | },
417 | "id": "1576364551"
418 | },
419 | {
420 | "type": "PushEvent",
421 | "actor": {
422 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab",
423 | "login": "pangratz",
424 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
425 | "id": 341877,
426 | "url": "https://api.github.com/users/pangratz"
427 | },
428 | "public": true,
429 | "payload": {
430 | "before": "ef958c028fa9528856afd72075eaad3c2e9132c9",
431 | "size": 1,
432 | "push_id": 91889329,
433 | "commits": [
434 | {
435 | "message": "Site updated at 2012-07-21 19:45:37 UTC",
436 | "author": {
437 | "email": "cmueller.418@gmail.com",
438 | "name": "pangratz"
439 | },
440 | "distinct": true,
441 | "sha": "5f4a902232496ebb30503c9a952aca8a2545eb25",
442 | "url": "https://api.github.com/repos/pangratz/dashboard/commits/5f4a902232496ebb30503c9a952aca8a2545eb25"
443 | }
444 | ],
445 | "ref": "refs/heads/gh-pages",
446 | "head": "5f4a902232496ebb30503c9a952aca8a2545eb25"
447 | },
448 | "created_at": "2012-07-21T19:45:42Z",
449 | "repo": {
450 | "id": 3625527,
451 | "url": "https://api.github.com/repos/pangratz/dashboard",
452 | "name": "pangratz/dashboard"
453 | },
454 | "id": "1576361369"
455 | },
456 | {
457 | "type": "CreateEvent",
458 | "actor": {
459 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab",
460 | "login": "pangratz",
461 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
462 | "id": 341877,
463 | "url": "https://api.github.com/users/pangratz"
464 | },
465 | "public": true,
466 | "payload": {
467 | "master_branch": "gh-pages",
468 | "ref_type": "branch",
469 | "ref": "gh-pages",
470 | "description": ""
471 | },
472 | "created_at": "2012-07-21T19:43:51Z",
473 | "repo": {
474 | "id": 3625527,
475 | "url": "https://api.github.com/repos/pangratz/dashboard",
476 | "name": "pangratz/dashboard"
477 | },
478 | "id": "1576361149"
479 | },
480 | {
481 | "type": "IssuesEvent",
482 | "actor": {
483 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab",
484 | "login": "pangratz",
485 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
486 | "id": 341877,
487 | "url": "https://api.github.com/users/pangratz"
488 | },
489 | "public": true,
490 | "payload": {
491 | "action": "opened",
492 | "issue": {
493 | "closed_at": null,
494 | "title": "Order repositories by last updated",
495 | "milestone": null,
496 | "user": {
497 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab",
498 | "login": "pangratz",
499 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
500 | "id": 341877,
501 | "url": "https://api.github.com/users/pangratz"
502 | },
503 | "pull_request": {
504 | "patch_url": null,
505 | "html_url": null,
506 | "diff_url": null
507 | },
508 | "id": 5297274,
509 | "updated_at": "2012-06-27T13:47:41Z",
510 | "number": 5,
511 | "assignee": null,
512 | "labels": [
513 | {
514 | "color": "444444",
515 | "url": "https://api.github.com/repos/pangratz/dashboard/labels/view",
516 | "name": "view"
517 | }
518 | ],
519 | "body": "",
520 | "comments": 0,
521 | "url": "https://api.github.com/repos/pangratz/dashboard/issues/5",
522 | "html_url": "https://github.com/pangratz/dashboard/issues/5",
523 | "state": "open",
524 | "created_at": "2012-06-27T13:47:41Z"
525 | }
526 | },
527 | "created_at": "2012-06-27T13:47:42Z",
528 | "repo": {
529 | "id": 3625527,
530 | "url": "https://api.github.com/repos/pangratz/dashboard",
531 | "name": "pangratz/dashboard"
532 | },
533 | "id": "1567169330"
534 | },
535 | {
536 | "type": "IssuesEvent",
537 | "actor": {
538 | "gravatar_id": "3cf2c8a8b86101d861efb7b14938513c",
539 | "login": "pangratz",
540 | "avatar_url": "https://secure.gravatar.com/avatar/3cf2c8a8b86101d861efb7b14938513c?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
541 | "id": 341877,
542 | "url": "https://api.github.com/users/pangratz"
543 | },
544 | "public": true,
545 | "payload": {
546 | "action": "opened",
547 | "issue": {
548 | "closed_at": null,
549 | "title": "Breadcrumb navigation",
550 | "milestone": null,
551 | "user": {
552 | "gravatar_id": "3cf2c8a8b86101d861efb7b14938513c",
553 | "login": "pangratz",
554 | "avatar_url": "https://secure.gravatar.com/avatar/3cf2c8a8b86101d861efb7b14938513c?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
555 | "id": 341877,
556 | "url": "https://api.github.com/users/pangratz"
557 | },
558 | "pull_request": {
559 | "patch_url": null,
560 | "html_url": null,
561 | "diff_url": null
562 | },
563 | "id": 3718679,
564 | "updated_at": "2012-03-19T21:41:11Z",
565 | "number": 4,
566 | "assignee": null,
567 | "labels": [
568 |
569 | ],
570 | "body": "which can be bookmarked",
571 | "comments": 0,
572 | "url": "https://api.github.com/repos/pangratz/dashboard/issues/4",
573 | "html_url": "https://github.com/pangratz/dashboard/issues/4",
574 | "state": "open",
575 | "created_at": "2012-03-19T21:41:11Z"
576 | }
577 | },
578 | "created_at": "2012-03-19T21:41:11Z",
579 | "repo": {
580 | "id": 3625527,
581 | "url": "https://api.github.com/repos/pangratz/dashboard",
582 | "name": "pangratz/dashboard"
583 | },
584 | "id": "1531831372"
585 | },
586 | {
587 | "type": "IssuesEvent",
588 | "actor": {
589 | "gravatar_id": "3cf2c8a8b86101d861efb7b14938513c",
590 | "login": "pangratz",
591 | "avatar_url": "https://secure.gravatar.com/avatar/3cf2c8a8b86101d861efb7b14938513c?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
592 | "id": 341877,
593 | "url": "https://api.github.com/users/pangratz"
594 | },
595 | "public": true,
596 | "payload": {
597 | "action": "opened",
598 | "issue": {
599 | "closed_at": null,
600 | "title": "Events can be filtered by type",
601 | "milestone": null,
602 | "user": {
603 | "gravatar_id": "3cf2c8a8b86101d861efb7b14938513c",
604 | "login": "pangratz",
605 | "avatar_url": "https://secure.gravatar.com/avatar/3cf2c8a8b86101d861efb7b14938513c?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
606 | "id": 341877,
607 | "url": "https://api.github.com/users/pangratz"
608 | },
609 | "pull_request": {
610 | "patch_url": null,
611 | "html_url": null,
612 | "diff_url": null
613 | },
614 | "id": 3718670,
615 | "updated_at": "2012-03-19T21:40:39Z",
616 | "number": 3,
617 | "assignee": null,
618 | "labels": [
619 |
620 | ],
621 | "body": "* all commits\r\n* all wiki entries\r\n* ...",
622 | "comments": 0,
623 | "url": "https://api.github.com/repos/pangratz/dashboard/issues/3",
624 | "html_url": "https://github.com/pangratz/dashboard/issues/3",
625 | "state": "open",
626 | "created_at": "2012-03-19T21:40:39Z"
627 | }
628 | },
629 | "created_at": "2012-03-19T21:40:41Z",
630 | "repo": {
631 | "id": 3625527,
632 | "url": "https://api.github.com/repos/pangratz/dashboard",
633 | "name": "pangratz/dashboard"
634 | },
635 | "id": "1531831141"
636 | },
637 | {
638 | "type": "IssuesEvent",
639 | "actor": {
640 | "gravatar_id": "3cf2c8a8b86101d861efb7b14938513c",
641 | "login": "pangratz",
642 | "avatar_url": "https://secure.gravatar.com/avatar/3cf2c8a8b86101d861efb7b14938513c?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
643 | "id": 341877,
644 | "url": "https://api.github.com/users/pangratz"
645 | },
646 | "public": true,
647 | "payload": {
648 | "action": "opened",
649 | "issue": {
650 | "closed_at": null,
651 | "title": "Show all events for a repository",
652 | "milestone": null,
653 | "user": {
654 | "gravatar_id": "3cf2c8a8b86101d861efb7b14938513c",
655 | "login": "pangratz",
656 | "avatar_url": "https://secure.gravatar.com/avatar/3cf2c8a8b86101d861efb7b14938513c?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
657 | "id": 341877,
658 | "url": "https://api.github.com/users/pangratz"
659 | },
660 | "pull_request": {
661 | "patch_url": null,
662 | "html_url": null,
663 | "diff_url": null
664 | },
665 | "id": 3718652,
666 | "updated_at": "2012-03-19T21:39:33Z",
667 | "number": 2,
668 | "assignee": null,
669 | "labels": [
670 |
671 | ],
672 | "body": "",
673 | "comments": 0,
674 | "url": "https://api.github.com/repos/pangratz/dashboard/issues/2",
675 | "html_url": "https://github.com/pangratz/dashboard/issues/2",
676 | "state": "open",
677 | "created_at": "2012-03-19T21:39:33Z"
678 | }
679 | },
680 | "created_at": "2012-03-19T21:39:34Z",
681 | "repo": {
682 | "id": 3625527,
683 | "url": "https://api.github.com/repos/pangratz/dashboard",
684 | "name": "pangratz/dashboard"
685 | },
686 | "id": "1531830742"
687 | },
688 | {
689 | "type": "IssuesEvent",
690 | "actor": {
691 | "gravatar_id": "3cf2c8a8b86101d861efb7b14938513c",
692 | "login": "pangratz",
693 | "avatar_url": "https://secure.gravatar.com/avatar/3cf2c8a8b86101d861efb7b14938513c?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
694 | "id": 341877,
695 | "url": "https://api.github.com/users/pangratz"
696 | },
697 | "public": true,
698 | "payload": {
699 | "action": "opened",
700 | "issue": {
701 | "closed_at": null,
702 | "title": "Show all events for an user",
703 | "milestone": null,
704 | "user": {
705 | "gravatar_id": "3cf2c8a8b86101d861efb7b14938513c",
706 | "login": "pangratz",
707 | "avatar_url": "https://secure.gravatar.com/avatar/3cf2c8a8b86101d861efb7b14938513c?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
708 | "id": 341877,
709 | "url": "https://api.github.com/users/pangratz"
710 | },
711 | "pull_request": {
712 | "patch_url": null,
713 | "html_url": null,
714 | "diff_url": null
715 | },
716 | "id": 3718649,
717 | "updated_at": "2012-03-19T21:39:19Z",
718 | "number": 1,
719 | "assignee": null,
720 | "labels": [
721 |
722 | ],
723 | "body": "",
724 | "comments": 0,
725 | "url": "https://api.github.com/repos/pangratz/dashboard/issues/1",
726 | "html_url": "https://github.com/pangratz/dashboard/issues/1",
727 | "state": "open",
728 | "created_at": "2012-03-19T21:39:19Z"
729 | }
730 | },
731 | "created_at": "2012-03-19T21:39:19Z",
732 | "repo": {
733 | "id": 3625527,
734 | "url": "https://api.github.com/repos/pangratz/dashboard",
735 | "name": "pangratz/dashboard"
736 | },
737 | "id": "1531830655"
738 | },
739 | {
740 | "type": "CreateEvent",
741 | "actor": {
742 | "gravatar_id": "3cf2c8a8b86101d861efb7b14938513c",
743 | "login": "pangratz",
744 | "avatar_url": "https://secure.gravatar.com/avatar/3cf2c8a8b86101d861efb7b14938513c?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
745 | "id": 341877,
746 | "url": "https://api.github.com/users/pangratz"
747 | },
748 | "public": true,
749 | "payload": {
750 | "master_branch": "master",
751 | "ref_type": "repository",
752 | "ref": null,
753 | "description": ""
754 | },
755 | "created_at": "2012-03-05T09:21:47Z",
756 | "repo": {
757 | "id": 3625527,
758 | "url": "https://api.github.com/repos/pangratz/dashboard",
759 | "name": "pangratz/dashboard"
760 | },
761 | "id": "1526403816"
762 | }
763 | ]
764 |
--------------------------------------------------------------------------------
/app/vendor/moment.js:
--------------------------------------------------------------------------------
1 | // moment.js
2 | // version : 1.6.2
3 | // author : Tim Wood
4 | // license : MIT
5 | // momentjs.com
6 |
7 | (function (Date, undefined) {
8 |
9 | var moment,
10 | VERSION = "1.6.2",
11 | round = Math.round, i,
12 | // internal storage for language config files
13 | languages = {},
14 | currentLanguage = 'en',
15 |
16 | // check for nodeJS
17 | hasModule = (typeof module !== 'undefined'),
18 |
19 | // parameters to check for on the lang config
20 | langConfigProperties = 'months|monthsShort|monthsParse|weekdays|weekdaysShort|longDateFormat|calendar|relativeTime|ordinal|meridiem'.split('|'),
21 |
22 | // ASP.NET json date format regex
23 | aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
24 |
25 | // format tokens
26 | formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|dddd?|do?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|SS?S?|zz?|ZZ?|LT|LL?L?L?)/g,
27 |
28 | // parsing tokens
29 | parseMultipleFormatChunker = /([0-9a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)/gi,
30 |
31 | // parsing token regexes
32 | parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99
33 | parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999
34 | parseTokenThreeDigits = /\d{3}/, // 000 - 999
35 | parseTokenFourDigits = /\d{4}/, // 0000 - 9999
36 | parseTokenWord = /[0-9a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+/i, // any word characters or numbers
37 | parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/i, // +00:00 -00:00 +0000 -0000 or Z
38 | parseTokenT = /T/i, // T (ISO seperator)
39 |
40 | // preliminary iso regex
41 | // 0000-00-00 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000
42 | isoRegex = /^\s*\d{4}-\d\d-\d\d(T(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/,
43 | isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
44 |
45 | // iso time formats and regexes
46 | isoTimes = [
47 | ['HH:mm:ss.S', /T\d\d:\d\d:\d\d\.\d{1,3}/],
48 | ['HH:mm:ss', /T\d\d:\d\d:\d\d/],
49 | ['HH:mm', /T\d\d:\d\d/],
50 | ['HH', /T\d\d/]
51 | ],
52 |
53 | // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"]
54 | parseTimezoneChunker = /([\+\-]|\d\d)/gi,
55 |
56 | // getter and setter names
57 | proxyGettersAndSetters = 'Month|Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
58 | unitMillisecondFactors = {
59 | 'Milliseconds' : 1,
60 | 'Seconds' : 1e3,
61 | 'Minutes' : 6e4,
62 | 'Hours' : 36e5,
63 | 'Days' : 864e5,
64 | 'Months' : 2592e6,
65 | 'Years' : 31536e6
66 | };
67 |
68 | // Moment prototype object
69 | function Moment(date, isUTC) {
70 | this._d = date;
71 | this._isUTC = !!isUTC;
72 | }
73 |
74 | function absRound(number) {
75 | if (number < 0) {
76 | return Math.ceil(number);
77 | } else {
78 | return Math.floor(number);
79 | }
80 | }
81 |
82 | // Duration Constructor
83 | function Duration(duration) {
84 | var data = this._data = {},
85 | years = duration.years || duration.y || 0,
86 | months = duration.months || duration.M || 0,
87 | weeks = duration.weeks || duration.w || 0,
88 | days = duration.days || duration.d || 0,
89 | hours = duration.hours || duration.h || 0,
90 | minutes = duration.minutes || duration.m || 0,
91 | seconds = duration.seconds || duration.s || 0,
92 | milliseconds = duration.milliseconds || duration.ms || 0;
93 |
94 | // representation for dateAddRemove
95 | this._milliseconds = milliseconds +
96 | seconds * 1e3 + // 1000
97 | minutes * 6e4 + // 1000 * 60
98 | hours * 36e5; // 1000 * 60 * 60
99 | // Because of dateAddRemove treats 24 hours as different from a
100 | // day when working around DST, we need to store them separately
101 | this._days = days +
102 | weeks * 7;
103 | // It is impossible translate months into days without knowing
104 | // which months you are are talking about, so we have to store
105 | // it separately.
106 | this._months = months +
107 | years * 12;
108 |
109 | // The following code bubbles up values, see the tests for
110 | // examples of what that means.
111 | data.milliseconds = milliseconds % 1000;
112 | seconds += absRound(milliseconds / 1000);
113 |
114 | data.seconds = seconds % 60;
115 | minutes += absRound(seconds / 60);
116 |
117 | data.minutes = minutes % 60;
118 | hours += absRound(minutes / 60);
119 |
120 | data.hours = hours % 24;
121 | days += absRound(hours / 24);
122 |
123 | days += weeks * 7;
124 | data.days = days % 30;
125 |
126 | months += absRound(days / 30);
127 |
128 | data.months = months % 12;
129 | years += absRound(months / 12);
130 |
131 | data.years = years;
132 | }
133 |
134 | // left zero fill a number
135 | // see http://jsperf.com/left-zero-filling for performance comparison
136 | function leftZeroFill(number, targetLength) {
137 | var output = number + '';
138 | while (output.length < targetLength) {
139 | output = '0' + output;
140 | }
141 | return output;
142 | }
143 |
144 | // helper function for _.addTime and _.subtractTime
145 | function addOrSubtractDurationFromMoment(mom, duration, isAdding) {
146 | var ms = duration._milliseconds,
147 | d = duration._days,
148 | M = duration._months,
149 | currentDate;
150 |
151 | if (ms) {
152 | mom._d.setTime(+mom + ms * isAdding);
153 | }
154 | if (d) {
155 | mom.date(mom.date() + d * isAdding);
156 | }
157 | if (M) {
158 | currentDate = mom.date();
159 | mom.date(1)
160 | .month(mom.month() + M * isAdding)
161 | .date(Math.min(currentDate, mom.daysInMonth()));
162 | }
163 | }
164 |
165 | // check if is an array
166 | function isArray(input) {
167 | return Object.prototype.toString.call(input) === '[object Array]';
168 | }
169 |
170 | // convert an array to a date.
171 | // the array should mirror the parameters below
172 | // note: all values past the year are optional and will default to the lowest possible value.
173 | // [year, month, day , hour, minute, second, millisecond]
174 | function dateFromArray(input) {
175 | return new Date(input[0], input[1] || 0, input[2] || 1, input[3] || 0, input[4] || 0, input[5] || 0, input[6] || 0);
176 | }
177 |
178 | // format date using native date object
179 | function formatMoment(m, inputString) {
180 | var currentMonth = m.month(),
181 | currentDate = m.date(),
182 | currentYear = m.year(),
183 | currentDay = m.day(),
184 | currentHours = m.hours(),
185 | currentMinutes = m.minutes(),
186 | currentSeconds = m.seconds(),
187 | currentMilliseconds = m.milliseconds(),
188 | currentZone = -m.zone(),
189 | ordinal = moment.ordinal,
190 | meridiem = moment.meridiem;
191 | // check if the character is a format
192 | // return formatted string or non string.
193 | //
194 | // uses switch/case instead of an object of named functions (like http://phpjs.org/functions/date:380)
195 | // for minification and performance
196 | // see http://jsperf.com/object-of-functions-vs-switch for performance comparison
197 | function replaceFunction(input) {
198 | // create a couple variables to be used later inside one of the cases.
199 | var a, b;
200 | switch (input) {
201 | // MONTH
202 | case 'M' :
203 | return currentMonth + 1;
204 | case 'Mo' :
205 | return (currentMonth + 1) + ordinal(currentMonth + 1);
206 | case 'MM' :
207 | return leftZeroFill(currentMonth + 1, 2);
208 | case 'MMM' :
209 | return moment.monthsShort[currentMonth];
210 | case 'MMMM' :
211 | return moment.months[currentMonth];
212 | // DAY OF MONTH
213 | case 'D' :
214 | return currentDate;
215 | case 'Do' :
216 | return currentDate + ordinal(currentDate);
217 | case 'DD' :
218 | return leftZeroFill(currentDate, 2);
219 | // DAY OF YEAR
220 | case 'DDD' :
221 | a = new Date(currentYear, currentMonth, currentDate);
222 | b = new Date(currentYear, 0, 1);
223 | return ~~ (((a - b) / 864e5) + 1.5);
224 | case 'DDDo' :
225 | a = replaceFunction('DDD');
226 | return a + ordinal(a);
227 | case 'DDDD' :
228 | return leftZeroFill(replaceFunction('DDD'), 3);
229 | // WEEKDAY
230 | case 'd' :
231 | return currentDay;
232 | case 'do' :
233 | return currentDay + ordinal(currentDay);
234 | case 'ddd' :
235 | return moment.weekdaysShort[currentDay];
236 | case 'dddd' :
237 | return moment.weekdays[currentDay];
238 | // WEEK OF YEAR
239 | case 'w' :
240 | a = new Date(currentYear, currentMonth, currentDate - currentDay + 5);
241 | b = new Date(a.getFullYear(), 0, 4);
242 | return ~~ ((a - b) / 864e5 / 7 + 1.5);
243 | case 'wo' :
244 | a = replaceFunction('w');
245 | return a + ordinal(a);
246 | case 'ww' :
247 | return leftZeroFill(replaceFunction('w'), 2);
248 | // YEAR
249 | case 'YY' :
250 | return leftZeroFill(currentYear % 100, 2);
251 | case 'YYYY' :
252 | return currentYear;
253 | // AM / PM
254 | case 'a' :
255 | return meridiem ? meridiem(currentHours, currentMinutes, false) : (currentHours > 11 ? 'pm' : 'am');
256 | case 'A' :
257 | return meridiem ? meridiem(currentHours, currentMinutes, true) : (currentHours > 11 ? 'PM' : 'AM');
258 | // 24 HOUR
259 | case 'H' :
260 | return currentHours;
261 | case 'HH' :
262 | return leftZeroFill(currentHours, 2);
263 | // 12 HOUR
264 | case 'h' :
265 | return currentHours % 12 || 12;
266 | case 'hh' :
267 | return leftZeroFill(currentHours % 12 || 12, 2);
268 | // MINUTE
269 | case 'm' :
270 | return currentMinutes;
271 | case 'mm' :
272 | return leftZeroFill(currentMinutes, 2);
273 | // SECOND
274 | case 's' :
275 | return currentSeconds;
276 | case 'ss' :
277 | return leftZeroFill(currentSeconds, 2);
278 | // MILLISECONDS
279 | case 'S' :
280 | return ~~ (currentMilliseconds / 100);
281 | case 'SS' :
282 | return leftZeroFill(~~(currentMilliseconds / 10), 2);
283 | case 'SSS' :
284 | return leftZeroFill(currentMilliseconds, 3);
285 | // TIMEZONE
286 | case 'Z' :
287 | return (currentZone < 0 ? '-' : '+') + leftZeroFill(~~(Math.abs(currentZone) / 60), 2) + ':' + leftZeroFill(~~(Math.abs(currentZone) % 60), 2);
288 | case 'ZZ' :
289 | return (currentZone < 0 ? '-' : '+') + leftZeroFill(~~(10 * Math.abs(currentZone) / 6), 4);
290 | // LONG DATES
291 | case 'L' :
292 | case 'LL' :
293 | case 'LLL' :
294 | case 'LLLL' :
295 | case 'LT' :
296 | return formatMoment(m, moment.longDateFormat[input]);
297 | // DEFAULT
298 | default :
299 | return input.replace(/(^\[)|(\\)|\]$/g, "");
300 | }
301 | }
302 | return inputString.replace(formattingTokens, replaceFunction);
303 | }
304 |
305 | // get the regex to find the next token
306 | function getParseRegexForToken(token) {
307 | switch (token) {
308 | case 'DDDD':
309 | return parseTokenThreeDigits;
310 | case 'YYYY':
311 | return parseTokenFourDigits;
312 | case 'S':
313 | case 'SS':
314 | case 'SSS':
315 | case 'DDD':
316 | return parseTokenOneToThreeDigits;
317 | case 'MMM':
318 | case 'MMMM':
319 | case 'ddd':
320 | case 'dddd':
321 | case 'a':
322 | case 'A':
323 | return parseTokenWord;
324 | case 'Z':
325 | case 'ZZ':
326 | return parseTokenTimezone;
327 | case 'T':
328 | return parseTokenT;
329 | case 'MM':
330 | case 'DD':
331 | case 'dd':
332 | case 'YY':
333 | case 'HH':
334 | case 'hh':
335 | case 'mm':
336 | case 'ss':
337 | case 'M':
338 | case 'D':
339 | case 'd':
340 | case 'H':
341 | case 'h':
342 | case 'm':
343 | case 's':
344 | return parseTokenOneOrTwoDigits;
345 | default :
346 | return new RegExp(token.replace('\\', ''));
347 | }
348 | }
349 |
350 | // function to convert string input to date
351 | function addTimeToArrayFromToken(token, input, datePartArray, config) {
352 | var a;
353 | //console.log('addTime', format, input);
354 | switch (token) {
355 | // MONTH
356 | case 'M' : // fall through to MM
357 | case 'MM' :
358 | datePartArray[1] = (input == null) ? 0 : ~~input - 1;
359 | break;
360 | case 'MMM' : // fall through to MMMM
361 | case 'MMMM' :
362 | for (a = 0; a < 12; a++) {
363 | if (moment.monthsParse[a].test(input)) {
364 | datePartArray[1] = a;
365 | break;
366 | }
367 | }
368 | break;
369 | // DAY OF MONTH
370 | case 'D' : // fall through to DDDD
371 | case 'DD' : // fall through to DDDD
372 | case 'DDD' : // fall through to DDDD
373 | case 'DDDD' :
374 | datePartArray[2] = ~~input;
375 | break;
376 | // YEAR
377 | case 'YY' :
378 | input = ~~input;
379 | datePartArray[0] = input + (input > 70 ? 1900 : 2000);
380 | break;
381 | case 'YYYY' :
382 | datePartArray[0] = ~~Math.abs(input);
383 | break;
384 | // AM / PM
385 | case 'a' : // fall through to A
386 | case 'A' :
387 | config.isPm = ((input + '').toLowerCase() === 'pm');
388 | break;
389 | // 24 HOUR
390 | case 'H' : // fall through to hh
391 | case 'HH' : // fall through to hh
392 | case 'h' : // fall through to hh
393 | case 'hh' :
394 | datePartArray[3] = ~~input;
395 | break;
396 | // MINUTE
397 | case 'm' : // fall through to mm
398 | case 'mm' :
399 | datePartArray[4] = ~~input;
400 | break;
401 | // SECOND
402 | case 's' : // fall through to ss
403 | case 'ss' :
404 | datePartArray[5] = ~~input;
405 | break;
406 | // MILLISECOND
407 | case 'S' :
408 | case 'SS' :
409 | case 'SSS' :
410 | datePartArray[6] = ~~ (('0.' + input) * 1000);
411 | break;
412 | // TIMEZONE
413 | case 'Z' : // fall through to ZZ
414 | case 'ZZ' :
415 | config.isUTC = true;
416 | a = (input + '').match(parseTimezoneChunker);
417 | if (a && a[1]) {
418 | config.tzh = ~~a[1];
419 | }
420 | if (a && a[2]) {
421 | config.tzm = ~~a[2];
422 | }
423 | // reverse offsets
424 | if (a && a[0] === '+') {
425 | config.tzh = -config.tzh;
426 | config.tzm = -config.tzm;
427 | }
428 | break;
429 | }
430 | }
431 |
432 | // date from string and format string
433 | function makeDateFromStringAndFormat(string, format) {
434 | var datePartArray = [0, 0, 1, 0, 0, 0, 0],
435 | config = {
436 | tzh : 0, // timezone hour offset
437 | tzm : 0 // timezone minute offset
438 | },
439 | tokens = format.match(formattingTokens),
440 | i, parsedInput;
441 |
442 | for (i = 0; i < tokens.length; i++) {
443 | parsedInput = (getParseRegexForToken(tokens[i]).exec(string) || [])[0];
444 | string = string.replace(getParseRegexForToken(tokens[i]), '');
445 | addTimeToArrayFromToken(tokens[i], parsedInput, datePartArray, config);
446 | }
447 | // handle am pm
448 | if (config.isPm && datePartArray[3] < 12) {
449 | datePartArray[3] += 12;
450 | }
451 | // if is 12 am, change hours to 0
452 | if (config.isPm === false && datePartArray[3] === 12) {
453 | datePartArray[3] = 0;
454 | }
455 | // handle timezone
456 | datePartArray[3] += config.tzh;
457 | datePartArray[4] += config.tzm;
458 | // return
459 | return config.isUTC ? new Date(Date.UTC.apply({}, datePartArray)) : dateFromArray(datePartArray);
460 | }
461 |
462 | // compare two arrays, return the number of differences
463 | function compareArrays(array1, array2) {
464 | var len = Math.min(array1.length, array2.length),
465 | lengthDiff = Math.abs(array1.length - array2.length),
466 | diffs = 0,
467 | i;
468 | for (i = 0; i < len; i++) {
469 | if (~~array1[i] !== ~~array2[i]) {
470 | diffs++;
471 | }
472 | }
473 | return diffs + lengthDiff;
474 | }
475 |
476 | // date from string and array of format strings
477 | function makeDateFromStringAndArray(string, formats) {
478 | var output,
479 | inputParts = string.match(parseMultipleFormatChunker) || [],
480 | formattedInputParts,
481 | scoreToBeat = 99,
482 | i,
483 | currentDate,
484 | currentScore;
485 | for (i = 0; i < formats.length; i++) {
486 | currentDate = makeDateFromStringAndFormat(string, formats[i]);
487 | formattedInputParts = formatMoment(new Moment(currentDate), formats[i]).match(parseMultipleFormatChunker) || [];
488 | currentScore = compareArrays(inputParts, formattedInputParts);
489 | if (currentScore < scoreToBeat) {
490 | scoreToBeat = currentScore;
491 | output = currentDate;
492 | }
493 | }
494 | return output;
495 | }
496 |
497 | // date from iso format
498 | function makeDateFromString(string) {
499 | var format = 'YYYY-MM-DDT',
500 | i;
501 | if (isoRegex.exec(string)) {
502 | for (i = 0; i < 4; i++) {
503 | if (isoTimes[i][1].exec(string)) {
504 | format += isoTimes[i][0];
505 | break;
506 | }
507 | }
508 | return parseTokenTimezone.exec(string) ?
509 | makeDateFromStringAndFormat(string, format + ' Z') :
510 | makeDateFromStringAndFormat(string, format);
511 | }
512 | return new Date(string);
513 | }
514 |
515 | // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
516 | function substituteTimeAgo(string, number, withoutSuffix, isFuture) {
517 | var rt = moment.relativeTime[string];
518 | return (typeof rt === 'function') ?
519 | rt(number || 1, !!withoutSuffix, string, isFuture) :
520 | rt.replace(/%d/i, number || 1);
521 | }
522 |
523 | function relativeTime(milliseconds, withoutSuffix) {
524 | var seconds = round(Math.abs(milliseconds) / 1000),
525 | minutes = round(seconds / 60),
526 | hours = round(minutes / 60),
527 | days = round(hours / 24),
528 | years = round(days / 365),
529 | args = seconds < 45 && ['s', seconds] ||
530 | minutes === 1 && ['m'] ||
531 | minutes < 45 && ['mm', minutes] ||
532 | hours === 1 && ['h'] ||
533 | hours < 22 && ['hh', hours] ||
534 | days === 1 && ['d'] ||
535 | days <= 25 && ['dd', days] ||
536 | days <= 45 && ['M'] ||
537 | days < 345 && ['MM', round(days / 30)] ||
538 | years === 1 && ['y'] || ['yy', years];
539 | args[2] = withoutSuffix;
540 | args[3] = milliseconds > 0;
541 | return substituteTimeAgo.apply({}, args);
542 | }
543 |
544 | moment = function (input, format) {
545 | if (input === null || input === '') {
546 | return null;
547 | }
548 | var date,
549 | matched,
550 | isUTC;
551 | // parse Moment object
552 | if (moment.isMoment(input)) {
553 | date = new Date(+input._d);
554 | isUTC = input._isUTC;
555 | // parse string and format
556 | } else if (format) {
557 | if (isArray(format)) {
558 | date = makeDateFromStringAndArray(input, format);
559 | } else {
560 | date = makeDateFromStringAndFormat(input, format);
561 | }
562 | // evaluate it as a JSON-encoded date
563 | } else {
564 | matched = aspNetJsonRegex.exec(input);
565 | date = input === undefined ? new Date() :
566 | matched ? new Date(+matched[1]) :
567 | input instanceof Date ? input :
568 | isArray(input) ? dateFromArray(input) :
569 | typeof input === 'string' ? makeDateFromString(input) :
570 | new Date(input);
571 | }
572 | return new Moment(date, isUTC);
573 | };
574 |
575 | // creating with utc
576 | moment.utc = function (input, format) {
577 | if (isArray(input)) {
578 | return new Moment(new Date(Date.UTC.apply({}, input)), true);
579 | }
580 | return (format && input) ?
581 | moment(input + ' +0000', format + ' Z').utc() :
582 | moment(input && !parseTokenTimezone.exec(input) ? input + '+0000' : input).utc();
583 | };
584 |
585 | // creating with unix timestamp (in seconds)
586 | moment.unix = function (input) {
587 | return moment(input * 1000);
588 | };
589 |
590 | // duration
591 | moment.duration = function (input, key) {
592 | var isDuration = moment.isDuration(input),
593 | isNumber = (typeof input === 'number'),
594 | duration = (isDuration ? input._data : (isNumber ? {} : input));
595 |
596 | if (isNumber) {
597 | if (key) {
598 | duration[key] = input;
599 | } else {
600 | duration.milliseconds = input;
601 | }
602 | }
603 |
604 | return new Duration(duration);
605 | };
606 |
607 | // humanizeDuration
608 | // This method is deprecated in favor of the new Duration object. Please
609 | // see the moment.duration method.
610 | moment.humanizeDuration = function (num, type, withSuffix) {
611 | return moment.duration(num, type === true ? null : type).humanize(type === true ? true : withSuffix);
612 | };
613 |
614 | // version number
615 | moment.version = VERSION;
616 |
617 | // default format
618 | moment.defaultFormat = isoFormat;
619 |
620 | // language switching and caching
621 | moment.lang = function (key, values) {
622 | var i, req,
623 | parse = [];
624 | if (!key) {
625 | return currentLanguage;
626 | }
627 | if (values) {
628 | for (i = 0; i < 12; i++) {
629 | parse[i] = new RegExp('^' + values.months[i] + '|^' + values.monthsShort[i].replace('.', ''), 'i');
630 | }
631 | values.monthsParse = values.monthsParse || parse;
632 | languages[key] = values;
633 | }
634 | if (languages[key]) {
635 | for (i = 0; i < langConfigProperties.length; i++) {
636 | moment[langConfigProperties[i]] = languages[key][langConfigProperties[i]] ||
637 | languages.en[langConfigProperties[i]];
638 | }
639 | currentLanguage = key;
640 | } else {
641 | if (hasModule) {
642 | req = require('./lang/' + key);
643 | moment.lang(key, req);
644 | }
645 | }
646 | };
647 |
648 | // set default language
649 | moment.lang('en', {
650 | months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
651 | monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
652 | weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
653 | weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
654 | longDateFormat : {
655 | LT : "h:mm A",
656 | L : "MM/DD/YYYY",
657 | LL : "MMMM D YYYY",
658 | LLL : "MMMM D YYYY LT",
659 | LLLL : "dddd, MMMM D YYYY LT"
660 | },
661 | meridiem : false,
662 | calendar : {
663 | sameDay : '[Today at] LT',
664 | nextDay : '[Tomorrow at] LT',
665 | nextWeek : 'dddd [at] LT',
666 | lastDay : '[Yesterday at] LT',
667 | lastWeek : '[last] dddd [at] LT',
668 | sameElse : 'L'
669 | },
670 | relativeTime : {
671 | future : "in %s",
672 | past : "%s ago",
673 | s : "a few seconds",
674 | m : "a minute",
675 | mm : "%d minutes",
676 | h : "an hour",
677 | hh : "%d hours",
678 | d : "a day",
679 | dd : "%d days",
680 | M : "a month",
681 | MM : "%d months",
682 | y : "a year",
683 | yy : "%d years"
684 | },
685 | ordinal : function (number) {
686 | var b = number % 10;
687 | return (~~ (number % 100 / 10) === 1) ? 'th' :
688 | (b === 1) ? 'st' :
689 | (b === 2) ? 'nd' :
690 | (b === 3) ? 'rd' : 'th';
691 | }
692 | });
693 |
694 | // compare moment object
695 | moment.isMoment = function (obj) {
696 | return obj instanceof Moment;
697 | };
698 |
699 | // for typechecking Duration objects
700 | moment.isDuration = function (obj) {
701 | return obj instanceof Duration;
702 | };
703 |
704 | // shortcut for prototype
705 | moment.fn = Moment.prototype = {
706 |
707 | clone : function () {
708 | return moment(this);
709 | },
710 |
711 | valueOf : function () {
712 | return +this._d;
713 | },
714 |
715 | unix : function () {
716 | return Math.floor(+this._d / 1000);
717 | },
718 |
719 | toString : function () {
720 | return this._d.toString();
721 | },
722 |
723 | toDate : function () {
724 | return this._d;
725 | },
726 |
727 | utc : function () {
728 | this._isUTC = true;
729 | return this;
730 | },
731 |
732 | local : function () {
733 | this._isUTC = false;
734 | return this;
735 | },
736 |
737 | format : function (inputString) {
738 | return formatMoment(this, inputString ? inputString : moment.defaultFormat);
739 | },
740 |
741 | add : function (input, val) {
742 | var dur = val ? moment.duration(+val, input) : moment.duration(input);
743 | addOrSubtractDurationFromMoment(this, dur, 1);
744 | return this;
745 | },
746 |
747 | subtract : function (input, val) {
748 | var dur = val ? moment.duration(+val, input) : moment.duration(input);
749 | addOrSubtractDurationFromMoment(this, dur, -1);
750 | return this;
751 | },
752 |
753 | diff : function (input, val, asFloat) {
754 | var inputMoment = this._isUTC ? moment(input).utc() : moment(input).local(),
755 | zoneDiff = (this.zone() - inputMoment.zone()) * 6e4,
756 | diff = this._d - inputMoment._d - zoneDiff,
757 | year = this.year() - inputMoment.year(),
758 | month = this.month() - inputMoment.month(),
759 | date = this.date() - inputMoment.date(),
760 | output;
761 | if (val === 'months') {
762 | output = year * 12 + month + date / 30;
763 | } else if (val === 'years') {
764 | output = year + (month + date / 30) / 12;
765 | } else {
766 | output = val === 'seconds' ? diff / 1e3 : // 1000
767 | val === 'minutes' ? diff / 6e4 : // 1000 * 60
768 | val === 'hours' ? diff / 36e5 : // 1000 * 60 * 60
769 | val === 'days' ? diff / 864e5 : // 1000 * 60 * 60 * 24
770 | val === 'weeks' ? diff / 6048e5 : // 1000 * 60 * 60 * 24 * 7
771 | diff;
772 | }
773 | return asFloat ? output : round(output);
774 | },
775 |
776 | from : function (time, withoutSuffix) {
777 | return moment.duration(this.diff(time)).humanize(!withoutSuffix);
778 | },
779 |
780 | fromNow : function (withoutSuffix) {
781 | return this.from(moment(), withoutSuffix);
782 | },
783 |
784 | calendar : function () {
785 | var diff = this.diff(moment().sod(), 'days', true),
786 | calendar = moment.calendar,
787 | allElse = calendar.sameElse,
788 | format = diff < -6 ? allElse :
789 | diff < -1 ? calendar.lastWeek :
790 | diff < 0 ? calendar.lastDay :
791 | diff < 1 ? calendar.sameDay :
792 | diff < 2 ? calendar.nextDay :
793 | diff < 7 ? calendar.nextWeek : allElse;
794 | return this.format(typeof format === 'function' ? format.apply(this) : format);
795 | },
796 |
797 | isLeapYear : function () {
798 | var year = this.year();
799 | return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
800 | },
801 |
802 | isDST : function () {
803 | return (this.zone() < moment([this.year()]).zone() ||
804 | this.zone() < moment([this.year(), 5]).zone());
805 | },
806 |
807 | day : function (input) {
808 | var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
809 | return input == null ? day :
810 | this.add({ d : input - day });
811 | },
812 |
813 | sod: function () {
814 | return moment(this)
815 | .hours(0)
816 | .minutes(0)
817 | .seconds(0)
818 | .milliseconds(0);
819 | },
820 |
821 | eod: function () {
822 | // end of day = start of day plus 1 day, minus 1 millisecond
823 | return this.sod().add({
824 | d : 1,
825 | ms : -1
826 | });
827 | },
828 |
829 | zone : function () {
830 | return this._isUTC ? 0 : this._d.getTimezoneOffset();
831 | },
832 |
833 | daysInMonth : function () {
834 | return moment(this).month(this.month() + 1).date(0).date();
835 | }
836 | };
837 |
838 | // helper for adding shortcuts
839 | function makeGetterAndSetter(name, key) {
840 | moment.fn[name] = function (input) {
841 | var utc = this._isUTC ? 'UTC' : '';
842 | if (input != null) {
843 | this._d['set' + utc + key](input);
844 | return this;
845 | } else {
846 | return this._d['get' + utc + key]();
847 | }
848 | };
849 | }
850 |
851 | // loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds)
852 | for (i = 0; i < proxyGettersAndSetters.length; i ++) {
853 | makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase(), proxyGettersAndSetters[i]);
854 | }
855 |
856 | // add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear')
857 | makeGetterAndSetter('year', 'FullYear');
858 |
859 | moment.duration.fn = Duration.prototype = {
860 | weeks : function () {
861 | return absRound(this.days() / 7);
862 | },
863 |
864 | valueOf : function () {
865 | return this._milliseconds +
866 | this._days * 864e5 +
867 | this._months * 2592e6;
868 | },
869 |
870 | humanize : function (withSuffix) {
871 | var difference = +this,
872 | rel = moment.relativeTime,
873 | output = relativeTime(difference, !withSuffix);
874 |
875 | if (withSuffix) {
876 | output = (difference <= 0 ? rel.past : rel.future).replace(/%s/i, output);
877 | }
878 |
879 | return output;
880 | }
881 | };
882 |
883 | function makeDurationGetter(name) {
884 | moment.duration.fn[name] = function () {
885 | return this._data[name];
886 | };
887 | }
888 |
889 | function makeDurationAsGetter(name, factor) {
890 | moment.duration.fn['as' + name] = function () {
891 | return +this / factor;
892 | };
893 | }
894 |
895 | for (i in unitMillisecondFactors) {
896 | if (unitMillisecondFactors.hasOwnProperty(i)) {
897 | makeDurationAsGetter(i, unitMillisecondFactors[i]);
898 | makeDurationGetter(i.toLowerCase());
899 | }
900 | }
901 |
902 | makeDurationAsGetter('Weeks', 6048e5);
903 |
904 | // CommonJS module is defined
905 | if (hasModule) {
906 | module.exports = moment;
907 | }
908 | /*global ender:false */
909 | if (typeof window !== 'undefined' && typeof ender === 'undefined') {
910 | window.moment = moment;
911 | }
912 | /*global define:false */
913 | if (typeof define === "function" && define.amd) {
914 | define("moment", [], function () {
915 | return moment;
916 | });
917 | }
918 | })(Date);
919 |
--------------------------------------------------------------------------------
/app/tests/mock_response_data/repos/pangratz/ember.js/events?page=9.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "created_at": "2012-05-02T08:02:38Z",
4 | "type": "DownloadEvent",
5 | "actor": {
6 | "id": 341877,
7 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
8 | "url": "https://api.github.com/users/pangratz",
9 | "login": "pangratz",
10 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
11 | },
12 | "payload": {
13 | "download": {
14 | "content_type": "application/json",
15 | "created_at": "2012-05-02T08:02:38Z",
16 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.js",
17 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230861",
18 | "id": 230861,
19 | "download_count": 0,
20 | "name": "ember-latest.js",
21 | "description": "Ember.js Master",
22 | "size": 581632
23 | }
24 | },
25 | "public": true,
26 | "repo": {
27 | "id": 2997378,
28 | "url": "https://api.github.com/repos/pangratz/ember.js",
29 | "name": "pangratz/ember.js"
30 | },
31 | "id": "1547862317"
32 | },
33 | {
34 | "created_at": "2012-05-02T07:58:42Z",
35 | "type": "PushEvent",
36 | "actor": {
37 | "id": 341877,
38 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
39 | "url": "https://api.github.com/users/pangratz",
40 | "login": "pangratz",
41 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
42 | },
43 | "payload": {
44 | "commits": [
45 | {
46 | "distinct": true,
47 | "author": {
48 | "email": "cmueller.418@gmail.com",
49 | "name": "pangratz"
50 | },
51 | "url": "https://api.github.com/repos/pangratz/ember.js/commits/b8d3cc5d931283eabcca1c580ebbe85e9b59bedf",
52 | "message": "Another test",
53 | "sha": "b8d3cc5d931283eabcca1c580ebbe85e9b59bedf"
54 | }
55 | ],
56 | "push_id": 76024626,
57 | "ref": "refs/heads/add_task_to_upload_latest_build",
58 | "head": "b8d3cc5d931283eabcca1c580ebbe85e9b59bedf",
59 | "size": 1
60 | },
61 | "public": true,
62 | "repo": {
63 | "id": 2997378,
64 | "url": "https://api.github.com/repos/pangratz/ember.js",
65 | "name": "pangratz/ember.js"
66 | },
67 | "id": "1547861382"
68 | },
69 | {
70 | "created_at": "2012-05-02T07:56:46Z",
71 | "type": "DownloadEvent",
72 | "actor": {
73 | "id": 341877,
74 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
75 | "url": "https://api.github.com/users/pangratz",
76 | "login": "pangratz",
77 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
78 | },
79 | "payload": {
80 | "download": {
81 | "content_type": "application/json",
82 | "created_at": "2012-05-02T07:56:45Z",
83 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.min.js",
84 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230859",
85 | "id": 230859,
86 | "download_count": 0,
87 | "name": "ember-latest.min.js",
88 | "description": "Ember.js Master (minified)",
89 | "size": 151552
90 | }
91 | },
92 | "public": true,
93 | "repo": {
94 | "id": 2997378,
95 | "url": "https://api.github.com/repos/pangratz/ember.js",
96 | "name": "pangratz/ember.js"
97 | },
98 | "id": "1547860860"
99 | },
100 | {
101 | "created_at": "2012-05-02T07:56:43Z",
102 | "type": "DownloadEvent",
103 | "actor": {
104 | "id": 341877,
105 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
106 | "url": "https://api.github.com/users/pangratz",
107 | "login": "pangratz",
108 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
109 | },
110 | "payload": {
111 | "download": {
112 | "content_type": "application/json",
113 | "created_at": "2012-05-02T07:56:43Z",
114 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.js",
115 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230858",
116 | "id": 230858,
117 | "download_count": 0,
118 | "name": "ember-latest.js",
119 | "description": "Ember.js Master",
120 | "size": 581632
121 | }
122 | },
123 | "public": true,
124 | "repo": {
125 | "id": 2997378,
126 | "url": "https://api.github.com/repos/pangratz/ember.js",
127 | "name": "pangratz/ember.js"
128 | },
129 | "id": "1547860854"
130 | },
131 | {
132 | "created_at": "2012-05-02T07:55:14Z",
133 | "type": "DownloadEvent",
134 | "actor": {
135 | "id": 341877,
136 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
137 | "url": "https://api.github.com/users/pangratz",
138 | "login": "pangratz",
139 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
140 | },
141 | "payload": {
142 | "download": {
143 | "content_type": "application/json",
144 | "created_at": "2012-05-02T07:55:11Z",
145 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.min.js",
146 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230855",
147 | "id": 230855,
148 | "download_count": 0,
149 | "name": "ember-latest.min.js",
150 | "description": "Ember.js Master (minified)",
151 | "size": 151552
152 | }
153 | },
154 | "public": true,
155 | "repo": {
156 | "id": 2997378,
157 | "url": "https://api.github.com/repos/pangratz/ember.js",
158 | "name": "pangratz/ember.js"
159 | },
160 | "id": "1547860556"
161 | },
162 | {
163 | "created_at": "2012-05-02T07:55:10Z",
164 | "type": "DownloadEvent",
165 | "actor": {
166 | "id": 341877,
167 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
168 | "url": "https://api.github.com/users/pangratz",
169 | "login": "pangratz",
170 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
171 | },
172 | "payload": {
173 | "download": {
174 | "content_type": "application/json",
175 | "created_at": "2012-05-02T07:55:10Z",
176 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.js",
177 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230854",
178 | "id": 230854,
179 | "download_count": 0,
180 | "name": "ember-latest.js",
181 | "description": "Ember.js Master",
182 | "size": 581632
183 | }
184 | },
185 | "public": true,
186 | "repo": {
187 | "id": 2997378,
188 | "url": "https://api.github.com/repos/pangratz/ember.js",
189 | "name": "pangratz/ember.js"
190 | },
191 | "id": "1547860537"
192 | },
193 | {
194 | "created_at": "2012-05-02T07:52:26Z",
195 | "type": "PushEvent",
196 | "actor": {
197 | "id": 341877,
198 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
199 | "url": "https://api.github.com/users/pangratz",
200 | "login": "pangratz",
201 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
202 | },
203 | "payload": {
204 | "commits": [
205 | {
206 | "distinct": true,
207 | "author": {
208 | "email": "cmueller.418@gmail.com",
209 | "name": "pangratz"
210 | },
211 | "url": "https://api.github.com/repos/pangratz/ember.js/commits/645a7bcff6a580fa477f2d221a6fccf3591267bb",
212 | "message": "Fix",
213 | "sha": "645a7bcff6a580fa477f2d221a6fccf3591267bb"
214 | }
215 | ],
216 | "push_id": 76023896,
217 | "ref": "refs/heads/add_task_to_upload_latest_build",
218 | "head": "645a7bcff6a580fa477f2d221a6fccf3591267bb",
219 | "size": 1
220 | },
221 | "public": true,
222 | "repo": {
223 | "id": 2997378,
224 | "url": "https://api.github.com/repos/pangratz/ember.js",
225 | "name": "pangratz/ember.js"
226 | },
227 | "id": "1547859907"
228 | },
229 | {
230 | "created_at": "2012-05-02T07:47:50Z",
231 | "type": "PushEvent",
232 | "actor": {
233 | "id": 341877,
234 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
235 | "url": "https://api.github.com/users/pangratz",
236 | "login": "pangratz",
237 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
238 | },
239 | "payload": {
240 | "commits": [
241 | {
242 | "distinct": true,
243 | "author": {
244 | "email": "cmueller.418@gmail.com",
245 | "name": "pangratz"
246 | },
247 | "url": "https://api.github.com/repos/pangratz/ember.js/commits/c1441189d985533f6c405e902f51058c43537291",
248 | "message": "Another test",
249 | "sha": "c1441189d985533f6c405e902f51058c43537291"
250 | }
251 | ],
252 | "push_id": 76023363,
253 | "ref": "refs/heads/add_task_to_upload_latest_build",
254 | "head": "c1441189d985533f6c405e902f51058c43537291",
255 | "size": 1
256 | },
257 | "public": true,
258 | "repo": {
259 | "id": 2997378,
260 | "url": "https://api.github.com/repos/pangratz/ember.js",
261 | "name": "pangratz/ember.js"
262 | },
263 | "id": "1547859000"
264 | },
265 | {
266 | "created_at": "2012-05-02T07:44:27Z",
267 | "type": "CreateEvent",
268 | "actor": {
269 | "id": 341877,
270 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
271 | "url": "https://api.github.com/users/pangratz",
272 | "login": "pangratz",
273 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
274 | },
275 | "payload": {
276 | "master_branch": "master",
277 | "ref": "upload_task_test",
278 | "ref_type": "branch",
279 | "description": "Ember.js - formerly known as SproutCore 2.0"
280 | },
281 | "public": true,
282 | "repo": {
283 | "id": 2997378,
284 | "url": "https://api.github.com/repos/pangratz/ember.js",
285 | "name": "pangratz/ember.js"
286 | },
287 | "id": "1547858272"
288 | },
289 | {
290 | "created_at": "2012-05-02T07:42:58Z",
291 | "type": "DownloadEvent",
292 | "actor": {
293 | "id": 341877,
294 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
295 | "url": "https://api.github.com/users/pangratz",
296 | "login": "pangratz",
297 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
298 | },
299 | "payload": {
300 | "download": {
301 | "content_type": "application/json",
302 | "created_at": "2012-05-02T07:42:57Z",
303 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.min.js",
304 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230844",
305 | "id": 230844,
306 | "download_count": 0,
307 | "name": "ember-latest.min.js",
308 | "description": "Ember.js Master (minified)",
309 | "size": 151552
310 | }
311 | },
312 | "public": true,
313 | "repo": {
314 | "id": 2997378,
315 | "url": "https://api.github.com/repos/pangratz/ember.js",
316 | "name": "pangratz/ember.js"
317 | },
318 | "id": "1547857922"
319 | },
320 | {
321 | "created_at": "2012-05-02T07:42:55Z",
322 | "type": "DownloadEvent",
323 | "actor": {
324 | "id": 341877,
325 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
326 | "url": "https://api.github.com/users/pangratz",
327 | "login": "pangratz",
328 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
329 | },
330 | "payload": {
331 | "download": {
332 | "content_type": "application/json",
333 | "created_at": "2012-05-02T07:42:54Z",
334 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.js",
335 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230843",
336 | "id": 230843,
337 | "download_count": 0,
338 | "name": "ember-latest.js",
339 | "description": "Ember.js Master",
340 | "size": 581632
341 | }
342 | },
343 | "public": true,
344 | "repo": {
345 | "id": 2997378,
346 | "url": "https://api.github.com/repos/pangratz/ember.js",
347 | "name": "pangratz/ember.js"
348 | },
349 | "id": "1547857912"
350 | },
351 | {
352 | "created_at": "2012-05-02T07:40:52Z",
353 | "type": "DownloadEvent",
354 | "actor": {
355 | "id": 341877,
356 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
357 | "url": "https://api.github.com/users/pangratz",
358 | "login": "pangratz",
359 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
360 | },
361 | "payload": {
362 | "download": {
363 | "content_type": "application/json",
364 | "created_at": "2012-05-02T07:40:50Z",
365 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.min.js",
366 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230842",
367 | "id": 230842,
368 | "download_count": 0,
369 | "name": "ember-latest.min.js",
370 | "description": "Ember.js Master (minified)",
371 | "size": 151552
372 | }
373 | },
374 | "public": true,
375 | "repo": {
376 | "id": 2997378,
377 | "url": "https://api.github.com/repos/pangratz/ember.js",
378 | "name": "pangratz/ember.js"
379 | },
380 | "id": "1547857433"
381 | },
382 | {
383 | "created_at": "2012-05-02T07:40:48Z",
384 | "type": "DownloadEvent",
385 | "actor": {
386 | "id": 341877,
387 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
388 | "url": "https://api.github.com/users/pangratz",
389 | "login": "pangratz",
390 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
391 | },
392 | "payload": {
393 | "download": {
394 | "content_type": "application/json",
395 | "created_at": "2012-05-02T07:40:48Z",
396 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.js",
397 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230841",
398 | "id": 230841,
399 | "download_count": 0,
400 | "name": "ember-latest.js",
401 | "description": "Ember.js Master",
402 | "size": 581632
403 | }
404 | },
405 | "public": true,
406 | "repo": {
407 | "id": 2997378,
408 | "url": "https://api.github.com/repos/pangratz/ember.js",
409 | "name": "pangratz/ember.js"
410 | },
411 | "id": "1547857429"
412 | },
413 | {
414 | "created_at": "2012-05-02T07:38:41Z",
415 | "type": "DownloadEvent",
416 | "actor": {
417 | "id": 341877,
418 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
419 | "url": "https://api.github.com/users/pangratz",
420 | "login": "pangratz",
421 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
422 | },
423 | "payload": {
424 | "download": {
425 | "content_type": "application/json",
426 | "created_at": "2012-05-02T07:38:41Z",
427 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.min.js",
428 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230840",
429 | "id": 230840,
430 | "download_count": 0,
431 | "name": "ember-latest.min.js",
432 | "description": "Ember.js Master (minified)",
433 | "size": 151552
434 | }
435 | },
436 | "public": true,
437 | "repo": {
438 | "id": 2997378,
439 | "url": "https://api.github.com/repos/pangratz/ember.js",
440 | "name": "pangratz/ember.js"
441 | },
442 | "id": "1547857000"
443 | },
444 | {
445 | "created_at": "2012-05-02T07:38:40Z",
446 | "type": "DownloadEvent",
447 | "actor": {
448 | "id": 341877,
449 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
450 | "url": "https://api.github.com/users/pangratz",
451 | "login": "pangratz",
452 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
453 | },
454 | "payload": {
455 | "download": {
456 | "content_type": "application/json",
457 | "created_at": "2012-05-02T07:38:39Z",
458 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.js",
459 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230839",
460 | "id": 230839,
461 | "download_count": 0,
462 | "name": "ember-latest.js",
463 | "description": "Ember.js Master",
464 | "size": 581632
465 | }
466 | },
467 | "public": true,
468 | "repo": {
469 | "id": 2997378,
470 | "url": "https://api.github.com/repos/pangratz/ember.js",
471 | "name": "pangratz/ember.js"
472 | },
473 | "id": "1547856996"
474 | },
475 | {
476 | "created_at": "2012-05-02T07:38:14Z",
477 | "type": "DownloadEvent",
478 | "actor": {
479 | "id": 341877,
480 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
481 | "url": "https://api.github.com/users/pangratz",
482 | "login": "pangratz",
483 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
484 | },
485 | "payload": {
486 | "download": {
487 | "content_type": "application/json",
488 | "created_at": "2012-05-02T07:38:13Z",
489 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.min.js",
490 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230838",
491 | "id": 230838,
492 | "name": "ember-latest.min.js",
493 | "download_count": 0,
494 | "description": "Ember.js Master (minified)",
495 | "size": 151552
496 | }
497 | },
498 | "public": true,
499 | "repo": {
500 | "id": 2997378,
501 | "url": "https://api.github.com/repos/pangratz/ember.js",
502 | "name": "pangratz/ember.js"
503 | },
504 | "id": "1547856899"
505 | },
506 | {
507 | "created_at": "2012-05-02T07:38:11Z",
508 | "type": "DownloadEvent",
509 | "actor": {
510 | "id": 341877,
511 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
512 | "url": "https://api.github.com/users/pangratz",
513 | "login": "pangratz",
514 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
515 | },
516 | "payload": {
517 | "download": {
518 | "content_type": "application/json",
519 | "created_at": "2012-05-02T07:38:11Z",
520 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.js",
521 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230837",
522 | "id": 230837,
523 | "download_count": 0,
524 | "name": "ember-latest.js",
525 | "description": "Ember.js Master",
526 | "size": 581632
527 | }
528 | },
529 | "public": true,
530 | "repo": {
531 | "id": 2997378,
532 | "url": "https://api.github.com/repos/pangratz/ember.js",
533 | "name": "pangratz/ember.js"
534 | },
535 | "id": "1547856892"
536 | },
537 | {
538 | "created_at": "2012-05-02T07:38:10Z",
539 | "type": "PushEvent",
540 | "actor": {
541 | "id": 341877,
542 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
543 | "url": "https://api.github.com/users/pangratz",
544 | "login": "pangratz",
545 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
546 | },
547 | "payload": {
548 | "commits": [
549 | {
550 | "distinct": true,
551 | "author": {
552 | "email": "cmueller.418@gmail.com",
553 | "name": "pangratz"
554 | },
555 | "url": "https://api.github.com/repos/pangratz/ember.js/commits/ec6b08d0fa57f6eb4a3570338c069d000e4dddb8",
556 | "message": "Modify branch output",
557 | "sha": "ec6b08d0fa57f6eb4a3570338c069d000e4dddb8"
558 | }
559 | ],
560 | "push_id": 76022217,
561 | "ref": "refs/heads/add_task_to_upload_latest_build",
562 | "head": "ec6b08d0fa57f6eb4a3570338c069d000e4dddb8",
563 | "size": 1
564 | },
565 | "public": true,
566 | "repo": {
567 | "id": 2997378,
568 | "url": "https://api.github.com/repos/pangratz/ember.js",
569 | "name": "pangratz/ember.js"
570 | },
571 | "id": "1547856888"
572 | },
573 | {
574 | "created_at": "2012-05-02T07:37:14Z",
575 | "type": "DownloadEvent",
576 | "actor": {
577 | "id": 341877,
578 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
579 | "url": "https://api.github.com/users/pangratz",
580 | "login": "pangratz",
581 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
582 | },
583 | "payload": {
584 | "download": {
585 | "content_type": "application/json",
586 | "created_at": "2012-05-02T07:37:14Z",
587 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.min.js",
588 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230836",
589 | "id": 230836,
590 | "download_count": 0,
591 | "name": "ember-latest.min.js",
592 | "description": "Ember.js Master (minified)",
593 | "size": 151552
594 | }
595 | },
596 | "public": true,
597 | "repo": {
598 | "id": 2997378,
599 | "url": "https://api.github.com/repos/pangratz/ember.js",
600 | "name": "pangratz/ember.js"
601 | },
602 | "id": "1547856674"
603 | },
604 | {
605 | "created_at": "2012-05-02T07:37:12Z",
606 | "type": "DownloadEvent",
607 | "actor": {
608 | "id": 341877,
609 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
610 | "url": "https://api.github.com/users/pangratz",
611 | "login": "pangratz",
612 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
613 | },
614 | "payload": {
615 | "download": {
616 | "content_type": "application/json",
617 | "created_at": "2012-05-02T07:37:12Z",
618 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.js",
619 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230835",
620 | "id": 230835,
621 | "download_count": 0,
622 | "name": "ember-latest.js",
623 | "description": "Ember.js Master",
624 | "size": 581632
625 | }
626 | },
627 | "public": true,
628 | "repo": {
629 | "id": 2997378,
630 | "url": "https://api.github.com/repos/pangratz/ember.js",
631 | "name": "pangratz/ember.js"
632 | },
633 | "id": "1547856665"
634 | },
635 | {
636 | "created_at": "2012-05-02T07:34:35Z",
637 | "type": "PushEvent",
638 | "actor": {
639 | "id": 341877,
640 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
641 | "url": "https://api.github.com/users/pangratz",
642 | "login": "pangratz",
643 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
644 | },
645 | "payload": {
646 | "commits": [
647 | {
648 | "distinct": true,
649 | "author": {
650 | "email": "cmueller.418@gmail.com",
651 | "name": "pangratz"
652 | },
653 | "url": "https://api.github.com/repos/pangratz/ember.js/commits/f405b03ccc2e07a83b88b17a94c6eb70b641782e",
654 | "message": "Show current branch",
655 | "sha": "f405b03ccc2e07a83b88b17a94c6eb70b641782e"
656 | }
657 | ],
658 | "push_id": 76021781,
659 | "ref": "refs/heads/add_task_to_upload_latest_build",
660 | "head": "f405b03ccc2e07a83b88b17a94c6eb70b641782e",
661 | "size": 1
662 | },
663 | "public": true,
664 | "repo": {
665 | "id": 2997378,
666 | "url": "https://api.github.com/repos/pangratz/ember.js",
667 | "name": "pangratz/ember.js"
668 | },
669 | "id": "1547855998"
670 | },
671 | {
672 | "created_at": "2012-05-02T07:24:38Z",
673 | "type": "DownloadEvent",
674 | "actor": {
675 | "id": 341877,
676 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
677 | "url": "https://api.github.com/users/pangratz",
678 | "login": "pangratz",
679 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
680 | },
681 | "payload": {
682 | "download": {
683 | "content_type": "application/json",
684 | "created_at": "2012-05-02T07:24:38Z",
685 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.min.js",
686 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230831",
687 | "id": 230831,
688 | "download_count": 0,
689 | "name": "ember-latest.min.js",
690 | "description": "Ember.js Master (minified)",
691 | "size": 151552
692 | }
693 | },
694 | "public": true,
695 | "repo": {
696 | "id": 2997378,
697 | "url": "https://api.github.com/repos/pangratz/ember.js",
698 | "name": "pangratz/ember.js"
699 | },
700 | "id": "1547853962"
701 | },
702 | {
703 | "created_at": "2012-05-02T07:24:35Z",
704 | "type": "DownloadEvent",
705 | "actor": {
706 | "id": 341877,
707 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
708 | "url": "https://api.github.com/users/pangratz",
709 | "login": "pangratz",
710 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
711 | },
712 | "payload": {
713 | "download": {
714 | "content_type": "application/json",
715 | "created_at": "2012-05-02T07:24:35Z",
716 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.js",
717 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230830",
718 | "id": 230830,
719 | "download_count": 0,
720 | "name": "ember-latest.js",
721 | "description": "Ember.js Master",
722 | "size": 581632
723 | }
724 | },
725 | "public": true,
726 | "repo": {
727 | "id": 2997378,
728 | "url": "https://api.github.com/repos/pangratz/ember.js",
729 | "name": "pangratz/ember.js"
730 | },
731 | "id": "1547853953"
732 | },
733 | {
734 | "created_at": "2012-05-02T07:23:51Z",
735 | "type": "DownloadEvent",
736 | "actor": {
737 | "id": 341877,
738 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
739 | "url": "https://api.github.com/users/pangratz",
740 | "login": "pangratz",
741 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
742 | },
743 | "payload": {
744 | "download": {
745 | "content_type": "application/json",
746 | "created_at": "2012-05-02T07:23:51Z",
747 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.min.js",
748 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230828",
749 | "id": 230828,
750 | "download_count": 0,
751 | "name": "ember-latest.min.js",
752 | "description": "Ember.js Master (minified)",
753 | "size": 151552
754 | }
755 | },
756 | "public": true,
757 | "repo": {
758 | "id": 2997378,
759 | "url": "https://api.github.com/repos/pangratz/ember.js",
760 | "name": "pangratz/ember.js"
761 | },
762 | "id": "1547853671"
763 | },
764 | {
765 | "created_at": "2012-05-02T07:23:51Z",
766 | "type": "DownloadEvent",
767 | "actor": {
768 | "id": 341877,
769 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
770 | "url": "https://api.github.com/users/pangratz",
771 | "login": "pangratz",
772 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
773 | },
774 | "payload": {
775 | "download": {
776 | "content_type": "application/json",
777 | "created_at": "2012-05-02T07:23:50Z",
778 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.js",
779 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230827",
780 | "id": 230827,
781 | "download_count": 0,
782 | "name": "ember-latest.js",
783 | "description": "Ember.js Master",
784 | "size": 581632
785 | }
786 | },
787 | "public": true,
788 | "repo": {
789 | "id": 2997378,
790 | "url": "https://api.github.com/repos/pangratz/ember.js",
791 | "name": "pangratz/ember.js"
792 | },
793 | "id": "1547853653"
794 | },
795 | {
796 | "created_at": "2012-05-02T07:21:07Z",
797 | "type": "PushEvent",
798 | "actor": {
799 | "id": 341877,
800 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
801 | "url": "https://api.github.com/users/pangratz",
802 | "login": "pangratz",
803 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
804 | },
805 | "payload": {
806 | "commits": [
807 | {
808 | "distinct": true,
809 | "author": {
810 | "email": "cmueller.418@gmail.com",
811 | "name": "pangratz"
812 | },
813 | "url": "https://api.github.com/repos/pangratz/ember.js/commits/474b13c606a8c1b9c6da5265e8c76464e8434a1c",
814 | "message": "Change to test upload_to_url task",
815 | "sha": "474b13c606a8c1b9c6da5265e8c76464e8434a1c"
816 | }
817 | ],
818 | "push_id": 76020236,
819 | "ref": "refs/heads/add_task_to_upload_latest_build",
820 | "head": "474b13c606a8c1b9c6da5265e8c76464e8434a1c",
821 | "size": 1
822 | },
823 | "public": true,
824 | "repo": {
825 | "id": 2997378,
826 | "url": "https://api.github.com/repos/pangratz/ember.js",
827 | "name": "pangratz/ember.js"
828 | },
829 | "id": "1547853063"
830 | },
831 | {
832 | "created_at": "2012-05-02T07:16:04Z",
833 | "type": "DownloadEvent",
834 | "actor": {
835 | "id": 341877,
836 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
837 | "url": "https://api.github.com/users/pangratz",
838 | "login": "pangratz",
839 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
840 | },
841 | "payload": {
842 | "download": {
843 | "content_type": "application/json",
844 | "created_at": "2012-05-02T07:16:03Z",
845 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.min.js",
846 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230826",
847 | "id": 230826,
848 | "download_count": 0,
849 | "name": "ember-latest.min.js",
850 | "description": "Ember.js Master (minified)",
851 | "size": 151552
852 | }
853 | },
854 | "public": true,
855 | "repo": {
856 | "id": 2997378,
857 | "url": "https://api.github.com/repos/pangratz/ember.js",
858 | "name": "pangratz/ember.js"
859 | },
860 | "id": "1547852018"
861 | },
862 | {
863 | "created_at": "2012-05-02T07:16:02Z",
864 | "type": "DownloadEvent",
865 | "actor": {
866 | "id": 341877,
867 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
868 | "url": "https://api.github.com/users/pangratz",
869 | "login": "pangratz",
870 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
871 | },
872 | "payload": {
873 | "download": {
874 | "content_type": "application/json",
875 | "created_at": "2012-05-02T07:16:02Z",
876 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.js",
877 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230825",
878 | "id": 230825,
879 | "download_count": 0,
880 | "name": "ember-latest.js",
881 | "description": "Ember.js Master",
882 | "size": 581632
883 | }
884 | },
885 | "public": true,
886 | "repo": {
887 | "id": 2997378,
888 | "url": "https://api.github.com/repos/pangratz/ember.js",
889 | "name": "pangratz/ember.js"
890 | },
891 | "id": "1547852013"
892 | },
893 | {
894 | "created_at": "2012-05-02T07:16:00Z",
895 | "type": "DownloadEvent",
896 | "actor": {
897 | "id": 341877,
898 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
899 | "url": "https://api.github.com/users/pangratz",
900 | "login": "pangratz",
901 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
902 | },
903 | "payload": {
904 | "download": {
905 | "content_type": "application/json",
906 | "created_at": "2012-05-02T07:15:59Z",
907 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.min.js",
908 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230824",
909 | "id": 230824,
910 | "download_count": 0,
911 | "name": "ember-latest.min.js",
912 | "description": "Ember.js Master (minified)",
913 | "size": 151552
914 | }
915 | },
916 | "public": true,
917 | "repo": {
918 | "id": 2997378,
919 | "url": "https://api.github.com/repos/pangratz/ember.js",
920 | "name": "pangratz/ember.js"
921 | },
922 | "id": "1547851991"
923 | },
924 | {
925 | "created_at": "2012-05-02T07:15:57Z",
926 | "type": "DownloadEvent",
927 | "actor": {
928 | "id": 341877,
929 | "avatar_url": "https://secure.gravatar.com/avatar/97c57e7e99431e76fbc04173cca51eab?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png",
930 | "url": "https://api.github.com/users/pangratz",
931 | "login": "pangratz",
932 | "gravatar_id": "97c57e7e99431e76fbc04173cca51eab"
933 | },
934 | "payload": {
935 | "download": {
936 | "content_type": "application/json",
937 | "created_at": "2012-05-02T07:15:57Z",
938 | "html_url": "https://github.com/downloads/pangratz/ember.js/ember-latest.js",
939 | "url": "https://api.github.com/repos/pangratz/ember.js/downloads/230823",
940 | "id": 230823,
941 | "download_count": 0,
942 | "name": "ember-latest.js",
943 | "description": "Ember.js Master",
944 | "size": 581632
945 | }
946 | },
947 | "public": true,
948 | "repo": {
949 | "id": 2997378,
950 | "url": "https://api.github.com/repos/pangratz/ember.js",
951 | "name": "pangratz/ember.js"
952 | },
953 | "id": "1547851988"
954 | }
955 | ]
956 |
--------------------------------------------------------------------------------