Merge pull request #327 from travis-ci/ember-cli

Switch to Ember CLI and Ember Data; refactor CSS.
This commit is contained in:
Piotr Sarnacki 2015-02-16 12:37:42 +01:00
commit 1105981e7d
1650 changed files with 20067 additions and 215626 deletions

4
.bowerrc Normal file
View File

@ -0,0 +1,4 @@
{
"directory": "bower_components",
"analytics": false
}

View File

@ -1,2 +1,2 @@
https://github.com/heroku/heroku-buildpack-ruby.git
https://github.com/travis-ci/travis-web-buildpack.git
https://github.com/travis-ci/travis-web-ember-cli-buildpack.git

33
.editorconfig Normal file
View File

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

9
.ember-cli Normal file
View File

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

27
.gitignore vendored
View File

@ -1,14 +1,15 @@
/.bundle
/config/travis.yml
/tmp
.sass-cache
.localeapp/key
/assets/scripts/config/locales.js
.DS_Store
*.sw[op]
# compiled output
dist
tmp
/public/images
/public/scripts
/public/styles/app.css
/public/version
vendor/bundle
# dependencies
node_modules
bower_components
# misc
.sass-cache
connect.lock
coverage/*
libpeerconnection.log
npm-debug.log
testem.log

32
.jshintrc Normal file
View File

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

View File

@ -1,47 +1,27 @@
language: ruby
rvm:
- 2.1.2
---
language: node_js
addons:
sauce_connect: true
sudo: false
cache:
bundler: true
matrix:
fast_finish: true
allow_failures:
- rvm: 2.1.2
env: 'TEST_SUITE=phantomjs'
- rvm: 2.1.2
env: "TEST_SUITE=saucelabs BROWSER='firefox::Windows XP'"
env:
global:
- secure: "RFuCOppyjWHC4XWKtQlgS4zO4B6KVxytdX8+G5jRY3XM+OEGte8VDD88gZLM\nKDpkqMFDbNJAVTsh1kMANCTct2ONi30RTxuJWLtRyK7RE5zCcaGbAkTNZgXo\nOR5OWLEPJZbNfbh17H6J7izTy6yiLR+CsVP1wMgeVusP0eoDhCA="
- secure: "duqMXPALumXB3e2j/kM2uCaCGwgZsRrU0GCDY+3Zk6a+PK+s0mE9BftcXdxm\n6u87ld2PvCBO0inpe5YeS9LOZsT+OFS4jj+GGTsRI6rmGz+kok0N+ATLTdcj\nu15zhLhUUlhoKW0DZURrDv/iTiC/FKvJ0u5Rft0XbjfTY+0go/M="
# SAUCE_ACCESS_KEY
- secure: "vf+RnbxcpCZcJYCcJdlT/EbPm9go5BnyyGKHCBwdPqb3+w+k0XyYnUkO5V9pIewIFAKof50i2YlAT8DG9GiUmKrX8z54dqSDlA+R1arMxgZRCHJohLxBjcxHVZe05aVZm/9U7YMOl3DuX88gISG91XC6nr8jnmmp2xGNQR8MkaU="
# SAUCE_USERNAME
- secure: "tg6TWOS7ZtS4/SXezv4mK482rR+F2MPKCe6PCp+U4oYidm5Jj0NPw/lVog26HSVxxLpayMq35WrBJPwR+7DiNLc+RyBS+yeL8zsLnDlBdGFqSYg6L0j3QK3mVUhQi/ivgvd5NiTcxLROKBie+NJ58HKbenwFFTfvVCy/WjK+dPA="
matrix:
- "TEST_SUITE=ruby ARTIFACTS_S3_BUCKET=travis-web-production"
- "TEST_SUITE=phantomjs"
- "TEST_SUITE=saucelabs BROWSER='firefox::Windows XP'"
- "TEST_SUITE=saucelabs BROWSER='chrome::Windows XP'"
script: "script/ci"
directories:
- node_modules
before_install:
- "gem install bundler"
before_script:
- "test $TEST_SUITE = \"ruby\" && gem install travis-artifacts || true"
- "bundle exec rakep"
- "phantomjs --version"
after_script:
- "test $TEST_SUITE = \"ruby\" && test $TRAVIS_PULL_REQUEST = \"false\" && travis-artifacts upload --target-path assets/$TRAVIS_BRANCH --path public/scripts:scripts --path public/styles:styles || true"
- "npm config set spin false"
- "npm install -g npm@^2"
install:
- npm install -g bower
- npm install
- bower install
script:
- npm test
notifications:
irc: "irc.freenode.org#travis"
campfire:
secure: "JJezWGD9KJY/LC2aznI3Zyohy31VTIhcTKX7RWR4C/C8YKbW9kZv3xV6Vn11\nSHxJTeZo6st2Bpv6tjlWZ+HCR09kyCNavIChedla3+oHOiuL0D4gSo+gkTNW\nUKYZz9mcQUd9RoQpTeyxvdvX+l7z62/7JwFA7txHOqxbTS8jrjc="

View File

@ -1,92 +0,0 @@
$: << 'lib'
require 'rake-pipeline-web-filters'
require 'travis/assets'
require 'compass'
Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = Encoding::UTF_8
assets ||= Travis::Assets.new
assets.setup_compass
assets.update_version
output 'public/scripts'
input assets.scripts do
match '**/*.hbs' do
travis_handlebars :precompile => false # assets.production?
concat 'templates.js'
end
match '**/*.coffee' do
coffee_script
end
match 'vendor/**/*.js' do
if assets.production?
reject 'ember.js'
else
reject 'ember.prod.js'
end
safe_concat assets.vendor_order, 'vendor.js'
end
match '{spec,spec/integration,spec/unit,spec/unit/views}/*.js' do
concat 'spec/specs.js'
end
match 'spec/support/*.js' do
concat 'spec/support.js'
end
match 'spec/vendor/*.js' do
concat assets.spec_vendor_order, 'spec/vendor.js'
end
match 'spec/{vendor,support,specs}.js' do
concat ['spec/vendor.js', 'spec/support.js', 'spec/specs.js'], 'specs.js'
end
match %r(^(?!vendor|spec).*\.js$) do
modules = proc { |input| input.path.gsub(%r((^app/|lib/|\.js$)), '') }
# why did we use the string strategy for development? makes it impossible to set breakpoints
# minispade(string: assets.development?, rewrite_requires: true, module_id_generator: modules)
minispade(string: false, rewrite_requires: true, module_id_generator: modules)
end
match %r(^(?!spec).*\.js$) do
paths = ['min/app.js']
paths.push 'app.js' unless assets.production?
concat ['vendor.js'], paths
end
if assets.production?
match 'min/app.js' do
strip_debug
# uglify squeeze: true
concat 'app.js'
end
end
end
output 'public/styles'
input assets.styles do
match '**/*.{scss,sass,css}' do
sass assets.production? ? { style: :compressed } : {}
concat assets.styles_order, ['app.css']
end
end
output 'public/images'
input assets.images do
match '**/*' do
copy
end
end
# output 'public'
# input assets.static do
# match '**/*' do
# copy
# end
# end

43
Brocfile.js Normal file
View File

@ -0,0 +1,43 @@
/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
var fingerprint = {
extensions: ['js', 'css', 'png', 'jpg', 'gif', 'map', 'svg']
},
assetsHost;
if (assetsHost = process.env.ASSETS_HOST) {
if (assetsHost.substr(-1) !== '/') {
assetsHost = assetsHost + '/'
}
fingerprint.prepend = assetsHost
}
var app = new EmberApp({
fingerprint: fingerprint
});
app.import('bower_components/pusher/dist/pusher.js');
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('vendor/log.js');
app.import('vendor/customerio.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();

13
Gemfile
View File

@ -1,6 +1,7 @@
source 'http://rubygems.org'
ruby '2.1.2'
gem 'travis-web', path: 'waiter'
gem 'puma'
gem 'rack-ssl', '~> 1.3'
gem 'rack-protection', '~> 1.3'
@ -8,15 +9,6 @@ gem 'rack-mobile-detect'
gem 'sinatra'
gem 'hashr'
gem 'rake-pipeline', github: 'livingsocial/rake-pipeline'
gem 'rake-pipeline-web-filters', github: 'wycats/rake-pipeline-web-filters'
gem 'coffee-script'
gem 'compass'
gem 'tilt'
gem 'uglifier'
gem 'yui-compressor'
gem 'libv8', '~> 3.16.0'
group :development, :test do
gem 'rake'
end
@ -25,9 +17,6 @@ end
group :development do
# gem 'debugger'
gem 'foreman'
gem 'rerun', '~> 0.10.0'
gem 'guard'
gem 'rb-fsevent', '~> 0.9.1'
end
group :test do

View File

@ -1,75 +1,21 @@
GIT
remote: git://github.com/livingsocial/rake-pipeline.git
revision: a75d96fbadcc659a35a0ae59212e0bc60b58cc54
PATH
remote: waiter
specs:
rake-pipeline (0.8.0)
json
rake (~> 10.1.0)
thor
GIT
remote: git://github.com/wycats/rake-pipeline-web-filters.git
revision: 7bd283aac83d7c46a8908f089033a6087d7cd68f
specs:
rake-pipeline-web-filters (0.6.0)
rack
rake-pipeline (~> 0.6)
travis-web (0.0.1)
GEM
remote: http://rubygems.org/
specs:
backports (3.6.3)
celluloid (0.16.0)
timers (~> 4.0.0)
chunky_png (1.3.1)
coderay (1.1.0)
coffee-script (2.3.0)
coffee-script-source
execjs
coffee-script-source (1.8.0)
compass (1.0.1)
chunky_png (~> 1.2)
compass-core (~> 1.0.1)
compass-import-once (~> 1.0.5)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
sass (>= 3.3.13, < 3.5)
compass-core (1.0.1)
multi_json (~> 1.0)
sass (>= 3.3.0, < 3.5)
compass-import-once (1.0.5)
sass (>= 3.2, < 3.5)
diff-lcs (1.2.5)
dotenv (0.11.1)
dotenv-deployment (~> 0.0.2)
dotenv-deployment (0.0.2)
execjs (2.2.1)
ffi (1.9.6)
foreman (0.75.0)
dotenv (~> 0.11.1)
thor (~> 0.19.1)
formatador (0.2.5)
guard (2.6.1)
formatador (>= 0.2.4)
listen (~> 2.7)
lumberjack (~> 1.0)
pry (>= 0.9.12)
thor (>= 0.18.1)
hashr (0.0.22)
hitimes (1.2.2)
json (1.8.1)
libv8 (3.16.14.7)
listen (2.7.11)
celluloid (>= 0.15.2)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
lumberjack (1.0.9)
method_source (0.8.2)
multi_json (1.10.1)
pry (0.10.1)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
puma (2.9.1)
rack (>= 1.1, < 2.0)
rack (1.5.2)
@ -82,11 +28,6 @@ GEM
rack-test (0.6.2)
rack (>= 1.0)
rake (10.1.1)
rb-fsevent (0.9.4)
rb-inotify (0.9.5)
ffi (>= 0.5.0)
rerun (0.10.0)
listen (~> 2.7, >= 2.7.3)
rspec (2.99.0)
rspec-core (~> 2.99.0)
rspec-expectations (~> 2.99.0)
@ -95,7 +36,6 @@ GEM
rspec-expectations (2.99.2)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.99.2)
sass (3.4.5)
sinatra (1.4.5)
rack (~> 1.4)
rack-protection (~> 1.4)
@ -107,38 +47,21 @@ GEM
rack-test
sinatra (~> 1.4.0)
tilt (~> 1.3)
slop (3.6.0)
thor (0.19.1)
tilt (1.4.1)
timers (4.0.1)
hitimes
uglifier (2.5.3)
execjs (>= 0.3.0)
json (>= 1.8.0)
yui-compressor (0.12.0)
PLATFORMS
ruby
DEPENDENCIES
coffee-script
compass
foreman
guard
hashr
libv8 (~> 3.16.0)
puma
rack-mobile-detect
rack-protection (~> 1.3)
rack-ssl (~> 1.3)
rake
rake-pipeline!
rake-pipeline-web-filters!
rb-fsevent (~> 0.9.1)
rerun (~> 0.10.0)
rspec (~> 2.11)
sinatra
sinatra-contrib
tilt
uglifier
yui-compressor
travis-web!

View File

@ -1,7 +0,0 @@
$: << 'lib'
guard 'assets' do
watch(%r(^Assetfile))
watch(%r(^assets))
end

View File

@ -1,6 +0,0 @@
$: << 'lib'
guard 'specs' do
watch(%r(^public))
end

View File

@ -1,3 +1,3 @@
web: bundle exec rackup -s puma -p $PORT
web: bundle exec rackup -s puma -p $PORT waiter/config.ru
assets: rerun -x -p 'assets/**/*' 'bundle exec rakep'
#specs: rerun -x -p 'public/**/*' './run_jasmine.coffee public/spec.html'

View File

@ -2,56 +2,26 @@
[![Build Status](https://travis-ci.org/travis-ci/travis-web.png?branch=master)](https://travis-ci.org/travis-ci/travis-web)
### Running the app
The app is developed using [http://ember-cli.com](Ember CLI). It requires nodejs
with npm installed.
In order to run the app you need to install dependencies with:
bundle install
bower install
npm install
Then you have to run the server, the easiest way to do this is to
use foreman:
Now you can run the server:
bundle exec foreman start
ember serve
Now you can open [localhost:5000](http://localhost:5000)
And open http://localhost:4200 in the browser.
By default it uses the official API at `https://api.travis-ci.org`, but you
can customize the API server URL using:
### Running tests
To run a test suite execute:
API_ENDPOINT="http://localhost:3000/" bundle exec foreman start
ember test
This will run against API run locally.
You can also start an interactive test runner for easier development:
### Compiling assets manually
bundle exec rakep
ENV=production bundle exec rakep
### Running the spec suite
First, start the app (see above).
bundle exec foreman start
To run the Ruby specs, run rspec against the spec/ directory:
bundle exec rspec spec/
To run the Jasmine specs, open the spec page: [localhost:5000/spec.html](http://localhost:5000/spec.html)
### i18n
Localization for travis-web is managed via [localeapp](http://localeapp.com).
If you are interested in improving the existing localizations or adding
a new locale, please contact us on irc (#travis) and we will set you up.
Please do **not** edit the YAML files directly.
Localization data can be synced with the following rake task:
bundle exec localeapp:update
This will publish any new keys in en.yml, as well as any missing keys
from your handlebars templates and pull down the latest localizations.
*note*: You will need to have the localeapp api key exported to
LOCALEAPP_API_KEY
ember test --serve

View File

@ -1,36 +0,0 @@
$: << 'lib'
namespace :localeapp do
desc "syncs localeapp, yaml and handlebars"
task :update do
require 'localeapp-handlebars_i18n'
Localeapp::HandlebarsI18n.configure($stdout) do |config|
config.hbs_load_path = Dir[File.expand_path '../assets/scripts/app/templates/**/*.hbs', __FILE__]
config.yml_load_path = File.expand_path '../locales/', __FILE__
config.localeapp_api_key = ENV['LOCALEAPP_API_KEY']
end
system "localeapp push locales/#{Localeapp::HandlebarsI18n.default_locale}.yml"
Localeapp::HandlebarsI18n.send_missing_translations
system "localeapp pull"
end
end
namespace :ember do
desc 'update ember'
task :update do
if File.exists?('tmp/ember.js')
system 'cd tmp/ember.js; git fetch origin; git reset --hard origin/master'
else
system 'git clone https://github.com/emberjs/ember.js.git tmp/ember.js'
end
system 'cd tmp/ember.js; bundle update'
system 'cd tmp/ember.js; rake dist'
system 'cp tmp/ember.js/dist/ember.js assets/javascripts/vendor/ember.js'
end
end
task :update_emojis do
s = Dir.glob('assets/images/emoji/*.png').map {|png| png.split('/', 4)[3].gsub('.png', '')}.map{|png| "'#{png}'"}.join(", ")
e = "@EmojiDictionary = [#{s}]"
File.open("assets/scripts/config/emoij.coffee", "w") {|f| f.write(e) }
end

View File

@ -0,0 +1,23 @@
`import DS from 'ember-data'`
`import config from 'travis/config/environment'`
Adapter = DS.ActiveModelAdapter.extend
host: config.apiEndpoint
coalesceFindRequests: true
ajaxOptions: (url, type, options) ->
hash = @_super(url, type, options)
hash.headers ||= {}
hash.headers['accept'] = 'application/json; version=2'
if token = Travis.sessionStorage.getItem('travis.token')
hash.headers['Authorization'] ||= "token #{token}"
hash
findMany: (store, type, ids) ->
@ajax(@buildURL(type.typeKey), 'GET', data: { ids: ids })
`export default Adapter`

View File

@ -0,0 +1,26 @@
`import Ember from 'ember'`
`import ApplicationAdapter from 'travis/adapters/application'`
Adapter = ApplicationAdapter.extend
namespace: 'settings'
buildURL: (type, id, record) ->
url = @_super.apply this, arguments
if record && (repoId = Ember.get(record, 'repo.id'))
delimiter = if url.indexOf('?') != -1 then '&' else '?'
url = "#{url}#{delimiter}repository_id=#{repoId}"
url
updateRecord: (store, type, record) ->
data = {};
serializer = store.serializerFor(type.typeKey);
serializer.serializeIntoHash(data, type, record);
id = Ember.get(record, 'id');
this.ajax(this.buildURL(type.typeKey, id, record), "PATCH", { data: data })
`export default Adapter`

View File

@ -0,0 +1,24 @@
`import Ember from 'ember'`
`import ApplicationAdapter from 'travis/adapters/application'`
Adapter = ApplicationAdapter.extend
namespace: 'settings'
find: (store, type, id, record) ->
@ajax(this.urlPrefix() + '/ssh_key/' + id, 'GET')
deleteRecord: (store, type, record) ->
id = Ember.get(record, 'id')
@ajax(this.urlPrefix() + '/ssh_key/' + id, "DELETE");
createRecord: (store, type, record) ->
data = {};
serializer = store.serializerFor(type.typeKey);
serializer.serializeIntoHash(data, type, record, { includeId: true });
id = Ember.get(record, 'id')
this.ajax(this.urlPrefix() + '/ssh_key/' + id, "PATCH", { data: data })
`export default Adapter`

98
app/app.coffee Normal file
View File

@ -0,0 +1,98 @@
`import Ember from 'ember'`
`import Resolver from 'ember/resolver'`
`import loadInitializers from 'ember/load-initializers'`
`import config from './config/environment'`
`import mb from 'travis/helpers/mb'`
`import label from 'travis/helpers/label'`
`import travisField from 'travis/helpers/travis-field'`
`import travisErrors from 'travis/helpers/travis-errors'`
`import tipsy from 'travis/helpers/tipsy'`
#`import input from 'travis/helpers/input'`
`import filterInput from 'travis/helpers/filter-input'`
Ember.Handlebars.registerHelper('label', label)
Ember.Handlebars.registerHelper('travis-field', travisField)
Ember.Handlebars.registerHelper('travis-errors', travisErrors)
Ember.Handlebars.registerHelper('tipsy', tipsy)
#Ember.Handlebars.registerHelper('input', input)
Ember.Handlebars.registerHelper('filter-input', filterInput)
Ember.Handlebars.registerBoundHelper('mb', mb)
Ember.MODEL_FACTORY_INJECTIONS = true
App = Ember.Application.extend(Ember.Evented,
LOG_TRANSITIONS: true
LOG_TRANSITIONS_INTERNAL: true
LOG_ACTIVE_GENERATION: true
LOG_MODULE_RESOLVER: true
LOG_VIEW_LOOKUPS: true
#LOG_RESOLVER: true
modulePrefix: config.modulePrefix
podModulePrefix: config.podModulePrefix
Resolver: Resolver
lookup: ->
@__container__.lookup.apply @__container__, arguments
flash: (options) ->
Travis.lookup('controller:flash').loadFlashes([options])
toggleSidebar: ->
$('body').toggleClass('maximized')
# TODO gotta force redraws here :/
element = $('<span></span>')
$('#top .profile').append(element)
Em.run.later (-> element.remove()), 10
element = $('<span></span>')
$('#repo').append(element)
Em.run.later (-> element.remove()), 10
ready: ->
location.href = location.href.replace('#!/', '') if location.hash.slice(0, 2) == '#!'
@on 'user:signed_in', (user) ->
Travis.onUserUpdate(user)
@on 'user:synced', (user) ->
Travis.onUserUpdate(user)
currentDate: ->
new Date()
onUserUpdate: (user) ->
if config.pro
@identifyCustomer(user)
@subscribePusher(user)
@setupCharm(user)
subscribePusher: (user) ->
channels = user.channels
channels = channels.map (channel) ->
if channel.match /^private-/
channel
else
"private-#{channel}"
Travis.pusher.subscribeAll(channels)
setupCharm: (user) ->
$.extend window.__CHARM,
customer: user.login,
customer_id: user.id,
email: user.email
displayCharm: ->
__CHARM.show()
identifyCustomer: (user) ->
if _cio && _cio.identify
_cio.identify
id: user.id
email: user.email
name: user.name
created_at: (Date.parse(user.created_at) / 1000) || null
login: user.login
)
loadInitializers(App, config.modulePrefix)
`export default App`

View File

@ -1,4 +1,6 @@
Travis.TravisSwitchComponent = Ember.Component.extend
`import Ember from 'ember'`
Component = Ember.Component.extend
tagName: 'a'
classNames: ['travis-switch']
classNameBindings: ['_active:active']
@ -19,3 +21,5 @@ Travis.TravisSwitchComponent = Ember.Component.extend
# allow for bindings to propagate
Ember.run.next this, ->
@sendAction('action', target)
`export default Component`

View File

@ -1,4 +1,6 @@
Travis.AccountController = Ember.ObjectController.extend
`import Ember from 'ember'`
Controller = Ember.ObjectController.extend
allHooks: []
needs: ['currentUser']
userBinding: 'controllers.currentUser'
@ -20,7 +22,12 @@ Travis.AccountController = Ember.ObjectController.extend
reloadHooks: ->
if login = @get('login')
@set('allHooks', Travis.Hook.find(all: true, owner_name: login))
hooks = @store.find('hook', all: true, owner_name: login)
hooks.then () ->
hooks.set('isLoaded', true)
@set('allHooks', hooks)
hooks: (->
@reloadHooks() unless hooks = @get('allHooks')
@ -33,9 +40,11 @@ Travis.AccountController = Ember.ObjectController.extend
).property('allHooks.length', 'allHooks')
showPrivateReposHint: (->
Travis.config.show_repos_hint == 'private'
@config.show_repos_hint == 'private'
) .property()
showPublicReposHint: (->
Travis.config.show_repos_hint == 'public'
@config.show_repos_hint == 'public'
) .property()
`export default Controller`

View File

@ -0,0 +1,6 @@
`import Ember from 'ember'`
Controller = Ember.ArrayController.extend
tab: 'accounts'
`export default Controller`

View File

@ -0,0 +1,7 @@
`import Ember from 'ember'`
Controller = Ember.Controller.extend
needs: ['currentUser', 'repos']
userBinding: 'controllers.currentUser'
`export default Controller`

View File

@ -0,0 +1,5 @@
`import Ember from 'ember'`
Controller = Ember.Controller.extend()
`export default Controller`

View File

@ -0,0 +1,26 @@
`import Ember from 'ember'`
`import { gravatarImage } from 'travis/utils/urls'`
`import GithubUrlPropertievs from 'travis/mixins/github-url-properties'`
Controller = Ember.Controller.extend GithubUrlPropertievs,
needs: ['repo']
repoBinding: 'controllers.repo.repo'
commitBinding: 'build.commit'
currentUserBinding: 'controllers.repo.currentUser'
tabBinding: 'controllers.repo.tab'
currentItemBinding: 'build'
loading: (->
@get('build.isLoading')
).property('build.isLoading')
urlCommitterGravatarImage: (->
gravatarImage(@get('commit.committerEmail'), 40)
).property('commit.committerEmail')
urlAuthorGravatarImage: (->
gravatarImage(@get('commit.authorEmail'), 40)
).property('commit.authorEmail')
`export default Controller`

View File

@ -0,0 +1,15 @@
`import Ember from 'ember'`
`import { colorForState } from 'travis/utils/helpers'`
`import GithubUrlProperties from 'travis/mixins/github-url-properties'`
Controller = Ember.ObjectController.extend(GithubUrlProperties,
needs: ['builds']
isPullRequestsListBinding: 'controllers.builds.isPullRequestsList'
buildBinding: 'content'
color: (->
colorForState(@get('build.state'))
).property('build.state')
)
`export default Controller`

View File

@ -1,4 +1,6 @@
Travis.BuildsController = Em.ArrayController.extend
`import Ember from 'ember'`
Controller = Ember.ArrayController.extend
isPullRequestsList: false
sortAscending: false
@ -14,8 +16,17 @@ Travis.BuildsController = Em.ArrayController.extend
showMore: ->
id = @get('repo.id')
number = @get('lastObject.number')
@get('content').load Travis.Build.olderThanNumber(id, number, @get('tab'))
@get('content').load @olderThanNumber(id, number, @get('tab'))
displayShowMoreButton: (->
@get('tab') != 'branches' and parseInt(@get('lastObject.number')) > 1
).property('tab', 'lastObject.number')
olderThanNumber: (id, number, type) ->
options = { repository_id: id, after_number: number }
if type?
options.event_type = type.replace(/s$/, '') # poor man's singularize
@store.find('build', options)
`export default Controller`

View File

@ -0,0 +1,25 @@
`import Ember from 'ember'`
`import Ajax from 'travis/utils/ajax'`
Controller = Ember.ObjectController.extend
isDeleting: false
needs: ['repo', 'caches']
repo: Ember.computed.alias('controllers.repo.repo')
actions:
delete: ->
return if @get('isDeleting')
if confirm('Are you sure?')
@set('isDeleting', true)
data = { branch: @get('branch') }
deletingDone = => @set('isDeleting', false)
repo = @get('repo')
Ajax.ajax("/repos/#{repo.get('id')}/caches", "DELETE", data: data).then(deletingDone, deletingDone).then =>
model = @get('model')
@get('controllers.caches').removeObject(model)
`export default Controller`

View File

@ -0,0 +1,27 @@
`import Ember from 'ember'`
`import Ajax from 'travis/utils/ajax'`
Controller = Ember.ObjectController.extend
isDeleting: false
needs: ['repo', 'caches']
repo: Ember.computed.alias('controllers.repo.repo')
actions:
delete: ->
return if @get('isDeleting')
if confirm('Are you sure?')
@set('isDeleting', true)
data = { branch: @get('branch'), match: @get('slug') }
deletingDone = => @set('isDeleting', false)
repo = @get('repo')
Ajax.ajax("/repos/#{repo.get('id')}/caches", "DELETE", data: data).then(deletingDone, deletingDone).then =>
model = @get('model')
@get('parent.caches').removeObject(model)
if @get('parent.caches.length') == 0
@get('controllers.caches').removeObject(@get('parent'))
`export default Controller`

View File

@ -0,0 +1,22 @@
`import Ember from 'ember'`
`import Ajax from 'travis/utils/ajax'`
Controller = Ember.ArrayController.extend
isDeleting: false
needs: ['repo']
repo: Ember.computed.alias('controllers.repo.repo')
actions:
deleteRepoCache: ->
return if @get('isDeleting')
if confirm('Are you sure?')
@set('isDeleting', true)
deletingDone = => @set('isDeleting', false)
repo = @get('repo')
Ajax.ajax("/repos/#{repo.get('id')}/caches", "DELETE").then(deletingDone, deletingDone).then =>
@clear()
`export default Controller`

View File

@ -1,4 +1,4 @@
Travis.CurrentUserController = Em.ObjectController.extend
Controller = Ember.ObjectController.extend
sync: ->
@get('model').sync()
@ -9,3 +9,5 @@ Travis.CurrentUserController = Em.ObjectController.extend
Ember.run.scheduleOnce 'routerTransitions', this, ->
@container.lookup('router:main').send('renderFirstSync')
).observes('isSyncing', 'auth.currentUser')
`export default Controller`

View File

@ -0,0 +1,29 @@
`import Ember from 'ember'`
Controller = Ember.Controller.extend
#queryParams: ['filter']
filter: null
filteredRepositories: (->
filter = @get('filter')
repos = @get('model')
if Ember.isBlank(filter)
repos
else
repos.filter (item, index) ->
item.slug.match(new RegExp(filter))
).property('filter', 'model')
updateFilter: () ->
value = @get('_lastFilterValue')
@transitionToRoute queryParams: { filter: value }
@set('filter', value)
actions:
updateFilter: (value) ->
@set('_lastFilterValue', value)
Ember.run.throttle this, @updateFilter, [], 200, false
`export default Controller`

View File

@ -1,6 +1,7 @@
require 'travis/validations'
`import Ember from 'ember'`
`import Validations from 'travis/utils/validations'`
Travis.EnvVarController = Ember.ObjectController.extend Travis.Validations,
Controller = Ember.ObjectController.extend Validations,
isEditing: false
isDeleting: false
@ -25,8 +26,7 @@ Travis.EnvVarController = Ember.ObjectController.extend Travis.Validations,
return if @get('isDeleting')
@set('isDeleting', true)
deletingDone = => @set('isDeleting', false)
@get('model').deleteRecord().then deletingDone, deletingDone
@get('model').destroyRecord()
edit: ->
@set('isEditing', true)
@ -37,7 +37,6 @@ Travis.EnvVarController = Ember.ObjectController.extend Travis.Validations,
save: ->
return if @get('isSaving')
@set('isSaving', true)
if @isValid()
env_var = @get('model')
@ -45,8 +44,5 @@ Travis.EnvVarController = Ember.ObjectController.extend Travis.Validations,
# TODO: handle errors
env_var.save().then =>
@set('isEditing', false)
@set('isSaving', false)
, =>
@set('isSaving', false)
else
@set('isSaving', false)
`export default Controller`

View File

@ -0,0 +1,6 @@
`import Ember from 'ember'`
Controller = Ember.ArrayController.extend
vars: Ember.computed.alias('model')
`export default Controller`

View File

@ -1,9 +1,11 @@
require 'travis/validations'
`import Validations from 'travis/utils/validations'`
Travis.EnvVarsNewController = Travis.Controller.extend Travis.Validations,
Controller = Ember.Controller.extend Validations,
needs: ['repo']
repo: Ember.computed.alias('controllers.repo.repo')
isSaving: false
validates:
name: ['presence']
@ -23,7 +25,7 @@ Travis.EnvVarsNewController = Travis.Controller.extend Travis.Validations,
@set('isSaving', true)
if @isValid()
env_var = Travis.EnvVar.create(
env_var = @store.createRecord('env_var',
name: @get('name')
value: @get('value')
public: @get('public')
@ -39,3 +41,5 @@ Travis.EnvVarsNewController = Travis.Controller.extend Travis.Validations,
@set('isSaving', false)
else
@set('isSaving', false)
`export default Controller`

View File

@ -0,0 +1,9 @@
`import Ember from 'ember'`
Controller = Ember.Controller.extend
needs: ['currentUser']
user: Ember.computed.alias('controllers.currentUser')
isSyncing: Ember.computed.alias('user.isSyncing')
`export default Controller`

View File

@ -1,10 +1,14 @@
Travis.FlashController = Ember.ArrayController.extend
`import Ember from 'ember'`
`import LimitedArray from 'travis/utils/limited-array'`
`import Broadcast from 'travis/models/broadcast'`
Controller = Ember.ArrayController.extend
needs: ['currentUser']
currentUserBinding: 'controllers.currentUser'
init: ->
@_super.apply this, arguments
@set('flashes', Travis.LimitedArray.create(limit: 2, content: []))
@set('flashes', LimitedArray.create(limit: 2, content: []))
model: (->
broadcasts = @get('unseenBroadcasts')
@ -20,7 +24,7 @@ Travis.FlashController = Ember.ArrayController.extend
).property('broadcasts.isLoaded', 'broadcasts.length')
broadcasts: (->
if @get('currentUser.id') then Travis.Broadcast.find() else Ember.A()
if @get('currentUser.id') then @store.find('broadcast') else Ember.A()
).property('currentUser.id')
loadFlashes: (msgs) ->
@ -31,7 +35,7 @@ Travis.FlashController = Ember.ArrayController.extend
Ember.run.later(this, (-> @get('flashes.content').removeObject(msg)), 15000)
close: (msg) ->
if msg instanceof Travis.Broadcast
if msg instanceof Broadcast
msg.setSeen()
@notifyPropertyChange('unseenBroadcasts')
else
@ -40,3 +44,5 @@ Travis.FlashController = Ember.ArrayController.extend
actions:
close: (msg) ->
@close(msg)
`export default Controller`

View File

@ -1,4 +1,7 @@
Travis.JobController = Em.Controller.extend
`import Ember from 'ember'`
`import { githubCommit } from 'travis/utils/urls'`
Controller = Ember.Controller.extend
needs: ['repo']
repoBinding: 'controllers.repo.repo'
@ -10,5 +13,7 @@ Travis.JobController = Em.Controller.extend
currentItemBinding: 'job'
urlGithubCommit: (->
Travis.Urls.githubCommit(@get('repo.slug'), @get('commit.sha'))
githubCommit(@get('repo.slug'), @get('commit.sha'))
).property('repo.slug', 'commit.sha')
`export default Controller`

View File

@ -0,0 +1,5 @@
`import Ember from 'ember'`
Controller = Ember.Controller.extend()
`export default Controller`

View File

@ -1,4 +1,6 @@
Travis.ProfileController = Travis.Controller.extend
`import Ember from 'ember'`
Controller = Ember.Controller.extend
name: 'profile'
needs: ['currentUser', 'accounts', 'account']
@ -6,7 +8,7 @@ Travis.ProfileController = Travis.Controller.extend
accountBinding: 'controllers.account'
activate: (action, params) ->
this["view#{$.camelize(action)}"]()
this["view_#{action}".camelize()]()
viewHooks: ->
@connectTab('hooks')
@ -16,14 +18,11 @@ Travis.ProfileController = Travis.Controller.extend
@connectTab('user')
connectTab: (tab) ->
if tab == 'user'
view = 'AccountsInfoView'
else
view = "#{$.camelize(tab)}View"
viewClass = Travis[view]
@set('tab', tab)
billingUrl: (->
id = if @get('account.type') == 'user' then 'user' else @get('account.login')
"#{Travis.config.billing_endpoint}/subscriptions/#{id}"
"#{@get('config').billingEndpoint}/subscriptions/#{id}"
).property('account.login', 'account.type')
`export default Controller`

View File

@ -0,0 +1,9 @@
`import Ember from 'ember'`
Controller = Ember.ArrayController.extend
content: (->
@store.filter 'job', {}, (job) ->
['created', 'queued'].indexOf(job.get('state')) != -1
).property()
`export default Controller`

View File

@ -1,4 +1,7 @@
Travis.RepoController = Travis.Controller.extend
`import Ember from 'ember'`
`import { githubRepo } from 'travis/utils/urls'`
Controller = Ember.Controller.extend
needs: ['repos', 'currentUser', 'build', 'request', 'job']
currentUserBinding: 'controllers.currentUser'
@ -12,7 +15,7 @@ Travis.RepoController = Travis.Controller.extend
init: ->
@_super.apply this, arguments
if !Ember.testing
Visibility.every Travis.INTERVALS.updateTimes, @updateTimes.bind(this)
Visibility.every @config.intervals.updateTimes, @updateTimes.bind(this)
updateTimes: ->
Ember.run this, ->
@ -30,7 +33,7 @@ Travis.RepoController = Travis.Controller.extend
activate: (action) ->
@stopObservingLastBuild()
this["view#{$.camelize(action)}"]()
this["view_#{action}".camelize()]()
viewIndex: ->
@observeLastBuild()
@ -68,7 +71,7 @@ Travis.RepoController = Travis.Controller.extend
@connectTab('settings')
lastBuildDidChange: ->
Ember.run.scheduleOnce('data', this, @_lastBuildDidChange);
Ember.run.scheduleOnce('actions', this, @_lastBuildDidChange);
_lastBuildDidChange: ->
build = @get('repo.lastBuild')
@ -88,5 +91,7 @@ Travis.RepoController = Travis.Controller.extend
@set('tab', tab)
urlGithub: (->
Travis.Urls.githubRepo(@get('repo.slug'))
githubRepo(@get('repo.slug'))
).property('repo.slug')
`export default Controller`

View File

@ -1,6 +1,8 @@
require 'travis/limited_array'
`import Ember from 'ember'`
`import limit from 'travis/utils/computed-limit'`
`import Repo from 'travis/models/repo'`
Travis.ReposController = Ember.ArrayController.extend
Controller = Ember.ArrayController.extend
actions:
activate: (name) ->
@activate(name)
@ -27,14 +29,14 @@ Travis.ReposController = Ember.ArrayController.extend
init: ->
@_super.apply this, arguments
if !Ember.testing
Visibility.every Travis.INTERVALS.updateTimes, @updateTimes.bind(this)
Visibility.every @config.intervals.updateTimes, @updateTimes.bind(this)
recentRepos: (->
Ember.ArrayProxy.extend(
isLoadedBinding: 'repos.isLoaded'
repos: Travis.Repo.withLastBuild()
repos: Repo.withLastBuild(@store)
sorted: Ember.computed.sort('repos', 'sortedReposKeys')
content: Ember.computed.limit('sorted', 'limit')
content: limit('sorted', 'limit')
sortedReposKeys: ['sortOrder:asc']
limit: 30
).create()
@ -47,7 +49,7 @@ Travis.ReposController = Ember.ArrayController.extend
activate: (tab, params) ->
@set('sortProperties', ['sortOrder'])
@set('tab', tab)
this["view#{$.camelize(tab)}"](params)
this["view_#{tab}".camelize()](params)
viewRecent: ->
@set('content', @get('recentRepos'))
@ -57,14 +59,14 @@ Travis.ReposController = Ember.ArrayController.extend
userRepos: (->
if login = @get('currentUser.login')
Travis.Repo.accessibleBy(login)
Repo.accessibleBy(@store, login)
else
[]
).property('currentUser.login')
viewSearch: (phrase) ->
@set('search', phrase)
@set('content', Travis.Repo.search(phrase))
@set('content', Repo.search(@store, phrase))
searchObserver: (->
search = @get('search')
@ -88,3 +90,5 @@ Travis.ReposController = Ember.ArrayController.extend
else
'Could not find any repos'
).property('tab')
`export default Controller`

View File

@ -1,4 +1,6 @@
Travis.RequestController = Ember.ObjectController.extend
`import Ember from 'ember'`
Controller = Ember.ObjectController.extend
requestClass: (->
if @get('content.isAccepted')
'accepted'
@ -22,10 +24,10 @@ Travis.RequestController = Ember.ObjectController.extend
message: (->
message = @get('model.message')
if Travis.config.pro && message == "private repository"
if @config.pro && message == "private repository"
''
else
message
).property('model.message')
`export default Controller`

View File

@ -1,4 +1,6 @@
Travis.RequestsController = Ember.ArrayController.extend
`import Ember from 'ember'`
Controller = Ember.ArrayController.extend
needs: ['repo']
repo: Ember.computed.alias('controllers.repo.repo')
@ -6,3 +8,5 @@ Travis.RequestsController = Ember.ArrayController.extend
slug = @get('repo.slug')
"https://lint.travis-ci.org/#{slug}"
).property('repo.slug')
`export default Controller`

View File

@ -0,0 +1,9 @@
`import Ember from 'ember'`
Controller = Ember.ArrayController.extend
content: (->
@store.filter 'job', { state: 'started' }, (job) ->
['started', 'received'].indexOf(job.get('state')) != -1
).property()
`export default Controller`

View File

@ -1,4 +1,6 @@
Travis.SettingsIndexController = Em.ObjectController.extend
`import Ember from 'ember'`
Controller = Ember.ObjectController.extend
settings: Ember.computed.alias('model.settings')
settingsChanged: (->
@ -16,3 +18,5 @@ Travis.SettingsIndexController = Em.ObjectController.extend
save: ->
@get('model').saveSettings(@get('settings')).then null, ->
Travis.flash(error: 'There was an error while saving settings. Please try again.')
`export default Controller`

View File

@ -0,0 +1,19 @@
`import Ember from 'ember'`
Controller = Ember.ArrayController.extend
init: ->
@_super.apply this, arguments
@tickables = []
tips: [
"Did you know that you can parallelize tests on Travis CI? <a href=\"http://docs.travis-ci.com/user/speeding-up-the-build/#Paralellizing-your-build-on-one-VM?utm_source=tips\">Learn more</a>"
"Did you know that you can split a build into several smaller pieces? <a href=\"http://docs.travis-ci.com/user/speeding-up-the-build/#Parallelizing-your-builds-across-virtual-machines?utm_source=tips\">Learn more</a>"
"Did you know that you can skip a build? <a href=\"http://docs.travis-ci.com/user/how-to-skip-a-build/?utm_source=tips\">Learn more</a>"
]
tip: (->
if tips = @get('tips')
tips[Math.floor(Math.random()*tips.length)]
).property().volatile()
`export default Controller`

View File

@ -0,0 +1,67 @@
`import Ember from 'ember'`
`import Validations from 'travis/utils/validations'`
Controller = Ember.ObjectController.extend Validations,
isEditing: false
isSaving: false
isDeleting: false
defaultKey: null
needs: ['repo']
repo: Ember.computed.alias('controllers.repo.repo')
validates:
value: ['presence']
reset: ->
@set('isEditing', false)
actions:
add: ->
id = @get('repo.id')
model = @store.recordForId('sshKey', id)
if model.get('currentState.stateName') == 'root.empty'
@store.dematerializeRecord(model)
model = @store.createRecord('sshKey', id: id)
@set('model', model)
@set('isEditing', true)
save: ->
return if @get('isSaving')
@set('isSaving', true)
if @isValid()
@get('model').save().then =>
@set('isEditing', false)
@set('isSaving', false)
, (error) =>
@set('isSaving', false)
if error.errors
@addErrorsFromResponse(error.errors)
else
@set('isSaving', false)
delete: ->
return if @get('isDeleting')
@set('isDeleting', true)
deletingDone = => @set('isDeleting', false)
@get('model').deleteRecord()
@get('model').save().then(deletingDone, deletingDone).then =>
@set('model', null)
cancel: ->
if model = @get('model')
if model.get('currentState.stateName') == 'root.empty' ||
model.get('currentState.stateName').indexOf('root.loaded.created') != -1
@store.dematerializeRecord(model)
@set('model', null)
@set('isEditing', false)
edit: ->
@set('isEditing', true)
`export default Controller`

View File

@ -0,0 +1,21 @@
`import Ember from 'ember'`
Controller = Ember.Controller.extend
needs: ['currentUser']
userBinding: 'controllers.currentUser'
userName: (->
@get('user.name') || @get('user.login')
).property('user.login', 'user.name')
gravatarUrl: (->
"#{location.protocol}//www.gravatar.com/avatar/#{@get('user.gravatarId')}?s=48&d=mm"
).property('user.gravatarId')
actions: {
toggleBurgerMenu: ->
@toggleProperty('is-open')
return false
}
`export default Controller`

View File

@ -0,0 +1,10 @@
`import { safe } from 'travis/utils/helpers'`
`import Ember from "ember"`
helper = Ember.Handlebars.makeBoundHelper (value, options) ->
if value?
safe $.capitalize(value)
else
''
`export default helper`

View File

@ -0,0 +1,18 @@
`import Ember from 'ember'`
TextField = Ember.TextField.extend
keyUp: (event) ->
@sendAction('action', @get('_value'), event)
_elementValueDidChange: ->
@set('_value', @$().val());
fn = (options) ->
Ember.assert('You can only pass attributes to the `input` helper, not arguments', arguments.length < 2)
onEvent = options.hash.on
delete options.hash.on
options.hash.onEvent = onEvent || 'enter'
return Ember.Handlebars.helpers.view.call(this, TextField, options)
`export default fn`

View File

@ -0,0 +1,6 @@
`import { safe, formatCommit as formatCommitHelper } from 'travis/utils/helpers'`
helper = Ember.Handlebars.makeBoundHelper (commit) ->
safe formatCommitHelper(commit.get('sha'), commit.get('branch')) if commit
`export default helper`

View File

@ -0,0 +1,6 @@
`import { safe, formatConfig as formatConfigHelper } from 'travis/utils/helpers'`
formatConfig = (config, options) ->
safe formatConfigHelper(config)
`export default helper`

View File

@ -0,0 +1,7 @@
`import { timeInWords, safe } from 'travis/utils/helpers'`
`import Ember from "ember"`
helper = Ember.Handlebars.makeBoundHelper (duration, options) ->
safe timeInWords(duration)
`export default helper`

View File

@ -0,0 +1,7 @@
`import { formatMessage as _formatMessage, safe } from 'travis/utils/helpers'`
`import Ember from "ember"`
helper = Ember.Handlebars.makeBoundHelper (message, options) ->
safe _formatMessage(message, options.hash)
`export default helper`

View File

@ -0,0 +1,7 @@
`import { formatSha as _formatSha, safe } from 'travis/utils/helpers'`
`import Ember from "ember"`
helper = Ember.Handlebars.makeBoundHelper (sha) ->
safe _formatSha(sha)
`export default helper`

View File

@ -0,0 +1,7 @@
`import { timeAgoInWords, safe } from 'travis/utils/helpers'`
`import Ember from "ember"`
helper = Ember.Handlebars.makeBoundHelper (value, options) ->
safe timeAgoInWords(value) || '-'
`export default helper`

View File

@ -0,0 +1,12 @@
`import { formatCommit, safe } from 'travis/utils/helpers'`
`import { githubCommit as githubCommitUrl } from 'travis/utils/urls'`
helper = Ember.Handlebars.makeBoundHelper (slug, commitSha) ->
return '' unless commitSha
sha = Handlebars.Utils.escapeExpression formatCommit(commitSha)
return sha unless slug
url = Handlebars.Utils.escapeExpression githubCommitUrl(slug, sha)
safe '<a class="github-link only-on-hover" href="' + url + '">' + sha + '</a>'
`export default helper`

View File

@ -0,0 +1,10 @@
`import { safe } from 'travis/utils/helpers'`
`import Ember from "ember"`
helper = Ember.Handlebars.makeBoundHelper (state) ->
if state == 'received'
'booting'
else
state
`export default helper`

30
app/helpers/input.coffee Normal file
View File

@ -0,0 +1,30 @@
`import Ember from 'ember'`
originalInputHelper = Ember.Handlebars.helpers.input
input = (options) ->
# for now I can match label only with the property name
# passed here matches the label
name = (options.hash.value || options.hash.checked)
id = options.hash.id
# generate id only if it's not given
if name && !name.match(/\./) && !id
labels = @get('_labels')
unless labels
labels = Ember.Object.create()
@set('_labels', labels)
# for now I support only label + input in their own context
id = labels.get(name)
unless id
id = "#{name}-#{Math.round(Math.random() * 1000000)}"
labels.set(name, id)
options.hash.id = id
options.hashTypes.id = 'STRING'
options.hashContexts.id = this
originalInputHelper.call(this, options)
`export default input`

34
app/helpers/label.coffee Normal file
View File

@ -0,0 +1,34 @@
`import Ember from 'ember'`
LabelView = Ember.View.extend(
tagName: 'label'
attributeBindings: ['for', 'accesskey', 'form']
classNameBindings: ['class']
)
label = (options) ->
view = LabelView
name = options.hash.for
if name
labels = @get('_labels')
unless labels
labels = Ember.Object.create()
@set('_labels', labels)
# for now I support only label + input in their own context
id = labels.get(name)
unless id
id = "#{name}-#{Math.round(Math.random() * 1000000)}"
labels.set(name, id)
options.hash.for = id
options.hashTypes.for = 'STRING'
options.hashContexts.for = this
if options.hash.content
view = view.extend(templateName: 'helpers/label')
Ember.Handlebars.helpers.view.call(this, view, options)
`export default label`

7
app/helpers/mb.coffee Normal file
View File

@ -0,0 +1,7 @@
`import Ember from "ember"`
fn = (size) ->
if size
(size / 1024 / 1024).toFixed(2)
`export default fn`

View File

@ -0,0 +1,12 @@
`import { pathFrom } from 'travis/utils/helpers'`
`import Ember from "ember"`
helper = Ember.Handlebars.makeBoundHelper (url, options) ->
path = pathFrom(url)
if path.indexOf('...') >= 0
shas = path.split('...')
"#{shas[0][0..6]}..#{shas[1][0..6]}"
else
path
`export default helper`

7
app/helpers/tipsy.coffee Normal file
View File

@ -0,0 +1,7 @@
`import { safe } from 'travis/utils/helpers'`
`import Ember from "ember"`
helper = Ember.Handlebars.makeBoundHelper (text, tip) ->
safe '<span class="tool-tip" original-title="' + tip + '">' + text + '</span>'
`export default helper`

View File

@ -0,0 +1,23 @@
`import Ember from 'ember'`
ErrorsView = Ember.View.extend
tagName: 'span'
templateName: 'helpers/travis-errors'
classNames: ['error']
classNameBindings: ['codes', 'show']
codes: (->
@get('errors').mapBy('code')
).property('@errors', 'errors.length')
show: Ember.computed.notEmpty('errors.[]')
fn = (name, options) ->
errors = @get('errors').for(name)
window[name + 'Errors'] = errors
view = ErrorsView.create(
controller: this
errors: errors
)
Ember.Handlebars.helpers.view.call(this, view, options)
`export default fn`

View File

@ -0,0 +1,23 @@
`import Ember from 'ember'`
FormFieldRowView = Ember.View.extend
invalid: Ember.computed.notEmpty('errors.[]')
classNameBindings: ['invalid']
classNames: 'field'
fn = (name, options) ->
errors = @get('errors').for(name)
template = options.fn
delete options.fn
view = FormFieldRowView.create(
controller: this
template: template
errors: errors
name: name
classNameBindings: ['name']
)
Ember.Handlebars.helpers.view.call(this, view, options)
`export default fn`

26
app/index.html Normal file
View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>{{title}}</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{content-for 'head'}}
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,800' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="assets/vendor.css">
<link rel="stylesheet" href="assets/travis.css">
{{content-for 'head-footer'}}
</head>
<body>
{{content-for 'body'}}
<script src="assets/vendor.js"></script>
<script src="assets/travis.js"></script>
{{content-for 'body-footer'}}
</body>
</html>

View File

@ -0,0 +1,10 @@
initialize = (container, app) ->
if typeof window != 'undefined'
window.Travis = app
Initializer =
name: 'app'
initialize: initialize
`export {initialize}`
`export default Initializer`

View File

@ -0,0 +1,19 @@
`import Auth from 'travis/utils/auth'`
`import TestAuth from 'travis/utils/test-auth'`
initialize = (container, app) ->
app.register 'auth:main', if Ember.testing then TestAuth else Auth
app.inject('route', 'auth', 'auth:main')
app.inject('controller', 'auth', 'auth:main')
app.inject('application', 'auth', 'auth:main')
app.inject('auth', 'store', 'store:main')
AuthInitializer =
name: 'auth'
after: 'ember-data'
initialize: initialize
`export {initialize}`
`export default AuthInitializer`

View File

@ -0,0 +1,16 @@
`import config from 'travis/config/environment'`
initialize = (container, app) ->
if config.charmKey
window.__CHARM =
key: config.charmKey
url: "https://charmscout.herokuapp.com/feedback"
$('head').append $('<script src="https://charmscout.herokuapp.com/charmeur.js?v=2" async defer></script>')
Initializer =
name: 'charm'
initialize: initialize
`export {initialize}`
`export default Initializer`

View File

@ -0,0 +1,14 @@
`import config from 'travis/config/environment'`
initialize = (container, application) ->
application.register 'config:main', config, { instantiate: false }
application.inject('controller', 'config', 'config:main')
application.inject('route', 'config', 'config:main')
ConfigInitializer =
name: 'config'
initialize: initialize
`export {initialize}`
`export default ConfigInitializer`

View File

@ -0,0 +1,20 @@
`import config from 'travis/config/environment'`
initialize = (container) ->
if config.gaCode
window._gaq = []
_gaq.push(['_setAccount', config.gaCode])
ga = document.createElement('script')
ga.type = 'text/javascript'
ga.async = true
ga.src = 'https://ssl.google-analytics.com/ga.js'
s = document.getElementsByTagName('script')[0]
s.parentNode.insertBefore(ga, s)
GAInitializer =
name: 'google-analytics'
initialize: initialize
`export {initialize}`
`export default GAInitializer`

View File

@ -0,0 +1,11 @@
`import TravisLocation from 'travis/utils/location'`
initialize = (container, application) ->
application.register 'location:travis', TravisLocation
Initializer =
name: 'location'
initialize: initialize
`export { initialize }`
`export default Initializer`

View File

@ -0,0 +1,21 @@
`import config from 'travis/config/environment'`
`import TravisPusher from 'travis/utils/pusher'`
initialize = (container, application) ->
if config.pusher.key
application.pusher = new TravisPusher(config.pusher)
application.register 'pusher:main', application.pusher, { instantiate: false }
application.inject('route', 'pusher', 'pusher:main')
application.pusher.store = container.lookup('store:main')
PusherInitializer =
name: 'pusher'
after: 'ember-data'
initialize: initialize
`export {initialize}`
`export default PusherInitializer`

View File

@ -0,0 +1,19 @@
`import Slider from 'travis/utils/slider'`
`import Tailing from 'travis/utils/tailing'`
`import ToTop from 'travis/utils/to-top'`
`import config from 'travis/config/environment'`
initialize = (container, application) ->
application.slider = new Slider(application.storage)
application.tailing = new Tailing($(window), '#tail', '#log')
application.toTop = new ToTop($(window), '.to-top', '#log-container')
application.register 'slider:main', application.slider, { instantiate: false }
application.inject('controller', 'slider', 'slider:main')
Initializer =
name: 'services'
initialize: initialize
`export {initialize}`
`export default Initializer`

View File

@ -0,0 +1,58 @@
`import Ember from 'ember'`
Storage = Em.Object.extend
init: ->
@set('storage', {})
key: (key) ->
"__#{key.replace('.', '__')}"
getItem: (k) ->
return @get("storage.#{@key(k)}")
setItem: (k,v) ->
@set("storage.#{@key(k)}", v)
removeItem: (k) ->
@setItem(k, null)
clear: ->
@set('storage', {})
sessionStorage = (->
storage = null
try
# firefox will not throw error on access for sessionStorage var,
# you need to actually get something from session
sessionStorage.getItem('foo')
storage = sessionStorage
catch err
storage = Storage.create()
storage
)()
storage = (->
storage = null
try
storage = window.localStorage || throw('no storage')
catch err
storage = Storage.create()
storage
)()
initialize = (container, application) ->
application.register 'storage:main', storage, { instantiate: false }
application.register 'sessionStorage:main', sessionStorage, { instantiate: false }
application.inject('auth', 'storage', 'storage:main')
application.inject('auth', 'sessionStorage', 'sessionStorage:main')
# I still use Travis.storage in some places which are not that easy to
# refactor
application.storage = storage
application.sessionStorage = sessionStorage
StorageInitializer =
name: 'storage'
before: 'services'
initialize: initialize
`export {initialize}`
`export default StorageInitializer`

View File

@ -0,0 +1,18 @@
stylesheetsManager = Ember.Object.create
enable: (id) ->
$("##{id}").removeAttr('disabled')
disable: (id) ->
$("##{id}").attr('disabled', 'disabled')
initialize = (container, application) ->
application.register 'stylesheetsManager:main', stylesheetsManager, { instantiate: false }
application.inject('route', 'stylesheetsManager', 'stylesheetsManager:main')
StylesheetsManagerInitializer =
name: 'inject-stylesheets-manager'
initialize: initialize
`export {initialize}`
`export default StylesheetsManagerInitializer`

View File

@ -0,0 +1,12 @@
`import { githubCommit, githubPullRequest } from 'travis/utils/urls'`
mixin = Ember.Mixin.create
urlGithubCommit: (->
githubCommit(@get('repo.slug'), @get('commit.sha'))
).property('repo.slug', 'commit.sha')
urlGithubPullRequest: (->
githubPullRequest(@get('repo.slug'), @get('build.pullRequestNumber'))
).property('repo.slug', 'build.pullRequestNumber')
`export default mixin`

0
app/models/.gitkeep Normal file
View File

12
app/models/account.coffee Normal file
View File

@ -0,0 +1,12 @@
`import Ember from 'ember'`
`import Model from 'travis/models/model'`
Account = Model.extend
name: DS.attr()
type: DS.attr()
reposCount: DS.attr('number')
subscribed: DS.attr('boolean')
education: DS.attr('boolean')
loginBinding: 'id'
`export default Account`

View File

@ -0,0 +1,13 @@
`import Ember from 'ember'`
`import Model from 'travis/models/model'`
Annotation = Model.extend
jobId: DS.attr('number')
description: DS.attr()
url: DS.attr()
status: DS.attr()
providerName: DS.attr()
job: DS.belongsTo('job')
`export default Annotation`

26
app/models/branch.coffee Normal file
View File

@ -0,0 +1,26 @@
`import Ember from 'ember'`
`import Model from 'travis/models/model'`
Branch = Model.extend
repositoryId: DS.attr('number')
commitId: DS.attr('number')
state: DS.attr()
number: DS.attr('number')
branch: DS.attr()
message: DS.attr()
result: DS.attr('number')
duration: DS.attr('number')
startedAt: DS.attr()
finishedAt: DS.attr()
commit: DS.belongsTo('commit')
repo: (->
@store.find('repo', @get('repositoryId')) if @get('repositoryId')
).property('repositoryId')
updateTimes: ->
@notifyPropertyChange 'started_at'
@notifyPropertyChange 'finished_at'
`export default Branch`

View File

@ -1,27 +1,29 @@
require 'travis/model'
`import Ember from 'ember'`
`import Model from 'travis/models/model'`
@Travis.Broadcast = Travis.Model.extend
message: Ember.attr('string')
Broadcast = Model.extend
message: DS.attr()
toObject: ->
{ type: 'broadcast', id: @get('id'), message: @get('message') }
isSeen: (->
@get('id') in Travis.Broadcast.seen
@get('id') in Broadcast.get('seen')
).property()
setSeen: ->
Travis.Broadcast.seen.pushObject(@get('id'))
Travis.storage.setItem('travis.seen_broadcasts', JSON.stringify(Travis.Broadcast.seen))
Broadcast.get('seen').pushObject(@get('id'))
Travis.storage.setItem('travis.seen_broadcasts', JSON.stringify(Broadcast.get('seen')))
@notifyPropertyChange('isSeen')
@Travis.Broadcast.reopenClass
Broadcast.reopenClass
seen: (->
seenBroadcasts = Travis.storage.getItem('travis.seen_broadcasts')
seenBroadcasts = JSON.parse(seenBroadcasts) if seenBroadcasts?
Ember.A(seenBroadcasts || [])
)()
).property()
# TODO fix or monkey-patch the adapter's url and key lookup/generation crap
# url: 'users/broadcasts'
`export default Broadcast`

View File

@ -1,32 +1,32 @@
require 'travis/model'
`import { durationFrom, configKeys, compact } from 'travis/utils/helpers'`
`import Ajax from 'travis/utils/ajax'`
`import configKeysMap from 'travis/utils/keys-map'`
`import Ember from 'ember'`
`import Model from 'travis/models/model'`
`import DurationCalculations from 'travis/utils/duration-calculations'`
@Travis.Build = Travis.Model.extend Travis.DurationCalculations,
repositoryId: Ember.attr('number')
commitId: Ember.attr('number')
Build = Model.extend DurationCalculations,
state: DS.attr()
number: DS.attr('number')
branch: DS.attr('string')
message: DS.attr('string')
_duration: DS.attr('number')
_config: DS.attr('object')
_startedAt: DS.attr()
_finishedAt: DS.attr()
pullRequest: DS.attr('boolean')
pullRequestTitle: DS.attr()
pullRequestNumber: DS.attr('number')
state: Ember.attr('string')
number: Ember.attr(Number)
branch: Ember.attr('string')
message: Ember.attr('string')
_duration: Ember.attr(Number, key: 'duration')
_config: Ember.attr('object', key: 'config')
_startedAt: Ember.attr('string', key: 'started_at')
_finishedAt: Ember.attr('string', key: 'finished_at')
pullRequest: Ember.attr('boolean')
pullRequestTitle: Ember.attr('string')
pullRequestNumber: Ember.attr(Number)
# TODO add eventType to the api for api build requests
# eventType: Ember.attr('string')
repo: Ember.belongsTo('Travis.Repo', key: 'repository_id')
commit: Ember.belongsTo('Travis.Commit')
jobs: Ember.hasMany('Travis.Job')
repo: DS.belongsTo('repo', async: true)
commit: DS.belongsTo('commit', async: true)
jobs: DS.hasMany('job', async: true)
config: (->
console.log('config')
if config = @get('_config')
Travis.Helpers.compact(config)
else
compact(config)
else if @get('currentState.stateName') != 'root.loading'
return if @get('isFetchingConfig')
@set 'isFetchingConfig', true
@ -76,7 +76,7 @@ require 'travis/model'
keys = []
@get('jobs').forEach (job) ->
Travis.Helpers.configKeys(job.get('config')).forEach (key) ->
configKeys(job.get('config')).forEach (key) ->
keys.pushObject key unless keys.contains key
keys
@ -85,7 +85,7 @@ require 'travis/model'
configKeys: (->
keys = @get('rawConfigKeys')
headers = ['Job', 'Duration', 'Finished']
$.map(headers.concat(keys), (key) -> if Travis.CONFIG_KEYS_MAP.hasOwnProperty(key) then Travis.CONFIG_KEYS_MAP[key] else key)
$.map(headers.concat(keys), (key) -> if configKeysMap.hasOwnProperty(key) then configKeysMap[key] else key)
).property('rawConfigKeys.length')
canCancel: (->
@ -93,29 +93,15 @@ require 'travis/model'
).property('isFinished', 'jobs.@each.canCancel')
cancel: (->
Travis.ajax.post "/builds/#{@get('id')}/cancel"
Ajax.post "/builds/#{@get('id')}/cancel"
)
requeue: ->
Travis.ajax.post "/builds/#{@get('id')}/restart"
Ajax.post "/builds/#{@get('id')}/restart"
formattedFinishedAt: (->
if finishedAt = @get('finishedAt')
moment(finishedAt).format('lll')
).property('finishedAt')
@Travis.Build.reopenClass
byRepoId: (id, parameters) ->
@find($.extend(parameters || {}, repository_id: id))
branches: (options) ->
@find repository_id: options.repoId, branches: true
olderThanNumber: (id, build_number, type) ->
console.log type
# TODO fix this api and use some kind of pagination scheme
options = { repository_id: id, after_number: build_number }
if type?
options.event_type = type.replace(/s$/, '') # poor man's singularize
@find(options)
`export default Build`

35
app/models/commit.coffee Normal file
View File

@ -0,0 +1,35 @@
`import Ember from 'ember'`
`import Model from 'travis/models/model'`
`import Build from 'travis/models/build'`
Commit = Model.extend
sha: DS.attr()
branch: DS.attr()
message: DS.attr()
compareUrl: DS.attr()
authorName: DS.attr()
authorEmail: DS.attr()
committerName: DS.attr()
committerEmail: DS.attr()
committedAt: DS.attr()
build: DS.belongsTo('build')
subject: ( ->
@get('message').split("\n", 1)[0]
).property('message')
body: ( ->
message = @get('message')
if message.indexOf("\n") > 0
message.substr(message.indexOf("\n") + 1).trim()
else
""
).property('message')
authorIsCommitter: ( ->
@get('authorName') == @get('committerName') and
@get('authorEmail') == @get('committerEmail')
).property('authorName', 'authorEmail', 'committerName', 'committerEmail')
`export default Commit`

11
app/models/env-var.coffee Normal file
View File

@ -0,0 +1,11 @@
`import Ember from 'ember'`
`import Model from 'travis/models/model'`
EnvVar = Model.extend
name: DS.attr()
value: DS.attr()
public: DS.attr('boolean')
repo: DS.belongsTo('repo', async: true)
`export default EnvVar`

34
app/models/hook.coffee Normal file
View File

@ -0,0 +1,34 @@
`import Ember from 'ember'`
`import Model from 'travis/models/model'`
`import config from 'travis/config/environment'`
Hook = Model.extend
name: DS.attr()
ownerName: DS.attr()
description: DS.attr()
active: DS.attr('boolean')
admin: DS.attr('boolean')
private: DS.attr('boolean')
account: (->
@get('slug').split('/')[0]
).property('slug')
slug: (->
"#{@get('ownerName')}/#{@get('name')}"
).property('ownerName', 'name')
urlGithub: (->
"#{config.sourceEndpoint}/#{@get('slug')}"
).property()
urlGithubAdmin: (->
"#{config.sourceEndpoint}/#{@get('slug')}/settings/hooks#travis_minibucket"
).property()
toggle: ->
return if @get('isSaving')
@set 'active', !@get('active')
@save()
`export default Hook`

View File

@ -1,30 +1,36 @@
require 'travis/model'
`import { durationFrom, configKeys, compact } from 'travis/utils/helpers'`
`import Ajax from 'travis/utils/ajax'`
`import configKeysMap from 'travis/utils/keys-map'`
`import Ember from 'ember'`
`import Model from 'travis/models/model'`
`import Log from 'travis/models/log'`
`import DurationCalculations from 'travis/utils/duration-calculations'`
@Travis.Job = Travis.Model.extend Travis.DurationCalculations,
repoId: Ember.attr('string', key: 'repository_id')
buildId: Ember.attr('string')
commitId: Ember.attr('string')
logId: Ember.attr('string')
Job = Model.extend DurationCalculations,
logId: DS.attr()
queue: Ember.attr('string')
state: Ember.attr('string')
number: Ember.attr('string')
_startedAt: Ember.attr('string', key: 'started_at')
_finishedAt: Ember.attr('string', key: 'finished_at')
allowFailure: Ember.attr('boolean')
queue: DS.attr()
state: DS.attr()
number: DS.attr()
_startedAt: DS.attr()
_finishedAt: DS.attr()
allowFailure: DS.attr('boolean')
tags: DS.attr()
repositorySlug: Ember.attr('string')
repo: Ember.belongsTo('Travis.Repo', key: 'repository_id')
build: Ember.belongsTo('Travis.Build')
commit: Ember.belongsTo('Travis.Commit')
repositoryPrivate: DS.attr()
annotations: Ember.hasMany('Travis.Annotation')
repositorySlug: DS.attr()
repo: DS.belongsTo('repo', async: true)
build: DS.belongsTo('build', async: true)
commit: DS.belongsTo('commit', async: true)
_config: Ember.attr('object', key: 'config')
annotations: DS.hasMany('annotation')
_config: DS.attr('object')
log: ( ->
@set('isLogAccessed', true)
Travis.Log.create(job: this)
Log.create(job: this)
).property()
startedAt: (->
@ -43,8 +49,8 @@ require 'travis/model'
config: (->
if config = @get('_config')
Travis.Helpers.compact(config)
else
compact(config)
else if @get('currentState.stateName') != 'root.loading'
return if @get('isFetchingConfig')
@set 'isFetchingConfig', true
@ -86,11 +92,11 @@ require 'travis/model'
).property('state')
cancel: (->
Travis.ajax.post "/jobs/#{@get('id')}/cancel"
Ajax.post "/jobs/#{@get('id')}/cancel"
)
removeLog: ->
Travis.ajax.patch("/jobs/#{@get('id')}/log").then =>
Ajax.patch("/jobs/#{@get('id')}/log").then =>
@reloadLog()
reloadLog: ->
@ -98,7 +104,7 @@ require 'travis/model'
@get('log').fetch()
requeue: ->
Travis.ajax.post "/jobs/#{@get('id')}/restart"
Ajax.post "/jobs/#{@get('id')}/restart"
appendLog: (part) ->
@get('log').append part
@ -119,10 +125,6 @@ require 'travis/model'
@unsubscribe() if @get('state') == 'finished' && Travis.pusher
).observes('state')
isFinished: (->
@get('state') in ['passed', 'failed', 'errored', 'canceled']
).property('state')
# TODO: such formattings should be done in controller, but in order
# to use it there easily, I would have to refactor job and build
# controllers
@ -139,33 +141,4 @@ require 'travis/model'
"#{@get('repo.slug')} ##{@get('number')}"
).property()
@Travis.Job.reopenClass
queued: ->
filtered = Ember.FilteredRecordArray.create(
modelClass: Travis.Job
filterFunction: (job) ->
['created', 'queued'].indexOf(job.get('state')) != -1
filterProperties: ['state', 'queue']
)
@fetch().then (array) ->
filtered.updateFilter()
filtered.set('isLoaded', true)
filtered
running: ->
filtered = Ember.FilteredRecordArray.create(
modelClass: Travis.Job
filterFunction: (job) ->
['started', 'received'].indexOf(job.get('state')) != -1
filterProperties: ['state']
)
@fetch(state: 'started').then (array) ->
filtered.updateFilter()
filtered.set('isLoaded', true)
filtered
`export default Job`

View File

@ -1,7 +1,42 @@
require 'travis/model'
require 'travis/log_chunks'
`import Model from 'travis/models/model'`
`import Ajax from 'travis/utils/ajax'`
`import Job from 'travis/models/job'`
`import Ember from 'ember'`
`import config from 'travis/config/environment'`
@Travis.Log = Em.Object.extend
Request = Ember.Object.extend
HEADERS:
accept: 'application/json; chunked=true; version=2, text/plain; version=2'
run: ->
Ajax.ajax "/jobs/#{@id}/log?cors_hax=true", 'GET',
dataType: 'text'
headers: @HEADERS
success: (body, status, xhr) => Ember.run(this, -> @handle(body, status, xhr))
handle: (body, status, xhr) ->
if config.pro
@log.set('token', xhr.getResponseHeader('X-Log-Access-Token'))
if xhr.status == 204
$.ajax(url: @redirectTo(xhr), type: 'GET', success: @handlers.text)
else if @isJson(xhr, body)
@handlers.json(body)
else
@handlers.text(body)
redirectTo: (xhr) ->
# Firefox can't see the Location header on the xhr response due to the wrong
# status code 204. Should be some redirect code but that doesn't work with CORS.
xhr.getResponseHeader('Location')
isJson: (xhr, body) ->
# Firefox can't see the Content-Type header on the xhr response due to the wrong
# status code 204. Should be some redirect code but that doesn't work with CORS.
type = xhr.getResponseHeader('Content-Type') || ''
type.indexOf('json') > -1
Log = Ember.Object.extend
version: 0 # used to refresh log on requeue
isLoaded: false
length: 0
@ -13,7 +48,7 @@ require 'travis/log_chunks'
data['part_numbers'] = partNumbers if partNumbers
data['after'] = after if after
Travis.ajax.ajax "/jobs/#{@get('job.id')}/log", 'GET',
Ajax.ajax "/jobs/#{@get('job.id')}/log", 'GET',
dataType: 'json'
headers:
accept: 'application/json; chunked=true; version=2'
@ -25,7 +60,7 @@ require 'travis/log_chunks'
@append part
parts: (->
#if Travis.config.pusher_log_fallback
#if config.pusher_log_fallback
# Travis.LogChunks.create(content: [], missingPartsCallback: => @fetchMissingParts.apply(this, arguments))
#else
Ember.ArrayProxy.create(content: [])
@ -44,7 +79,7 @@ require 'travis/log_chunks'
@set('removed', true)
@loadParts(json['log']['parts'])
text: (text) => @loadText(text)
Travis.Log.Request.create(id: id, handlers: handlers).run() if id = @get('job.id')
Request.create(id: id, handlers: handlers, log: this).run() if id = @get('job.id')
clear: ->
@clearParts()
@ -64,34 +99,4 @@ require 'travis/log_chunks'
@append(number: 1, content: text, final: true)
@set('isLoaded', true)
Travis.Log.Request = Em.Object.extend
HEADERS:
accept: 'application/json; chunked=true; version=2, text/plain; version=2'
run: ->
Travis.ajax.ajax "/jobs/#{@id}/log?cors_hax=true", 'GET',
dataType: 'text'
headers: @HEADERS
success: (body, status, xhr) => Ember.run(this, -> @handle(body, status, xhr))
handle: (body, status, xhr) ->
if Travis.config.pro
Travis.Job.find(@get('id')).get('log').set('token', xhr.getResponseHeader('X-Log-Access-Token'))
if xhr.status == 204
$.ajax(url: @redirectTo(xhr), type: 'GET', success: @handlers.text)
else if @isJson(xhr, body)
@handlers.json(body)
else
@handlers.text(body)
redirectTo: (xhr) ->
# Firefox can't see the Location header on the xhr response due to the wrong
# status code 204. Should be some redirect code but that doesn't work with CORS.
xhr.getResponseHeader('Location')
isJson: (xhr, body) ->
# Firefox can't see the Content-Type header on the xhr response due to the wrong
# status code 204. Should be some redirect code but that doesn't work with CORS.
type = xhr.getResponseHeader('Content-Type') || ''
type.indexOf('json') > -1
`export default Log`

3
app/models/model.coffee Normal file
View File

@ -0,0 +1,3 @@
`import DS from 'ember-data';`
`export default DS.Model.extend();`

192
app/models/repo.coffee Normal file
View File

@ -0,0 +1,192 @@
`import ExpandableRecordArray from 'travis/utils/expandable-record-array'`
`import Model from 'travis/models/model'`
`import Ajax from 'travis/utils/ajax'`
# TODO: Investigate for some weird reason if I use durationFrom here not durationFromHelper,
# the function stops being visible inside computed properties.
`import { durationFrom as durationFromHelper } from 'travis/utils/helpers'`
`import Build from 'travis/models/build'`
Repo = Model.extend
slug: DS.attr()
description: DS.attr()
private: DS.attr('boolean')
lastBuildNumber: DS.attr('number')
lastBuildState: DS.attr()
lastBuildStartedAt: DS.attr()
lastBuildFinishedAt: DS.attr()
githubLanguage: DS.attr()
_lastBuildDuration: DS.attr('number')
lastBuildLanguage: DS.attr()
active: DS.attr()
lastBuildId: DS.attr('number')
lastBuildHash: (->
{
id: @get('lastBuildId')
number: @get('lastBuildNumber')
repo: this
}
).property('lastBuildId', 'lastBuildNumber')
lastBuild: (->
if id = @get('lastBuildId')
@store.find('build', id)
@store.recordForId('build', id)
).property('lastBuildId')
withLastBuild: ->
@filter( (repo) -> repo.get('lastBuildId') )
sshKey: (->
@store.find('ssh_key', @get('id'))
@store.recordForId('ssh_key', @get('id'))
)
envVars: (->
id = @get('id')
@store.filter('env_var', { repository_id: id }, (v) ->
v.get('repo.id') == id
)
).property()
builds: (->
id = @get('id')
builds = @store.filter('build', event_type: 'push', repository_id: id, (b) ->
b.get('repo.id') == id && b.get('eventType') == 'push'
)
# TODO: move to controller
array = ExpandableRecordArray.create
type: 'build'
content: Ember.A([])
array.load(builds)
array.observe(builds)
array
).property()
pullRequests: (->
id = @get('id')
builds = @store.filter('build', event_type: 'pull_request', repository_id: id, (b) ->
b.get('repo.id') == id && b.get('eventType') == 'pull_request'
)
# TODO: move to controller
array = ExpandableRecordArray.create
type: 'build'
content: Ember.A([])
array.load(builds)
id = @get('id')
array.observe(builds)
array
).property()
branches: (->
builds = @store.find 'build', repository_id: @get('id'), branches: true
builds.then ->
builds.set 'isLoaded', true
builds
).property()
owner: (->
(@get('slug') || '').split('/')[0]
).property('slug')
name: (->
(@get('slug') || '').split('/')[1]
).property('slug')
lastBuildDuration: (->
duration = @get('_lastBuildDuration')
duration = durationFromHelper(@get('lastBuildStartedAt'), @get('lastBuildFinishedAt')) unless duration
duration
).property('_lastBuildDuration', 'lastBuildStartedAt', 'lastBuildFinishedAt')
sortOrder: (->
# cuz sortAscending seems buggy when set to false
if lastBuildFinishedAt = @get('lastBuildFinishedAt')
- new Date(lastBuildFinishedAt).getTime()
else
- new Date('9999').getTime() - parseInt(@get('lastBuildId'))
).property('lastBuildFinishedAt', 'lastBuildId')
stats: (->
if @get('slug')
@get('_stats') || $.get("https://api.github.com/repos/#{@get('slug')}", (data) =>
@set('_stats', data)
@notifyPropertyChange 'stats'
) && {}
).property('slug')
updateTimes: ->
@notifyPropertyChange 'lastBuildDuration'
regenerateKey: (options) ->
Ajax.ajax '/repos/' + @get('id') + '/key', 'post', options
fetchSettings: ->
Ajax.ajax('/repos/' + @get('id') + '/settings', 'get', forceAuth: true).then (data) ->
data['settings']
saveSettings: (settings) ->
Ajax.ajax('/repos/' + @get('id') + '/settings', 'patch', data: { settings: settings })
Repo.reopenClass
recent: ->
@find()
accessibleBy: (store, login) ->
repos = store.find('repo', { member: login, orderBy: 'name' })
repos.then () ->
repos.set('isLoaded', true)
repos
search: (store, query) ->
promise = store.find('repo', search: query, orderBy: 'name')
result = Ember.ArrayProxy.create(content: [])
promise.then ->
result.pushObjects(promise.get('content').toArray())
result.set('isLoaded', true)
result
withLastBuild: (store) ->
repos = store.filter('repo', {}, (build) ->
build.get('lastBuildId')
)
repos.then () ->
repos.set('isLoaded', true)
repos
bySlug: (store, slug) ->
# first check if there is a repo with a given slug already ordered
repos = store.all('repo').filterBy('slug', slug)
if repos.get('length') > 0
repos
else
store.find('repo', { slug: slug })
fetchBySlug: (store, slug) ->
repos = @bySlug(store, slug)
if repos.get('length') > 0
repos.get('firstObject')
else
repos.then (repos) ->
error = new Error('repo not found')
error.slug = slug
Ember.get(repos, 'firstObject') || throw(error)
# buildURL: (slug) ->
# if slug then slug else 'repos'
`export default Repo`

32
app/models/request.coffee Normal file
View File

@ -0,0 +1,32 @@
`import Ember from 'ember'`
`import Model from 'travis/models/model'`
Request = Model.extend
created_at: DS.attr()
event_type: DS.attr()
result: DS.attr()
message: DS.attr()
headCommit: DS.attr()
baseCommit: DS.attr()
branchName: DS.attr()
tagName: DS.attr()
pullRequest: DS.attr('boolean')
pullRequestTitle: DS.attr()
pullRequestNumber: DS.attr('number')
repo: DS.belongsTo('repo', async: true)
commit: DS.belongsTo('commit', async: true)
build: DS.belongsTo('build', async: true)
isAccepted: (->
# For some reason some of the requests have a null result beside the fact that
# the build was created. We need to look into it, but for now we can just assume
# that if build was created, the request was accepted
@get('result') == 'accepted' || @get('build.id')
).property('result')
isPullRequest: (->
@get('event_type') == 'pull_request'
).property('event_type')
`export default Request`

View File

@ -0,0 +1,9 @@
`import Ember from 'ember'`
`import Model from 'travis/models/model'`
SshKey = Model.extend
value: DS.attr()
description: DS.attr()
fingerprint: DS.attr()
`export default SshKey`

View File

@ -1,24 +1,21 @@
require 'travis/ajax'
require 'travis/model'
`import Ember from 'ember'`
`import Model from 'travis/models/model'`
`import Ajax from 'travis/utils/ajax'`
`import config from 'travis/config/environment'`
@Travis.User = Travis.Model.extend
_name: Ember.attr('string', key: 'name')
email: Ember.attr('string')
login: Ember.attr('string')
token: Ember.attr('string')
gravatarId: Ember.attr('string')
isSyncing: Ember.attr('boolean')
syncedAt: Ember.attr('string')
repoCount: Ember.attr('number')
User = Model.extend
name: DS.attr()
email: DS.attr()
login: DS.attr()
token: DS.attr()
gravatarId: DS.attr()
isSyncing: DS.attr('boolean')
syncedAt: DS.attr()
repoCount: DS.attr('number')
# This is the only way I found to override the attribue created with Ember.attr
name: Ember.computed( (key, value) ->
if arguments.length == 1
@get('_name') || @get('login')
else
@set('_name', value)
value
).property('login', '_name')
fullName: (->
@get('name') || @get('login')
).property('name', 'login')
isSyncingDidChange: (->
Ember.run.next this, ->
@ -26,11 +23,11 @@ require 'travis/model'
).observes('isSyncing')
urlGithub: (->
"#{Travis.config.source_endpoint}/#{@get('login')}"
"#{config.sourceEndpoint}/#{@get('login')}"
).property()
_rawPermissions: (->
Travis.ajax.get('/users/permissions')
Ajax.get('/users/permissions')
).property()
permissions: (->
@ -63,12 +60,12 @@ require 'travis/model'
sync: ->
self = this
Travis.ajax.post('/users/sync', {}, ->
Ajax.post('/users/sync', {}, ->
self.setWithSession('isSyncing', true)
)
poll: ->
Travis.ajax.get '/users', (data) =>
Ajax.get '/users', (data) =>
if data.user.is_syncing
self = this
setTimeout ->
@ -79,11 +76,12 @@ require 'travis/model'
@setWithSession('syncedAt', data.user.synced_at)
Travis.trigger('user:synced', data.user)
# need to pass any param to trigger findQuery
Travis.Account.find(foo: '')
@store.findQuery('account', {})
setWithSession: (name, value) ->
@set(name, value)
user = JSON.parse(Travis.sessionStorage.getItem('travis.user'))
user[$.underscore(name)] = @get(name)
user[name.underscore()] = @get(name)
Travis.sessionStorage.setItem('travis.user', JSON.stringify(user))
`export default User`

58
app/router.coffee Normal file
View File

@ -0,0 +1,58 @@
`import Ember from 'ember'`
`import config from './config/environment'`
`import Location from 'travis/utils/location'`
Router = Ember.Router.extend
# TODO: we should use TravisLocation here
location: if Ember.testing then 'none' else 'travis'
handleURL: (url) ->
url = url.replace(/#.*?$/, '')
@_super(url)
didTransition: ->
@_super.apply @, arguments
if config.gaCode
_gaq.push ['_trackPageview', location.pathname]
Router.map ->
@resource 'dashboard', ->
@route 'repositories', path: '/'
@resource 'main', path: '/', ->
@resource 'getting_started'
@route 'recent'
@route 'repositories'
@route 'my_repositories'
@route 'search', path: '/search/:phrase'
@resource 'repo', path: '/:owner/:name', ->
@route 'index', path: '/'
@resource 'build', path: '/builds/:build_id'
@resource 'job', path: '/jobs/:job_id'
@resource 'builds', path: '/builds'
@resource 'pullRequests', path: '/pull_requests'
@resource 'branches', path: '/branches'
@resource 'requests', path: '/requests'
@resource 'caches', path: '/caches' if config.endpoints.caches
@resource 'request', path: '/requests/:request_id'
@resource 'settings', ->
@route 'index', path: '/'
@resource 'env_vars', ->
@route 'new'
@resource 'ssh_key' if config.endpoints.sshKey
@route 'first_sync'
@route 'insufficient_oauth_permissions'
@route 'auth', path: '/auth'
@resource 'profile', path: '/profile', ->
@resource 'accounts', path: '/', ->
@resource 'account', path: '/:login'
@route 'info', path: '/info'
@route 'maintenance', path: "/maintenance"
@route 'error404', path: "/*path"
`export default Router`

Some files were not shown because too many files have changed in this diff Show More