diff --git a/.bowerrc b/.bowerrc
new file mode 100644
index 00000000..959e1696
--- /dev/null
+++ b/.bowerrc
@@ -0,0 +1,4 @@
+{
+ "directory": "bower_components",
+ "analytics": false
+}
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..2fe4874a
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,33 @@
+# EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# editorconfig.org
+
+root = true
+
+
+[*]
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+indent_style = space
+indent_size = 2
+
+[*.js]
+indent_style = space
+indent_size = 2
+
+[*.hbs]
+indent_style = space
+indent_size = 2
+
+[*.css]
+indent_style = space
+indent_size = 2
+
+[*.html]
+indent_style = space
+indent_size = 2
+
+[*.{diff,md}]
+trim_trailing_whitespace = false
diff --git a/.ember-cli b/.ember-cli
new file mode 100644
index 00000000..ee64cfed
--- /dev/null
+++ b/.ember-cli
@@ -0,0 +1,9 @@
+{
+ /**
+ Ember CLI sends analytics information by default. The data is completely
+ anonymous, but there are times when you might want to disable this behavior.
+
+ Setting `disableAnalytics` to true will prevent any data from being sent.
+ */
+ "disableAnalytics": false
+}
diff --git a/.gitignore b/.gitignore
index ec47c7b9..20b90e33 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,15 +1,15 @@
-/.bundle
-/config/travis.yml
+# compiled output
+/dist
/tmp
-.sass-cache
-.localeapp/key
-/assets/scripts/config/locales.js
-.DS_Store
-*.sw[op]
-/public/images
-/public/scripts
-/public/styles/app.css
-/public/styles/dashboard.css
-/public/version
-vendor/bundle
+# dependencies
+/node_modules
+/bower_components
+
+# misc
+/.sass-cache
+/connect.lock
+/coverage/*
+/libpeerconnection.log
+npm-debug.log
+testem.log
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 00000000..08096eff
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,32 @@
+{
+ "predef": [
+ "document",
+ "window",
+ "-Promise"
+ ],
+ "browser": true,
+ "boss": true,
+ "curly": true,
+ "debug": false,
+ "devel": true,
+ "eqeqeq": true,
+ "evil": true,
+ "forin": false,
+ "immed": false,
+ "laxbreak": false,
+ "newcap": true,
+ "noarg": true,
+ "noempty": false,
+ "nonew": false,
+ "nomen": false,
+ "onevar": false,
+ "plusplus": false,
+ "regexp": false,
+ "undef": true,
+ "sub": true,
+ "strict": false,
+ "white": false,
+ "eqnull": true,
+ "esnext": true,
+ "unused": true
+}
diff --git a/Brocfile.js b/Brocfile.js
new file mode 100644
index 00000000..dad08e20
--- /dev/null
+++ b/Brocfile.js
@@ -0,0 +1,26 @@
+/* global require, module */
+
+var EmberApp = require('ember-cli/lib/broccoli/ember-app');
+
+var app = new EmberApp();
+
+app.import('bower_components/jquery-timeago/jquery.timeago.js');
+app.import('bower_components/visibilityjs/lib/visibility.core.js');
+app.import('bower_components/visibilityjs/lib/visibility.timers.js');
+app.import('bower_components/JavaScript-MD5/js/md5.js');
+app.import('vendor/ansiparse.js');
+app.import('bower_components/moment/moment.js');
+// Use `app.import` to add additional libraries to the generated
+// output files.
+//
+// If you need to use different assets in different
+// environments, specify an object as the first parameter. That
+// object's keys should be the environment name and the values
+// should be the asset to use in that environment.
+//
+// If the library that you are including contains AMD or ES6
+// modules that you would like to import into your application
+// please specify an object with the list of modules as keys
+// along with the exports of each module as its value.
+
+module.exports = app.toTree();
diff --git a/bower.json b/bower.json
new file mode 100644
index 00000000..c1b4d7d9
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,20 @@
+{
+ "name": "travis",
+ "dependencies": {
+ "handlebars": "2.0.0",
+ "jquery": "^1.11.1",
+ "ember": "1.9.1",
+ "ember-data": "1.0.0-beta.12",
+ "ember-resolver": "~0.1.11",
+ "loader.js": "ember-cli/loader.js#1.0.1",
+ "ember-cli-shims": "ember-cli/ember-cli-shims#0.0.3",
+ "ember-cli-test-loader": "ember-cli/ember-cli-test-loader#0.1.0",
+ "ember-load-initializers": "ember-cli/ember-load-initializers#0.0.2",
+ "ember-qunit": "0.1.8",
+ "ember-qunit-notifications": "0.0.5",
+ "qunit": "~1.17.1",
+ "visibilityjs": "~1.2.1",
+ "JavaScript-MD5": "~1.1.0",
+ "moment": "~2.9.0"
+ }
+}
diff --git a/config/environment.js b/config/environment.js
new file mode 100644
index 00000000..dbc3c1c9
--- /dev/null
+++ b/config/environment.js
@@ -0,0 +1,69 @@
+/* jshint node: true */
+
+module.exports = function(environment) {
+ var ENV = {
+ modulePrefix: 'travis',
+ environment: environment,
+ baseURL: '/',
+ locationType: 'auto',
+ EmberENV: {
+ FEATURES: {
+ // Here you can enable experimental features on an ember canary build
+ // e.g. 'with-controller': true
+ }
+ },
+
+ APP: {
+ // Here you can pass flags/options to your application instance
+ // when it is created
+ }
+ };
+
+ if (environment === 'development') {
+ // ENV.APP.LOG_RESOLVER = true;
+ // ENV.APP.LOG_ACTIVE_GENERATION = true;
+ // ENV.APP.LOG_TRANSITIONS = true;
+ // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
+ // ENV.APP.LOG_VIEW_LOOKUPS = true;
+ }
+
+ if (environment === 'test') {
+ // Testem prefers this...
+ ENV.baseURL = '/';
+ ENV.locationType = 'none';
+
+ // keep test console output quieter
+ ENV.APP.LOG_ACTIVE_GENERATION = false;
+ ENV.APP.LOG_VIEW_LOOKUPS = false;
+
+ ENV.APP.rootElement = '#ember-testing';
+ }
+
+ if (environment === 'production') {
+
+ }
+
+ ENV.endpoints = {
+ ssh_key: false,
+ caches: false
+ };
+ ENV.pro = false;
+ ENV.pusher = {};
+ ENV.intervals = { updateTimes: 1000, times: -1 };
+ ENV.api_endpoint = 'https://api.travis-ci.org';
+
+ ENV.contentSecurityPolicy = {
+ 'default-src': "'none'",
+ // TODO: for some reason unsafe-eval is needed when I use collection helper,
+ // we should probably remove it at some point
+ 'script-src': "'self' 'unsafe-eval'",
+ 'font-src': "'self'",
+ 'connect-src': "'self' https://api.travis-ci.org",
+ 'img-src': "'self' data: https://www.gravatar.com http://www.gravatar.com",
+ 'style-src': "'self'",
+ 'media-src': "'self'",
+ 'frame-src': "'self' https://api.travis-ci.org"
+ }
+
+ return ENV;
+};
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..beecb008
--- /dev/null
+++ b/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "travis",
+ "version": "0.0.0",
+ "description": "Small description for travis goes here",
+ "private": true,
+ "directories": {
+ "doc": "doc",
+ "test": "tests"
+ },
+ "scripts": {
+ "start": "ember server",
+ "build": "ember build",
+ "test": "ember test"
+ },
+ "repository": "",
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "author": "",
+ "license": "MIT",
+ "devDependencies": {
+ "broccoli-asset-rev": "^2.0.0",
+ "ember-cli": "0.1.11",
+ "ember-cli-6to5": "^3.0.0",
+ "ember-cli-app-version": "0.3.0",
+ "ember-cli-coffeescript": "0.7.0",
+ "ember-cli-content-security-policy": "0.3.0",
+ "ember-cli-dependency-checker": "0.0.7",
+ "ember-cli-htmlbars": "^0.6.0",
+ "ember-cli-ic-ajax": "0.1.1",
+ "ember-cli-inject-live-reload": "^1.3.0",
+ "ember-cli-qunit": "0.3.0",
+ "ember-cli-uglify": "1.0.1",
+ "ember-data": "1.0.0-beta.12",
+ "ember-export-application-global": "^1.0.0",
+ "express": "^4.8.5",
+ "glob": "^4.0.5"
+ }
+}
diff --git a/public/crossdomain.xml b/public/crossdomain.xml
new file mode 100644
index 00000000..29a035d7
--- /dev/null
+++ b/public/crossdomain.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/robots.txt b/public/robots.txt
new file mode 100644
index 00000000..5debfa4d
--- /dev/null
+++ b/public/robots.txt
@@ -0,0 +1,2 @@
+# http://www.robotstxt.org
+User-agent: *
diff --git a/testem.json b/testem.json
new file mode 100644
index 00000000..42a4ddb2
--- /dev/null
+++ b/testem.json
@@ -0,0 +1,11 @@
+{
+ "framework": "qunit",
+ "test_page": "tests/index.html?hidepassed",
+ "launch_in_ci": [
+ "PhantomJS"
+ ],
+ "launch_in_dev": [
+ "PhantomJS",
+ "Chrome"
+ ]
+}
diff --git a/tests/.jshintrc b/tests/.jshintrc
new file mode 100644
index 00000000..6ebf71a0
--- /dev/null
+++ b/tests/.jshintrc
@@ -0,0 +1,74 @@
+{
+ "predef": [
+ "document",
+ "window",
+ "location",
+ "setTimeout",
+ "$",
+ "-Promise",
+ "QUnit",
+ "define",
+ "console",
+ "equal",
+ "notEqual",
+ "notStrictEqual",
+ "test",
+ "asyncTest",
+ "testBoth",
+ "testWithDefault",
+ "raises",
+ "throws",
+ "deepEqual",
+ "start",
+ "stop",
+ "ok",
+ "strictEqual",
+ "module",
+ "moduleFor",
+ "moduleForComponent",
+ "moduleForModel",
+ "process",
+ "expect",
+ "visit",
+ "exists",
+ "fillIn",
+ "click",
+ "keyEvent",
+ "triggerEvent",
+ "find",
+ "findWithAssert",
+ "wait",
+ "DS",
+ "isolatedContainer",
+ "startApp",
+ "andThen",
+ "currentURL",
+ "currentPath",
+ "currentRouteName"
+ ],
+ "node": false,
+ "browser": false,
+ "boss": true,
+ "curly": false,
+ "debug": false,
+ "devel": false,
+ "eqeqeq": true,
+ "evil": true,
+ "forin": false,
+ "immed": false,
+ "laxbreak": false,
+ "newcap": true,
+ "noarg": true,
+ "noempty": false,
+ "nonew": false,
+ "nomen": false,
+ "onevar": false,
+ "plusplus": false,
+ "regexp": false,
+ "undef": true,
+ "sub": true,
+ "strict": false,
+ "white": false,
+ "eqnull": true,
+ "esnext": true
+}
diff --git a/tests/helpers/resolver.js b/tests/helpers/resolver.js
new file mode 100644
index 00000000..28f4ece4
--- /dev/null
+++ b/tests/helpers/resolver.js
@@ -0,0 +1,11 @@
+import Resolver from 'ember/resolver';
+import config from '../../config/environment';
+
+var resolver = Resolver.create();
+
+resolver.namespace = {
+ modulePrefix: config.modulePrefix,
+ podModulePrefix: config.podModulePrefix
+};
+
+export default resolver;
diff --git a/tests/helpers/start-app.js b/tests/helpers/start-app.js
new file mode 100644
index 00000000..16cc7c39
--- /dev/null
+++ b/tests/helpers/start-app.js
@@ -0,0 +1,19 @@
+import Ember from 'ember';
+import Application from '../../app';
+import Router from '../../router';
+import config from '../../config/environment';
+
+export default function startApp(attrs) {
+ var application;
+
+ var attributes = Ember.merge({}, config.APP);
+ attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
+
+ Ember.run(function() {
+ application = Application.create(attributes);
+ application.setupForTesting();
+ application.injectTestHelpers();
+ });
+
+ return application;
+}
diff --git a/tests/index.html b/tests/index.html
new file mode 100644
index 00000000..86513a02
--- /dev/null
+++ b/tests/index.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+ Travis Tests
+
+
+
+ {{content-for 'head'}}
+ {{content-for 'test-head'}}
+
+
+
+
+
+ {{content-for 'head-footer'}}
+ {{content-for 'test-head-footer'}}
+
+
+
+ {{content-for 'body'}}
+ {{content-for 'test-body'}}
+
+
+
+
+
+
+ {{content-for 'body-footer'}}
+ {{content-for 'test-body-footer'}}
+
+
diff --git a/tests/test-helper.js b/tests/test-helper.js
new file mode 100644
index 00000000..e6cfb70f
--- /dev/null
+++ b/tests/test-helper.js
@@ -0,0 +1,6 @@
+import resolver from './helpers/resolver';
+import {
+ setResolver
+} from 'ember-qunit';
+
+setResolver(resolver);
diff --git a/tests/unit/.gitkeep b/tests/unit/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/unit/initializers/auth-test.coffee b/tests/unit/initializers/auth-test.coffee
new file mode 100644
index 00000000..f8c70675
--- /dev/null
+++ b/tests/unit/initializers/auth-test.coffee
@@ -0,0 +1,19 @@
+`import Ember from 'ember'`
+`import { initialize } from 'travis/initializers/auth'`
+
+container = null
+application = null
+
+module 'AuthInitializer',
+ setup: ->
+ Ember.run ->
+ application = Ember.Application.create()
+ container = application.__container__
+ application.deferReadiness()
+
+# Replace this with your real tests.
+test 'it works', ->
+ initialize container, application
+
+ # you would normally confirm the results of the initializer here
+ ok true
diff --git a/tests/unit/initializers/config-test.coffee b/tests/unit/initializers/config-test.coffee
new file mode 100644
index 00000000..faea5eb8
--- /dev/null
+++ b/tests/unit/initializers/config-test.coffee
@@ -0,0 +1,19 @@
+`import Ember from 'ember'`
+`import { initialize } from 'travis/initializers/config'`
+
+container = null
+application = null
+
+module 'ConfigInitializer',
+ setup: ->
+ Ember.run ->
+ application = Ember.Application.create()
+ container = application.__container__
+ application.deferReadiness()
+
+# Replace this with your real tests.
+test 'it works', ->
+ initialize container, application
+
+ # you would normally confirm the results of the initializer here
+ ok true
diff --git a/tests/unit/initializers/storage-test.coffee b/tests/unit/initializers/storage-test.coffee
new file mode 100644
index 00000000..81c9a132
--- /dev/null
+++ b/tests/unit/initializers/storage-test.coffee
@@ -0,0 +1,19 @@
+`import Ember from 'ember'`
+`import { initialize } from 'travis/initializers/storage'`
+
+container = null
+application = null
+
+module 'StorageInitializer',
+ setup: ->
+ Ember.run ->
+ application = Ember.Application.create()
+ container = application.__container__
+ application.deferReadiness()
+
+# Replace this with your real tests.
+test 'it works', ->
+ initialize container, application
+
+ # you would normally confirm the results of the initializer here
+ ok true
diff --git a/vendor/.gitkeep b/vendor/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/vendor/ansiparse.js b/vendor/ansiparse.js
new file mode 100644
index 00000000..692247d7
--- /dev/null
+++ b/vendor/ansiparse.js
@@ -0,0 +1,186 @@
+ansiparse = function (str) {
+ //
+ // I'm terrible at writing parsers.
+ //
+ var matchingControl = null,
+ matchingData = null,
+ matchingText = '',
+ ansiState = [],
+ result = [],
+ state = {},
+ eraseChar;
+
+ //
+ // General workflow for this thing is:
+ // \033\[33mText
+ // | | |
+ // | | matchingText
+ // | matchingData
+ // matchingControl
+ //
+ // In further steps we hope it's all going to be fine. It usually is.
+ //
+
+ //
+ // Erases a char from the output
+ //
+ eraseChar = function () {
+ var index, text;
+ if (matchingText.length) {
+ matchingText = matchingText.substr(0, matchingText.length - 1);
+ }
+ else if (result.length) {
+ index = result.length - 1;
+ text = result[index].text;
+ if (text.length === 1) {
+ //
+ // A result bit was fully deleted, pop it out to simplify the final output
+ //
+ result.pop();
+ }
+ else {
+ result[index].text = text.substr(0, text.length - 1);
+ }
+ }
+ };
+
+ for (var i = 0; i < str.length; i++) {
+ if (matchingControl != null) {
+ if (matchingControl == '\033' && str[i] == '\[') {
+ //
+ // We've matched full control code. Lets start matching formating data.
+ //
+
+ //
+ // "emit" matched text with correct state
+ //
+ if (matchingText) {
+ state.text = matchingText;
+ result.push(state);
+ state = {};
+ matchingText = "";
+ }
+
+ matchingControl = null;
+ matchingData = '';
+ }
+ else {
+ //
+ // We failed to match anything - most likely a bad control code. We
+ // go back to matching regular strings.
+ //
+ matchingText += matchingControl + str[i];
+ matchingControl = null;
+ }
+ continue;
+ }
+ else if (matchingData != null) {
+ if (str[i] == ';') {
+ //
+ // `;` separates many formatting codes, for example: `\033[33;43m`
+ // means that both `33` and `43` should be applied.
+ //
+ // TODO: this can be simplified by modifying state here.
+ //
+ ansiState.push(matchingData);
+ matchingData = '';
+ }
+ else if (str[i] == 'm') {
+ //
+ // `m` finished whole formatting code. We can proceed to matching
+ // formatted text.
+ //
+ ansiState.push(matchingData);
+ matchingData = null;
+ matchingText = '';
+
+ //
+ // Convert matched formatting data into user-friendly state object.
+ //
+ // TODO: DRY.
+ //
+ ansiState.forEach(function (ansiCode) {
+ if (ansiparse.foregroundColors[ansiCode]) {
+ state.foreground = ansiparse.foregroundColors[ansiCode];
+ }
+ else if (ansiparse.backgroundColors[ansiCode]) {
+ state.background = ansiparse.backgroundColors[ansiCode];
+ }
+ else if (ansiCode == 39) {
+ delete state.foreground;
+ }
+ else if (ansiCode == 49) {
+ delete state.background;
+ }
+ else if (ansiparse.styles[ansiCode]) {
+ state[ansiparse.styles[ansiCode]] = true;
+ }
+ else if (ansiCode == 22) {
+ state.bold = false;
+ }
+ else if (ansiCode == 23) {
+ state.italic = false;
+ }
+ else if (ansiCode == 24) {
+ state.underline = false;
+ }
+ });
+ ansiState = [];
+ }
+ else {
+ matchingData += str[i];
+ }
+ continue;
+ }
+
+ if (str[i] == '\033') {
+ matchingControl = str[i];
+ }
+ else if (str[i] == '\u0008') {
+ eraseChar();
+ }
+ else {
+ matchingText += str[i];
+ }
+ }
+
+ if (matchingText) {
+ state.text = matchingText + (matchingControl ? matchingControl : '');
+ result.push(state);
+ }
+ return result;
+}
+
+ansiparse.foregroundColors = {
+ '30': 'black',
+ '31': 'red',
+ '32': 'green',
+ '33': 'yellow',
+ '34': 'blue',
+ '35': 'magenta',
+ '36': 'cyan',
+ '37': 'white',
+ '90': 'grey'
+};
+
+ansiparse.backgroundColors = {
+ '40': 'black',
+ '41': 'red',
+ '42': 'green',
+ '43': 'yellow',
+ '44': 'blue',
+ '45': 'magenta',
+ '46': 'cyan',
+ '47': 'white'
+};
+
+ansiparse.styles = {
+ '1': 'bold',
+ '3': 'italic',
+ '4': 'underline'
+};
+
+if (typeof module == "object" && typeof window == "undefined") {
+ module.exports = ansiparse;
+}
+