Merge branch 'master' into 142-ShowPrTitle

Conflicts:
	assets/scripts/app/templates/builds/show.hbs
This commit is contained in:
Jason Smale 2013-03-18 09:43:29 +11:00
commit 63c461e86b
137 changed files with 35989 additions and 17780 deletions

View File

@ -1,3 +1,3 @@
---
:polled_at: 1361792606
:updated_at: 1361792606
:polled_at: 1363093348
:updated_at: 1363093348

View File

@ -4,6 +4,7 @@ rvm:
before_script:
- "gem install travis-artifacts"
- "bundle exec rakep"
- "phantomjs --version"
env:
global:
@ -16,14 +17,9 @@ env:
script: "script/ci"
after_script:
- "ENV=production bundle exec rakep"
- "bundle exec rakep"
- "test $TEST_SUITE = \"ember\" && travis-artifacts upload --target-path assets/$TRAVIS_BRANCH --path public/scripts:scripts --path public/styles:styles"
matrix:
allow_failures:
- env: "TEST_SUITE=ember"
rvm: "1.9.3"
notifications:
irc: "irc.freenode.org#travis"
campfire:

View File

@ -22,7 +22,7 @@ end
output 'public/scripts'
input assets.scripts do
match '**/*.hbs' do
travis_handlebars :precompile => assets.production?
travis_handlebars :precompile => false # assets.production?
concat 'templates.js'
end
@ -64,7 +64,7 @@ input assets.scripts do
if assets.production?
match 'min/app.js' do
strip_debug
uglify squeeze: true
# uglify squeeze: true
concat 'app.js'
end
end

15
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,15 @@
# Contributing to Travis-CI
Issues for any Travis-CI should be submitted to https://github.com/travis-ci/travis-ci/issues
## Security Issues
***Any security issues should be submitted directly to [security@travis-ci.org](mailto:security@travis-ci.org)***
## Reporting Issues
- Explain what you expected to happen vs the actual results
- Include a screenshot if it helps illustrate the issue. https://github.com/blog/1347-issue-attachments
- What steps are required to reproduce the issue
- An example build that shows the issue
## Submitting a PR to Travis-Web
See testing and setup notes in the base [README](https://github.com/travis-ci/travis-web)

View File

@ -1,6 +1,5 @@
ruby '1.9.3' rescue nil
source :rubygems
source 'https://rubygems.org'
ruby '1.9.3'
gem 'puma'
gem 'rack-ssl', '~> 1.3'
@ -21,7 +20,7 @@ group :assets do
end
group :development, :test do
gem 'rake', '~> 0.9.2'
gem 'rake'
gem 'localeapp'
gem 'handlebars'
gem 'localeapp-handlebars_i18n'

View File

@ -1,60 +1,61 @@
GIT
remote: git://github.com/livingsocial/rake-pipeline.git
revision: 50b8d77b703c96539a433ee53a680c43542aaa75
revision: 65b1e744defa208e313703d89f3453447cc103b2
specs:
rake-pipeline (0.8.0)
json
rake (~> 0.9.0)
rake (~> 10.0.0)
thor
GIT
remote: git://github.com/wycats/rake-pipeline-web-filters.git
revision: 1a6dc173776b188836aa2ce2ac35b61c7f7daafe
revision: fd8d838491bd6b8de0bab72d90115b9a4f2da8a1
specs:
rake-pipeline-web-filters (0.6.0)
rack
rake-pipeline (~> 0.6)
GEM
remote: http://rubygems.org/
remote: https://rubygems.org/
specs:
POpen4 (0.1.4)
Platform (>= 0.4.0)
open4
Platform (0.4.0)
backports (2.6.5)
chunky_png (1.2.6)
coderay (1.0.8)
backports (3.0.3)
chunky_png (1.2.7)
coderay (1.0.9)
coffee-script (2.2.0)
coffee-script-source
execjs
coffee-script-source (1.4.0)
coffee-script-source (1.5.0)
commonjs (0.2.6)
compass (0.12.2)
chunky_png (~> 1.2)
fssm (>= 0.2.7)
sass (~> 3.1)
diff-lcs (1.1.3)
diff-lcs (1.2.1)
eventmachine (1.0.0)
execjs (1.4.0)
multi_json (~> 1.0)
foreman (0.60.2)
foreman (0.61.0)
thor (>= 0.13.6)
fssm (0.2.9)
gli (2.5.2)
guard (1.5.4)
listen (>= 0.4.2)
fssm (0.2.10)
gli (2.5.4)
guard (1.6.2)
listen (>= 0.6.0)
lumberjack (>= 1.0.2)
pry (>= 0.9.10)
terminal-table (>= 1.4.3)
thor (>= 0.14.6)
handlebars (0.3.1)
handlebars (0.4.0)
commonjs (~> 0.2.3)
therubyracer (~> 0.10.0)
i18n (0.6.1)
json (1.7.5)
libv8 (3.3.10.4)
listen (0.6.0)
localeapp (0.6.8)
therubyracer (~> 0.11.1)
i18n (0.6.3)
json (1.7.7)
libv8 (3.11.8.13)
listen (0.7.3)
localeapp (0.6.9)
gli
i18n
json
@ -64,46 +65,47 @@ GEM
localeapp
lumberjack (1.0.2)
method_source (0.8.1)
mime-types (1.19)
multi_json (1.5.0)
mime-types (1.21)
multi_json (1.6.1)
open4 (1.3.0)
pry (0.9.10)
pry (0.9.12)
coderay (~> 1.0.5)
method_source (~> 0.8)
slop (~> 3.3.1)
slop (~> 3.4)
puma (1.6.3)
rack (~> 1.2)
rack (1.4.1)
rack (1.5.2)
rack-cache (1.2)
rack (>= 0.4)
rack-mobile-detect (0.4.0)
rack
rack-protection (1.3.2)
rack
rack-ssl (1.3.2)
rack-ssl (1.3.3)
rack
rack-test (0.6.2)
rack (>= 1.0)
rake (0.9.6)
rake (10.0.3)
rake-pipeline-i18n-filters (0.0.5)
rake-pipeline (~> 0.6)
rb-fsevent (0.9.2)
rerun (0.7.1)
rb-fsevent (0.9.3)
ref (1.0.2)
rerun (0.8.0)
listen
rest-client (1.6.7)
mime-types (>= 1.16)
rspec (2.12.0)
rspec-core (~> 2.12.0)
rspec-expectations (~> 2.12.0)
rspec-mocks (~> 2.12.0)
rspec-core (2.12.2)
rspec-expectations (2.12.0)
diff-lcs (~> 1.1.3)
rspec-mocks (2.12.0)
sass (3.2.3)
sinatra (1.3.3)
rack (~> 1.3, >= 1.3.6)
rack-protection (~> 1.2)
rspec (2.13.0)
rspec-core (~> 2.13.0)
rspec-expectations (~> 2.13.0)
rspec-mocks (~> 2.13.0)
rspec-core (2.13.0)
rspec-expectations (2.13.0)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.13.0)
sass (3.2.6)
sinatra (1.3.5)
rack (~> 1.4)
rack-protection (~> 1.3)
tilt (~> 1.3, >= 1.3.3)
sinatra-contrib (1.3.2)
backports (>= 2.0)
@ -112,10 +114,12 @@ GEM
rack-test
sinatra (~> 1.3.0)
tilt (~> 1.3)
slop (3.3.3)
therubyracer (0.10.2)
libv8 (~> 3.3.10)
thor (0.16.0)
slop (3.4.3)
terminal-table (1.4.5)
therubyracer (0.11.4)
libv8 (~> 3.11.8.12)
ref
thor (0.17.0)
tilt (1.3.3)
uglifier (1.3.0)
execjs (>= 0.3.0)
@ -140,7 +144,7 @@ DEPENDENCIES
rack-mobile-detect
rack-protection (~> 1.3)
rack-ssl (~> 1.3)
rake (~> 0.9.2)
rake
rake-pipeline!
rake-pipeline-i18n-filters
rake-pipeline-web-filters!

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,64 +1,4 @@
require 'auth'
require 'controllers'
require 'helpers'
require 'models'
require 'pusher'
require 'routes'
require 'slider'
require 'store'
require 'tailing'
require 'templates'
require 'views'
require 'config/locales'
require 'data/sponsors'
require 'travis/instrumentation'
# $.mockjaxSettings.log = false
# Ember.LOG_BINDINGS = true
# Ember.ENV.RAISE_ON_DEPRECATION = true
# Pusher.log = -> console.log(arguments)
Travis.reopen
App: Em.Application.extend
autoinit: false
currentUserBinding: 'auth.user'
authStateBinding: 'auth.state'
init: ->
@_super.apply this, arguments
@store = Travis.Store.create()
@store.loadMany(Travis.Sponsor, Travis.SPONSORS)
@slider = new Travis.Slider()
@pusher = new Travis.Pusher(Travis.config.pusher_key)
@tailing = new Travis.Tailing()
@set('auth', Travis.Auth.create(app: this, endpoint: Travis.config.api_endpoint))
storeAfterSignInPath: (path) ->
@get('auth').storeAfterSignInPath(path)
autoSignIn: (path) ->
@get('auth').autoSignIn()
signIn: ->
@get('auth').signIn()
signOut: ->
@get('auth').signOut()
@get('router').send('afterSignOut')
receive: ->
@store.receive.apply(@store, arguments)
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

View File

@ -13,6 +13,8 @@
Travis.setLocale Travis.default_locale
@set('state', 'signed-out')
@set('user', undefined)
Travis.__container__.lookup('controller:currentUser').set('content', null)
Travis.__container__.lookup('router:main').send('afterSignOut')
signIn: ->
@set('state', 'signing-in')
@ -31,7 +33,7 @@
if user && token && @validateUser(user)
{ user: user, token: token }
else
console.log('dropping user, no token') unless token?
# console.log('dropping user, no token') if token?
storage.removeItem('travis.user')
storage.removeItem('travis.token')
null
@ -43,25 +45,30 @@
if user[field]
true
else
console.log("discarding user data, lacks #{field}")
# console.log("discarding user data, lacks #{field}")
false
setData: (data) ->
@storeData(data, Travis.sessionStorage)
@storeData(data, Travis.storage) unless @userDataFrom(Travis.storage)
@set('user', @loadUser(data.user))
user = @loadUser(data.user)
# TODO: we should not use __container__ directly, how to do it better?
# A good answer seems to do auth in context of controller.
Travis.__container__.lookup('controller:currentUser').set('content', user)
@set('state', 'signed-in')
Travis.setLocale(data.user.locale || Travis.default_locale)
Travis.trigger('user:signed_in', data.user)
@get('app.router').send('afterSignIn', @readAfterSignInPath())
Travis.__container__.lookup('router:main').send('afterSignIn', @readAfterSignInPath())
storeData: (data, storage) ->
storage.setItem('travis.token', data.token)
storage.setItem('travis.user', JSON.stringify(data.user))
loadUser: (user) ->
@app.store.load(Travis.User, user)
user = @app.store.find(Travis.User, user.id)
store = @app.store
store.load(Travis.User, user.id, user)
user = store.find(Travis.User, user.id)
user.get('permissions')
user

View File

@ -1,25 +1,40 @@
require 'helpers'
require 'travis/ticker'
Travis.reopen
Controller: Em.Controller.extend()
Travis.Controller = Em.Controller.extend()
Travis.TopController = Em.Controller.extend
needs: ['currentUser']
userBinding: 'controllers.currentUser'
TopController: Em.Controller.extend
userBinding: 'Travis.app.currentUser'
Travis.ApplicationController = Em.Controller.extend
templateName: 'layouts/home'
ApplicationController: Em.Controller.extend()
MainController: Em.Controller.extend()
StatsLayoutController: Em.Controller.extend()
ProfileLayoutController: Em.Controller.extend()
AuthLayoutController: Em.Controller.extend()
connectLayout: (name) ->
name = "layouts/#{name}"
if @get('templateName') != name
@set('templateName', name)
Travis.MainController = Em.Controller.extend()
Travis.StatsLayoutController = Em.Controller.extend()
Travis.ProfileLayoutController = Em.Controller.extend()
Travis.AuthLayoutController = Em.Controller.extend()
Travis.AccountProfileController = Em.Controller.extend
needs: ['currentUser']
userBinding: 'controllers.currentUser'
require 'controllers/accounts'
require 'controllers/build'
require 'controllers/builds'
require 'controllers/flash'
require 'controllers/home'
require 'controllers/job'
require 'controllers/profile'
require 'controllers/repos'
require 'controllers/repo'
require 'controllers/running_jobs'
require 'controllers/sidebar'
require 'controllers/stats'
require 'controllers/current_user'
require 'controllers/account_index'

View File

@ -0,0 +1,10 @@
Travis.AccountIndexController = Em.Controller.extend
needs: ['profile', 'currentUser']
hooksBinding: 'controllers.profile.hooks'
userBinding: 'controllers.currentUser'
sync: ->
@get('user').sync()
toggle: (hook) ->
hook.toggle()

View File

@ -1,8 +1,5 @@
Travis.AccountsController = Ember.ArrayController.extend
tab: 'accounts'
init: ->
@_super()
findByLogin: (login) ->
@find (account) -> account.get('login') == login

View File

@ -0,0 +1,24 @@
Travis.BuildController = Ember.Controller.extend
needs: ['repo']
repoBinding: 'controllers.repo.repo'
buildBinding: 'controllers.repo.build'
commitBinding: 'build.commit'
lineNumberBinding: 'controllers.repo.lineNumber'
currentItemBinding: 'build'
loading: (->
@get('build.isLoading')
).property('build.isLoading')
urlGithubCommit: (->
Travis.Urls.githubCommit(@get('repo.slug'), @get('commit.sha'))
).property('repo.slug', 'commit.sha')
urlAuthor: (->
Travis.Urls.email(@get('commit.authorEmail'))
).property('commit.authorEmail')
urlCommitter: (->
Travis.Urls.email(@get('commit.committerEmail'))
).property('commit.committerEmail')

View File

@ -1,5 +1,19 @@
Travis.BuildsController = Em.ArrayController.extend
# sortAscending: false
sortAscending: false
sortProperties: ['number']
repo: 'parent.repo'
contentBinding: 'parent.builds'
needs: ['repo']
repoBinding: 'controllers.repo.repo'
contentBinding: 'controllers.repo.builds'
tabBinding: 'controllers.repo.tab'
isLoadedBinding: 'content.isLoaded'
showMore: ->
id = @get('repo.id')
number = @get('lastObject.number')
@get('content').load Travis.Build.olderThanNumber(id, number, @get('tab'))
displayShowMoreButton: (->
@get('tab') != 'branches'
).property('tab')

View File

@ -0,0 +1,3 @@
Travis.CurrentUserController = Em.ObjectController.extend
sync: ->
@get('content').sync()

View File

@ -1,9 +1,12 @@
Travis.FlashController = Ember.ArrayController.extend
broadcastBinding: 'Travis.app.currentUser.broadcasts'
needs: ['currentUser']
currentUserBinding: 'controllers.currentUser'
broadcastBinding: 'currentUser.broadcasts'
init: ->
@set('flashes', Ember.A())
@_super.apply this, arguments
@set('flashes', Ember.A())
content: (->
@get('unseenBroadcasts').concat(@get('flashes'))
@ -14,8 +17,8 @@ Travis.FlashController = Ember.ArrayController.extend
).property('broadcasts.isLoaded', 'broadcasts.length')
broadcasts: (->
if Travis.app.get('currentUser') then Travis.Broadcast.find() else Ember.A()
).property('Travis.app.currentUser')
if @get('currentUser') then Travis.Broadcast.find() else Ember.A()
).property('currentUser')
loadFlashes: (msgs) ->
for msg in msgs

View File

@ -0,0 +1,21 @@
Travis.JobController = Em.Controller.extend
needs: ['repo']
jobBinding: 'controllers.repo.job'
repoBinding: 'controllers.repo.repo'
commitBinding: 'job.commit'
lineNumberBinding: 'controllers.repo.lineNumber'
currentItemBinding: 'job'
urlGithubCommit: (->
Travis.Urls.githubCommit(@get('repo.slug'), @get('commit.sha'))
).property('repo.slug', 'commit.sha')
urlAuthor: (->
Travis.Urls.email(@get('commit.authorEmail'))
).property('commit.authorEmail')
urlCommitter: (->
Travis.Urls.email(@get('commit.committerEmail'))
).property('commit.committerEmail')

View File

@ -1,21 +1,31 @@
Travis.ProfileController = Travis.Controller.extend
name: 'profile'
userBinding: 'Travis.app.currentUser'
accountsBinding: 'Travis.app.router.accountsController'
needs: ['currentUser', 'accounts']
userBinding: 'controllers.currentUser'
accountsBinding: 'controllers.accounts'
init: ->
@_super.apply this, arguments
self = this
Travis.on("user:synced", (->
self.reloadHooks()
))
account: (->
login = @get('params.login') || Travis.app.get('currentUser.login')
login = @get('params.login') || @get('user.login')
account = @get('accounts').filter((account) -> account if account.get('login') == login)[0]
account.select() if account
account
).property('accounts.length', 'params.login')
sync: ->
@get('user').sync()
toggle: (hook) ->
hook.toggle()
activate: (action, params) ->
@setParams(params || @get('params'))
this["view#{$.camelize(action)}"]()
@ -25,7 +35,7 @@ Travis.ProfileController = Travis.Controller.extend
@reloadHooks()
reloadHooks: ->
@set('hooks', Travis.Hook.find(owner_name: @get('params.login') || Travis.app.get('currentUser.login')))
@set('hooks', Travis.Hook.find(owner_name: @get('params.login') || @get('user.login')))
viewUser: ->
@connectTab('user')
@ -33,7 +43,6 @@ Travis.ProfileController = Travis.Controller.extend
connectTab: (tab) ->
viewClass = Travis["#{$.camelize(tab)}View"]
@set('tab', tab)
@connectOutlet(outletName: 'pane', controller: this, viewClass: viewClass)
setParams: (params) ->
@set('params', {})

View File

@ -1,20 +1,15 @@
Travis.RepoController = Travis.Controller.extend
bindings: []
needs: ['repos', 'currentUser']
currentUserBinding: 'controllers.currentUser'
isError: (-> @get('repo.isError') ).property('repo.isError')
slug: (-> @get('repo.slug') ).property('repo.slug')
isLoading: (-> @get('repo.isLoading') ).property('repo.isLoading')
init: ->
@_super.apply this, arguments
Ember.run.later(@updateTimes.bind(this), Travis.INTERVALS.updateTimes)
@set 'builds', Em.ArrayProxy.create(Em.SortableMixin,
isLoadedBinding: 'content.isLoaded'
sortProperties: ['number']
sortAscending: false
content: []
isLoadingBinding: 'content.isLoading'
load: (records) ->
content = @get('content')
if content && content.load
content.load(records)
)
updateTimes: ->
if builds = @get('builds')
@ -33,7 +28,7 @@ Travis.RepoController = Travis.Controller.extend
this["view#{$.camelize(action)}"]()
viewIndex: ->
@_bind('repo', 'controllers.reposController.firstObject')
@_bind('repo', 'controllers.repos.firstObject')
@_bind('build', 'repo.lastBuild')
@connectTab('current')
@ -43,15 +38,15 @@ Travis.RepoController = Travis.Controller.extend
viewBuilds: ->
@connectTab('builds')
@_bind('builds.content', 'repo.builds')
@_bind('builds', 'repo.builds')
viewPullRequests: ->
@connectTab('pull_requests')
@_bind('builds.content', 'repo.pullRequests')
@_bind('builds', 'repo.pullRequests')
viewBranches: ->
@connectTab('branches')
@_bind('builds.content', 'repo.branches')
@_bind('builds', 'repo.branches')
viewEvents: ->
@connectTab('events')
@ -64,12 +59,9 @@ Travis.RepoController = Travis.Controller.extend
@_bind('build', 'job.build')
@connectTab('job')
repoObserver: (->
repo = @get('repo')
repo.select() if repo
).observes('repo.id')
connectTab: (tab) ->
# TODO: such implementation seems weird now, because we render
# in the renderTemplate function in routes
name = if tab == 'current' then 'build' else tab
viewClass = if name in ['builds', 'branches', 'pull_requests']
Travis.BuildsView
@ -77,7 +69,6 @@ Travis.RepoController = Travis.Controller.extend
Travis["#{$.camelize(name)}View"]
@set('tab', tab)
@connectOutlet(outletName: 'pane', controller: this, viewClass: viewClass)
_bind: (to, from) ->
@bindings.push Ember.oneWay(this, to, from)
@ -85,3 +76,7 @@ Travis.RepoController = Travis.Controller.extend
_unbind: ->
binding.disconnect(this) for binding in @bindings
@bindings.clear()
urlGithub: (->
Travis.Urls.githubRepo(@get('repo.slug'))
).property('repo.slug')

View File

@ -3,10 +3,30 @@ require 'travis/limited_array'
Travis.ReposController = Ember.ArrayController.extend
defaultTab: 'recent'
isLoadedBinding: 'content.isLoaded'
needs: ['currentUser', 'repo']
currentUserBinding: 'controllers.currentUser'
selectedRepo: (->
# we need to observe also repo.content here, because we use
# ObjectProxy in repo controller
# TODO: get rid of ObjectProxy there
@get('controllers.repo.repo.content') || @get('controllers.repo.repo')
).property('controllers.repo.repo', 'controllers.repo.repo.content')
init: ->
@_super.apply this, arguments
Ember.run.later(@updateTimes.bind(this), Travis.INTERVALS.updateTimes)
recentRepos: (->
Travis.Repo.find()
Travis.LimitedArray.create
content: Em.ArrayProxy.extend(Em.SortableMixin).create(
sortProperties: ['sortOrder']
content: Travis.Repo.withLastBuild()
isLoadedBinding: 'content.isLoaded'
)
limit: 30
).property()
updateTimes: ->
if content = @get('content')
content.forEach (r) -> r.updateTimes()
@ -19,18 +39,10 @@ Travis.ReposController = Ember.ArrayController.extend
this["view#{$.camelize(tab)}"](params)
viewRecent: ->
content = Travis.LimitedArray.create
content: Em.ArrayProxy.extend(Em.SortableMixin).create(
sortProperties: ['sortOrder']
content: Travis.Repo.find()
isLoadedBinding: 'content.isLoaded'
)
limit: 30
@set('content', content)
# @set('content', Travis.Repo.find())
@set('content', @get('recentRepos'))
viewOwned: ->
@set('content', Travis.Repo.accessibleBy(Travis.app.get('currentUser.login')))
@set('content', Travis.Repo.accessibleBy(@get('currentUser.login')))
viewSearch: (params) ->
@set('content', Travis.Repo.search(params.search))

View File

@ -1,8 +1,9 @@
Travis.RunningJobsController = Em.ArrayProxy.extend
Group: Em.Object.extend
repo: (-> @get('jobs.firstObject.repo') ).property('jobs.firstObject.repo')
slug: (-> @get('jobs.firstObject.repoSlug') ).property('jobs.firstObject.repoSlug')
init: ->
@_super.apply this, arguments
@set 'jobs', []
@set 'sortedJobs', Em.ArrayProxy.extend(Em.SortableMixin,
@ -39,7 +40,9 @@ Travis.RunningJobsController = Em.ArrayProxy.extend
init: ->
@_super.apply this, arguments
@addedJobs @get('content') if @get('content')
jobs = Travis.Job.running()
@set 'content', jobs
@addedJobs jobs
contentArrayWillChange: (array, index, removedCount, addedCount) ->
@_super.apply this, arguments

View File

@ -1,15 +1,32 @@
Travis.reopen
SidebarController: Em.ArrayController.extend
init: ->
@_super.apply this, arguments
@tickables = []
Travis.Ticker.create(target: this, interval: Travis.INTERVALS.sponsors)
tick: ->
tickable.tick() for tickable in @tickables
QueuesController: Em.ArrayController.extend()
QueuesController: Em.ArrayController.extend
init: ->
@_super.apply this, arguments
queues = for queue in Travis.QUEUES
Travis.LimitedArray.create
content: Travis.Job.queued(queue.name), limit: 20
id: "queue_#{queue.name}"
name: queue.display
@set 'content', queues
showAll: (queue) ->
queue.showAll()
WorkersController: Em.ArrayController.extend
init: ->
@_super.apply this, arguments
@set 'content', Travis.Worker.find()
groups: (->
if content = @get 'arrangedContent'
groups = {}
@ -53,3 +70,22 @@ Travis.reopen
end: ->
@start() + @get('perPage')
Travis.DecksController = Travis.SponsorsController.extend
needs: ['sidebar']
perPage: 1
init: ->
@_super.apply this, arguments
@get('controllers.sidebar').tickables.push(this)
@set 'content', Travis.Sponsor.decks()
Travis.LinksController = Travis.SponsorsController.extend
needs: ['sidebar']
perPage: 6
init: ->
@_super.apply this, arguments
@get('controllers.sidebar').tickables.push(this)
@set 'content', Travis.Sponsor.links()

View File

@ -2,7 +2,7 @@ Travis.StatsController = Travis.Controller.extend
name: 'stats'
init: ->
@_super('top')
@_super.apply this, arguments
#@connectOutlet(outletName: 'main', controller: this, viewClass: Travis.StatsView)
activate: (action, params) ->

View File

@ -1,4 +1,3 @@
require 'travis/log'
require 'config/emoij'
@Travis.Helpers =
@ -40,16 +39,6 @@ require 'config/emoij'
message = message.split(/\n/)[0] if options.short
@_emojize(@_escape(message)).replace /\n/g, '<br/>'
formatLog: (log, repo, item) ->
event = if item.constructor == Travis.Build
'showBuild'
else
'showJob'
url = Travis.app.get('router').urlForEvent(event, repo, item)
Travis.Log.filter(log, url)
pathFrom: (url) ->
(url || '').split('/').pop()
@ -84,7 +73,7 @@ require 'config/emoij'
string
_nowUtc: ->
@_toUtc new Date()
@_toUtc Travis.currentDate()
_toUtc: (date) ->
Date.UTC date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()

View File

@ -1,6 +1,5 @@
require 'models/extensions'
require 'models/account'
require 'models/artifact'
require 'models/broadcast'
require 'models/branch'
require 'models/build'
@ -8,6 +7,7 @@ require 'models/commit'
require 'models/event'
require 'models/hook'
require 'models/job'
require 'models/log'
require 'models/repo'
require 'models/sponsor'
require 'models/user'

View File

@ -1,89 +0,0 @@
require 'travis/model'
@Travis.Artifact = Em.Object.extend
version: 1 # used to refresh log on requeue
body: null
isLoaded: false
init: ->
@_super.apply this, arguments
@addObserver 'job.id', @fetchBody
@fetchBody()
@set 'queue', Ember.A([])
@set 'parts', Ember.ArrayProxy.create(content: [])
@addObserver 'body', @fetchWorker
@fetchWorker()
id: (->
@get('job.id')
).property('job.id')
clear: ->
@set('body', '')
@incrementProperty('version')
fetchBody: ->
if jobId = @get('job.id')
@removeObserver 'job.id', @fetchBody
self = this
Travis.ajax.ajax "/jobs/#{jobId}/log.txt?cors_hax=true", 'GET',
dataType: 'text'
contentType: 'text/plain'
success: (data, textStatus, xhr) ->
if xhr.status == 204
logUrl = xhr.getResponseHeader('X-Log-Location')
# For some reason not all browsers can fetch this header
unless logUrl
logUrl = self.s3Url("/jobs/#{jobId}/log.txt")
$.ajax
url: logUrl
type: 'GET'
success: (data) ->
self.fetchedBody(data)
else
self.fetchedBody(data)
s3Url: (path) ->
endpoint = Travis.config.api_endpoint
staging = if endpoint.match(/-staging/) then '-staging' else ''
host = Travis.config.api_endpoint.replace(/^https?:\/\//, '').split('.').slice(-2).join('.')
"https://s3.amazonaws.com/archive#{staging}.#{host}#{path}"
fetchedBody: (body) ->
@set 'body', body
@set 'isLoaded', true
append: (body) ->
if @get('isInitialized')
@get('parts').pushObject body
@set('body', @get('body') + body)
else
@get('queue').pushObject(body)
recordDidLoad: (->
if @get('isLoaded')
if (body = @get 'body') && @get('parts.length') == 0
@get('parts').pushObject body
@set 'isInitialized', true
queue = @get('queue')
if queue.get('length') > 0
@append queue.toArray().join('')
).observes('isLoaded')
fetchWorker: ->
if !@get('workerName') && (body = @get('body'))
line = body.split("\n")[0]
if line && (match = line.match /Using worker: (.*)/)
if worker = match[1]
worker = worker.trim().split(':')[0]
@set('workerName', worker)
@removeObserver 'body', @fetchWorker

View File

@ -2,32 +2,33 @@ require 'travis/model'
@Travis.Build = Travis.Model.extend Travis.DurationCalculations,
eventType: DS.attr('string')
repoId: DS.attr('number', key: 'repository_id')
repoId: DS.attr('number')
commitId: DS.attr('number')
state: DS.attr('string')
number: DS.attr('number')
branch: DS.attr('string')
message: DS.attr('string')
_duration: DS.attr('number', key: 'duration')
startedAt: DS.attr('string', key: 'started_at')
finishedAt: DS.attr('string', key: 'finished_at')
_duration: DS.attr('number')
_config: DS.attr('object')
startedAt: DS.attr('string')
finishedAt: DS.attr('string')
repo: DS.belongsTo('Travis.Repo', key: 'repository_id')
repo: DS.belongsTo('Travis.Repo')
commit: DS.belongsTo('Travis.Commit')
jobs: DS.hasMany('Travis.Job', key: 'job_ids')
jobs: DS.hasMany('Travis.Job')
config: (->
Travis.Helpers.compact(@get('data.config'))
).property('data.config')
Travis.Helpers.compact(@get('_config'))
).property('_config')
isPullRequest: (->
@get('eventType') == 'pull_request'
).property('eventType')
isMatrix: (->
@get('data.job_ids.length') > 1
).property('data.job_ids.length')
@get('jobs.length') > 1
).property('jobs.length')
isFinished: (->
@get('state') in ['passed', 'failed', 'errored', 'canceled']
@ -73,6 +74,11 @@ require 'travis/model'
branches: (options) ->
@find repository_id: options.repoId, branches: true
olderThanNumber: (id, build_number) ->
olderThanNumber: (id, build_number, type) ->
console.log type
# TODO fix this api and use some kind of pagination scheme
@find(url: "/builds", repository_id: id, after_number: build_number)
options = { repository_id: id, after_number: build_number }
if type?
options.event_type = type.replace(/s$/, '') # poor man's singularize
@find(options)

View File

@ -13,4 +13,4 @@ require 'travis/model'
pullRequestTitle: DS.attr('string')
pullRequestNumber: DS.attr('number')
build: DS.belongsTo('Travis.Build', key: 'buildId')
build: DS.belongsTo('Travis.Build')

View File

@ -1,7 +1,7 @@
require 'travis/model'
@Travis.Job = Travis.Model.extend Travis.DurationCalculations,
repoId: DS.attr('number', key: 'repository_id')
repoId: DS.attr('number')
buildId: DS.attr('number')
commitId: DS.attr('number')
logId: DS.attr('number')
@ -11,14 +11,23 @@ require 'travis/model'
number: DS.attr('string')
startedAt: DS.attr('string')
finishedAt: DS.attr('string')
allowFailure: DS.attr('boolean', key: 'allow_failure')
allowFailure: DS.attr('boolean')
repositorySlug: DS.attr('string')
repo: DS.belongsTo('Travis.Repo', key: 'repository_id')
build: DS.belongsTo('Travis.Build', key: 'build_id')
commit: DS.belongsTo('Travis.Commit', key: 'commit_id')
repo: DS.belongsTo('Travis.Repo')
build: DS.belongsTo('Travis.Build')
commit: DS.belongsTo('Travis.Commit')
# this is a fake relationship just to get rid
# of ember data's bug: https://github.com/emberjs/data/issues/758
# TODO: remove when this issue is fixed
fakeBuild: DS.belongsTo('Travis.Build')
_config: DS.attr('object')
log: ( ->
Travis.Artifact.create(job: this)
@set('isLogAccessed', true)
Travis.Log.create(job: this)
).property()
repoSlug: (->
@ -30,15 +39,17 @@ require 'travis/model'
).property('repoSlug', 'repoId')
config: (->
Travis.Helpers.compact(@get('data.config'))
).property('data.config')
Travis.Helpers.compact(@get('_config'))
).property('_config')
isFinished: (->
@get('state') in ['passed', 'failed', 'errored', 'canceled']
).property('state')
clearLog: ->
@get('log').clear() if @get('log.isLoaded')
# This is needed if we don't want to fetch log just to clear it
if @get('isLogAccessed')
@get('log').clear()
sponsor: (->
worker = @get('log.workerName')
@ -70,17 +81,22 @@ require 'travis/model'
requeue: ->
Travis.ajax.post '/requests', job_id: @get('id')
appendLog: (text) ->
if log = @get('log')
log.append(text)
appendLog: (part) ->
@get('log').append part
subscribe: ->
if id = @get('id')
Travis.app.pusher.subscribe "job-#{id}"
return if @get('subscribed')
@set('subscribed', true)
Travis.pusher.subscribe "job-#{@get('id')}"
unsubscribe: ->
return unless @get('subscribed')
@set('subscribed', false)
Travis.pusher.unsubscribe "job-#{@get('id')}"
onStateChange: (->
if @get('state') == 'finished' && Travis.app
Travis.app.pusher.unsubscribe "job-#{@get('id')}"
if @get('state') == 'finished' && Travis.pusher
Travis.pusher.unsubscribe "job-#{@get('id')}"
).observes('state')
isAttributeLoaded: (key) ->
@ -98,16 +114,16 @@ require 'travis/model'
@Travis.Job.reopenClass
queued: (queue) ->
@find()
Travis.app.store.filter this, (job) ->
Travis.store.filter this, (job) ->
queued = ['created', 'queued'].indexOf(job.get('state')) != -1
# TODO: why queue is sometimes just common instead of build.common?
queued && (!queue || job.get('queue') == "builds.#{queue}" || job.get('queue') == queue)
running: ->
@find(state: 'started')
Travis.app.store.filter this, (job) ->
Travis.store.filter this, (job) ->
job.get('state') == 'started'
findMany: (ids) ->
Travis.app.store.findMany this, ids
Travis.store.findMany this, ids

View File

@ -0,0 +1,75 @@
require 'travis/model'
require 'travis/chunk_buffer'
@Travis.Log = Em.Object.extend
version: 0 # used to refresh log on requeue
isLoaded: false
length: 0
init: ->
@setParts()
@fetch()
setParts: ->
#@set 'parts', Ember.ArrayProxy.create(content: [])
@set 'parts', Travis.ChunkBuffer.create(content: [])
fetch: ->
console.log 'log model: fetching log' if Log.DEBUG
handlers =
json: (json) => @loadParts(json['log']['parts'])
text: (text) => @loadText(text)
Travis.Log.Request.create(id: id, handlers: handlers).run() if id = @get('job.id')
clear: ->
@setParts()
@incrementProperty('version')
append: (part) ->
@get('parts').pushObject(part)
loadParts: (parts) ->
console.log 'log model: load parts' if Log.DEBUG
@append(part) for part in parts
@set('isLoaded', true)
loadText: (text) ->
console.log 'log model: load text' if Log.DEBUG
number = -1
@append(number: 1, content: text)
@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) => @handle(body, status, xhr)
handle: (body, status, xhr) ->
if xhr.status == 204
$.ajax(url: @redirectTo(xhr), type: 'GET', success: @handlers.text)
else if @isJson(xhr, body)
@handlers.json(JSON.parse(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') || @s3Url()
s3Url: ->
endpoint = Travis.config.api_endpoint
staging = if endpoint.match(/-staging/) then '-staging' else ''
host = endpoint.replace(/^https?:\/\//, '').split('.').slice(-2).join('.')
"https://s3.amazonaws.com/archive#{staging}.#{host}#{path}/jobs/#{@id}/log.txt"
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 || body.slice(0, 8) == '{"log":{'

View File

@ -9,6 +9,7 @@ require 'travis/model'
lastBuildState: DS.attr('string')
lastBuildStartedAt: DS.attr('string')
lastBuildFinishedAt: DS.attr('string')
_lastBuildDuration: DS.attr('number')
lastBuild: DS.belongsTo('Travis.Build')
@ -21,17 +22,14 @@ require 'travis/model'
).property('lastBuildId', 'lastBuildNumber')
allBuilds: (->
allBuilds = DS.RecordArray.create
type: Travis.Build
content: Ember.A([])
store: @get('store')
@get('store').registerRecordArray(allBuilds, Travis.Build);
allBuilds
Travis.Build.find()
).property()
builds: (->
id = @get('id')
builds = Travis.Build.byRepoId id, event_type: 'push'
# TODO: move to controller
array = Travis.ExpandableRecordArray.create
type: Travis.Build
content: Ember.A([])
@ -40,7 +38,7 @@ require 'travis/model'
array.load(builds)
id = @get('id')
array.observe(@get('allBuilds'), (build) -> build.get('repo.id') == id && !build.get('isPullRequest') )
array.observe(@get('allBuilds'), (build) -> build.get('isLoaded') && build.get('eventType') && build.get('repo.id') == id && !build.get('isPullRequest') )
array
).property()
@ -56,7 +54,7 @@ require 'travis/model'
array.load(builds)
id = @get('id')
array.observe(@get('allBuilds'), (build) -> @get('repositoryId') == id && build.get('isPullRequest') )
array.observe(@get('allBuilds'), (build) -> build.get('isLoaded') && build.get('eventType') && build.get('repo.id') == id && build.get('isPullRequest') )
array
).property()
@ -78,10 +76,10 @@ require 'travis/model'
).property('slug')
lastBuildDuration: (->
duration = @get('data.last_build_duration')
duration = @get('_lastBuildDuration')
duration = Travis.Helpers.durationFrom(@get('lastBuildStartedAt'), @get('lastBuildFinishedAt')) unless duration
duration
).property('data.last_build_duration', 'lastBuildStartedAt', 'lastBuildFinishedAt')
).property('_lastBuildDuration', 'lastBuildStartedAt', 'lastBuildFinishedAt')
sortOrder: (->
# cuz sortAscending seems buggy when set to false
@ -118,6 +116,9 @@ require 'travis/model'
search: (query) ->
@find(search: query, orderBy: 'name')
withLastBuild: ->
@filter( (repo) -> repo.get('lastBuildId') )
bySlug: (slug) ->
repo = $.select(@find().toArray(), (repo) -> repo.get('slug') == slug)
if repo.length > 0 then repo else @find(slug: slug)

View File

@ -4,10 +4,11 @@ require 'travis/model'
type: DS.attr('string')
url: DS.attr('string')
link: DS.attr('string')
_image: DS.attr('string')
image: (->
"/images/sponsors/#{@get('data.image')}"
).property('data.image')
"/images/sponsors/#{@get('_image')}"
).property('_image')
Travis.Sponsor.reopenClass
decks: ->

View File

@ -2,7 +2,7 @@ require 'travis/ajax'
require 'travis/model'
@Travis.User = Travis.Model.extend
_name: DS.attr('string', key: 'name')
_name: DS.attr('string')
email: DS.attr('string')
login: DS.attr('string')
token: DS.attr('string')
@ -22,9 +22,11 @@ require 'travis/model'
).property('login', '_name')
init: ->
@poll() if @get('isSyncing')
@_super()
# TODO: the next line fails, check this
#@poll() if @get('isSyncing')
Ember.run.next this, ->
transaction = @get('store').transaction()
transaction.add this

View File

@ -4,10 +4,7 @@ require 'travis/model'
state: DS.attr('string')
name: DS.attr('string')
host: DS.attr('string')
payload: (->
@get('data.payload')
).property('data.payload')
payload: DS.attr('object')
number: (->
@get('name').match(/\d+$/)[0]

View File

@ -24,10 +24,12 @@ $.extend Travis.Pusher.prototype,
@pusher.subscribeAll()
subscribe: (channel) ->
console.log("subscribing to #{channel}")
channel = @prefix(channel)
@pusher.subscribe(channel).bind_all((event, data) => @receive(event, data)) unless @pusher?.channel(channel)
unsubscribe: (channel) ->
console.log("unsubscribing from #{channel}")
channel = @prefix(channel)
@pusher.unsubscribe(channel) if @pusher?.channel(channel)
@ -45,7 +47,7 @@ $.extend Travis.Pusher.prototype,
Travis.Job.find(data.job.id).clearLog()
Ember.run.next ->
Travis.app.store.receive(event, data)
Travis.store.receive(event, data)
normalize: (event, data) ->
switch event

View File

@ -1,374 +1,287 @@
require 'travis/location'
require 'travis/line_number_parser'
Ember.Router.reopen
location: (if testMode? then Ember.NoneLocation.create() else Travis.Location.create())
handleURL: (url) ->
url = url.replace(/#.*?$/, '')
try
@_super(url)
catch error
@_super('/not-found')
# TODO: don't reopen Ember.Route to add events, there should be
# a better way (like "parent" resource for everything inside map)
Ember.Route.reopen
enter: (router) ->
@_super(router)
_gaq.push(['_trackPageview', @absoluteRoute(router)]) if @get('isLeafRoute') && _gaq?
events:
afterSignIn: (path) ->
@routeTo(path)
defaultRoute = Ember.Route.extend
route: '/'
index: 1000
afterSignOut: ->
@routeTo('/')
lineNumberRoute = Ember.Route.extend
route: '#L:number'
index: 1
connectOutlets: (router) ->
router.saveLineNumberHash()
dynamicSegmentPattern: "([0-9]+)"
Travis.Router = Ember.Router.extend
location: 'travis'
# enableLogging: true
enableLogging: false
initialState: 'loading'
showRoot: Ember.Route.transitionTo('root.home.show')
showStats: Ember.Route.transitionTo('root.stats')
showRepo: Ember.Route.transitionTo('root.home.repo.show')
showBuilds: Ember.Route.transitionTo('root.home.repo.builds.index')
showBuild: Ember.Route.transitionTo('root.home.repo.builds.show')
showPullRequests: Ember.Route.transitionTo('root.home.repo.pullRequests')
showBranches: Ember.Route.transitionTo('root.home.repo.branches')
showEvents: Ember.Route.transitionTo('root.home.repo.events')
showJob: Ember.Route.transitionTo('root.home.repo.job')
showProfile: Ember.Route.transitionTo('root.profile')
showAccount: Ember.Route.transitionTo('root.profile.account')
showUserProfile: Ember.Route.transitionTo('root.profile.account.profile')
saveLineNumberHash: (path) ->
Ember.run.next this, ->
path = path || @get('location').getURL()
if match = path.match(/#L\d+$/)
@set 'repoController.lineNumberHash', match[0]
reload: ->
console.log 'Triggering reload'
url = @get('location').getURL()
@transitionTo('loading')
# Without ember next @route sometimes hit the place where HistoryLocation
# does not have any state set up yet, so it's best to defer it a little bit.
Ember.run.next this, ->
@route(url)
routeTo: (path) ->
return unless path
@router.handleURL(path)
@router.location.setURL(path)
signedIn: ->
!!Travis.app.get('auth.user')
@controllerFor('currentUser').get('content')
needsAuth: (path) ->
path.indexOf('/profile') == 0
afterSignOut: ->
@authorize('/')
loading: Ember.Route.extend
routePath: (router, path) ->
router.saveLineNumberHash(path)
router.authorize(path)
Travis.app.autoSignIn() unless router.signedIn()
redirect: ->
if @get('needsAuth')
@authorize(@router.location.getURL())
else
@_super.apply this, arguments
Travis.autoSignIn() unless @signedIn()
authorize: (path) ->
if !@signedIn() && @needsAuth(path)
Travis.app.storeAfterSignInPath(path)
@transitionTo('root.auth')
if !@signedIn()
Travis.storeAfterSignInPath(path)
@transitionTo('auth')
Travis.Router.reopen
transitionTo: ->
this.container.lookup('controller:repo').set('lineNumber', null)
@_super.apply this, arguments
Travis.Router.map ->
@resource 'index', path: '/', ->
@route 'current', path: '/'
@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'
@route 'stats', path: '/stats'
@route 'auth', path: '/auth'
@route 'notFound', path: '/not-found'
@resource 'profile', path: '/profile', ->
@route 'index', path: '/'
@resource 'account', path: '/:login', ->
@route 'index', path: '/'
@route 'profile', path: '/profile'
Travis.ApplicationRoute = Ember.Route.extend Travis.LineNumberParser,
setupController: ->
@_super.apply this, arguments
this.controllerFor('repo').set('lineNumber', @fetchLineNumber())
Travis.IndexCurrentRoute = Ember.Route.extend
renderTemplate: ->
@render 'repo'
@render 'build', outlet: 'pane', into: 'repo'
setupController: ->
@container.lookup('controller:repo').activate('index')
Travis.AbstractBuildsRoute = Ember.Route.extend
renderTemplate: ->
@render 'builds', outlet: 'pane', into: 'repo'
setupController: ->
@container.lookup('controller:repo').activate(@get('contentType'))
Travis.BuildsRoute = Travis.AbstractBuildsRoute.extend(contentType: 'builds')
Travis.PullRequestsRoute = Travis.AbstractBuildsRoute.extend(contentType: 'pull_requests')
Travis.BranchesRoute = Travis.AbstractBuildsRoute.extend(contentType: 'branches')
Travis.BuildRoute = Ember.Route.extend
renderTemplate: ->
@render 'build', outlet: 'pane', into: 'repo'
serialize: (model, params) ->
id = if model.get then model.get('id') else model
{ build_id: id }
setupController: (controller, model) ->
model = Travis.Build.find(model) if model && !model.get
repo = @container.lookup('controller:repo')
repo.set('build', model)
repo.activate('build')
Travis.JobRoute = Ember.Route.extend
renderTemplate: ->
@render 'job', outlet: 'pane', into: 'repo'
serialize: (model, params) ->
id = if model.get then model.get('id') else model
{ job_id: id }
setupController: (controller, model) ->
model = Travis.Job.find(model) if model && !model.get
repo = @container.lookup('controller:repo')
repo.set('job', model)
repo.activate('job')
Travis.RepoIndexRoute = Ember.Route.extend
setupController: (controller, model) ->
@container.lookup('controller:repo').activate('current')
renderTemplate: ->
@render 'build', outlet: 'pane', into: 'repo'
Travis.RepoRoute = Ember.Route.extend
renderTemplate: ->
@render 'repo'
setupController: (controller, model) ->
# TODO: if repo is just a data hash with id and slug load it
# as incomplete record
model = Travis.Repo.find(model.id) if model && !model.get
controller.set('repo', model)
serialize: (repo) ->
slug = if repo.get then repo.get('slug') else repo.slug
[owner, name] = slug.split('/')
{ owner: owner, name: name }
deserialize: (params) ->
slug = "#{params.owner}/#{params.name}"
content = Ember.Object.create slug: slug, isLoaded: false, isLoading: true
proxy = Ember.ObjectProxy.create(content: content)
repos = Travis.Repo.bySlug(slug)
observer = ->
if repos.get 'isLoaded'
repos.removeObserver 'isLoaded', observer
proxy.set 'isLoading', false
if repos.get('length') == 0
# isError is also used in DS.Model, but maybe we should use something
# more focused like notFound later
proxy.set 'isError', true
else
proxy.set 'content', repos.objectAt(0)
if repos.length
proxy.set('content', repos[0])
else
@transitionTo('root')
@route(path)
repos.addObserver 'isLoaded', observer
root: Ember.Route.extend
route: '/'
loading: Ember.State.extend()
afterSignIn: (-> )
proxy
auth: Ember.Route.extend
route: '/auth'
customRegexp: /^\/?auth($|\/)/
connectOutlets: (router) ->
router.get('applicationView').connectLayout 'simple'
$('body').attr('id', 'auth')
router.get('applicationController').connectOutlet('top', 'top')
router.get('applicationController').connectOutlet('main', 'signin')
Travis.IndexRoute = Ember.Route.extend
renderTemplate: ->
$('body').attr('id', 'home')
afterSignIn: (router, path) ->
router.route(path || '/')
@render 'repos', outlet: 'left'
@render 'sidebar', outlet: 'right'
@render 'top', outlet: 'top'
@render 'flash', outlet: 'flash'
stats: Ember.Route.extend
route: '/stats'
customRegexp: /^\/?stats($|\/)/
connectOutlets: (router) ->
router.get('applicationView').connectLayout 'simple'
$('body').attr('id', 'stats')
router.get('applicationController').connectOutlet 'top', 'top'
router.get('applicationController').connectOutlet 'main', 'stats'
setupController: (controller)->
@container.lookup('controller:repos').activate()
@container.lookup('controller:application').connectLayout 'home'
profile: Ember.Route.extend
initialState: 'index'
route: '/profile'
Travis.StatsRoute = Ember.Route.extend
renderTemplate: ->
$('body').attr('id', 'stats')
connectOutlets: (router) ->
router.get('applicationView').connectLayout 'profile'
$('body').attr('id', 'profile')
router.get('accountsController').set('content', Travis.Account.find())
router.get('applicationController').connectOutlet 'top', 'top'
router.get('applicationController').connectOutlet 'left', 'accounts'
router.get('applicationController').connectOutlet 'flash', 'flash'
@render 'top', outlet: 'top'
@render 'stats'
index: Ember.Route.extend
route: '/'
connectOutlets: (router) ->
router.get('applicationController').connectOutlet 'main', 'profile'
router.get('profileController').activate 'hooks'
setupController: ->
@container.lookup('controller:application').connectLayout('simple')
account: Ember.Route.extend
initialState: 'index'
route: '/:login'
Travis.NotFoundRoute = Ember.Route.extend
renderTemplate: ->
$('body').attr('id', 'not-found')
connectOutlets: (router, account) ->
if account
params = { login: account.get('login') }
router.get('profileController').setParams(params)
else
router.send 'showProfile'
@render 'top', outlet: 'top'
@render 'not_found'
deserialize: (router, params) ->
controller = router.get('accountsController')
setupController: ->
@container.lookup('controller:application').connectLayout('simple')
unless controller.get 'content'
controller.set('content', Travis.Account.find())
Travis.ProfileRoute = Ember.Route.extend
needsAuth: true
account = controller.findByLogin(params.login)
setupController: ->
@container.lookup('controller:application').connectLayout('profile')
@container.lookup('controller:accounts').set('content', Travis.Account.find())
if account
account
else
deferred = $.Deferred()
renderTemplate: ->
$('body').attr('id', 'profile')
observer = ->
if account = controller.findByLogin(params.login)
controller.removeObserver 'content.length', observer
deferred.resolve account
controller.addObserver 'content.length', observer
@render 'top', outlet: 'top'
@render 'accounts', outlet: 'left'
@render 'flash', outlet: 'flash'
@render 'profile'
deferred.promise()
Travis.ProfileIndexRoute = Ember.Route.extend
setupController: ->
@container.lookup('controller:profile').activate 'hooks'
serialize: (router, account) ->
if account
{ login: account.get('login') }
else
{}
renderTemplate: ->
@render 'hooks', outlet: 'pane', into: 'profile', controller: 'profile'
index: Ember.Route.extend
route: '/'
connectOutlets: (router) ->
router.get('profileController').activate 'hooks'
Travis.AccountRoute = Ember.Route.extend
setupController: (controller, account) ->
profileController = @container.lookup('controller:profile')
profileController.activate 'hooks'
profile: Ember.Route.extend
route: '/profile'
if account
params = { login: account.get('login') }
profileController.setParams(params)
connectOutlets: (router) ->
router.get('profileController').activate 'user'
deserialize: (params) ->
controller = @container.lookup('controller:accounts')
account = controller.findByLogin(params.login)
home: Ember.Route.extend
route: '/'
connectOutlets: (router) ->
router.get('applicationView').connectLayout 'home'
$('body').attr('id', 'home')
router.get('applicationController').connectOutlet 'left', 'repos'
router.get('applicationController').connectOutlet 'right', 'sidebar'
router.get('applicationController').connectOutlet 'top', 'top'
router.get('applicationController').connectOutlet 'main', 'repo'
router.get('applicationController').connectOutlet 'flash', 'flash'
router.get('reposController').activate()
router.get('repoController').set('repos', router.get('reposController'))
if account
account
else
content = Ember.Object.create(login: params.login)
proxy = Ember.ObjectProxy.create(content: content)
show: Ember.Route.extend
route: '/'
connectOutlets: (router) ->
router.get('repoController').activate('index')
observer = ->
if account = controller.findByLogin(params.login)
controller.removeObserver 'content.length', observer
proxy.set('content', account)
controller.addObserver 'content.length', observer
initialState: 'default'
default: defaultRoute
lineNumber: lineNumberRoute
proxy
showWithLineNumber: Ember.Route.extend
route: '/#/L:number'
connectOutlets: (router) ->
router.get('repoController').activate('index')
serialize: (account) ->
if account
{ login: account.get('login') }
else
{}
repo: Ember.Route.extend
route: '/:owner/:name'
dynamicSegmentPattern: "([^/#]+)"
Travis.AccountIndexRoute = Ember.Route.extend
setupController: ->
@container.lookup('controller:profile').activate 'hooks'
connectOutlets: (router, repo) ->
if repo && repo.constructor != Travis.Repo
repo = Travis.Repo.find(repo.id)
router.get('repoController').set 'repo', repo
renderTemplate: ->
@render 'hooks', outlet: 'pane', into: 'profile'
deserialize: (router, params) ->
slug = "#{params.owner}/#{params.name}"
repos = Travis.Repo.bySlug(slug)
deferred = $.Deferred()
Travis.AccountProfileRoute = Ember.Route.extend
setupController: ->
@container.lookup('controller:profile').activate 'user'
observer = ->
if repos.get 'isLoaded'
repos.removeObserver 'isLoaded', observer
deferred.resolve repos.objectAt(0)
renderTemplate: ->
@render 'user', outlet: 'pane', into: 'profile'
if repos.length
deferred.resolve repos[0]
else
repos.addObserver 'isLoaded', observer
Travis.AuthRoute = Ember.Route.extend
renderTemplate: ->
$('body').attr('id', 'auth')
deferred.promise()
@render 'top', outlet: 'top'
@render 'auth.signin'
serialize: (router, repo) ->
if typeof repo == 'string'
[owner, name] = repo.split '/'
{ owner: owner, name: name }
else if repo && repo.constructor == Travis.Repo
{ owner: repo.get('owner'), name: repo.get('name') }
else if repo && repo.id && repo.slug
[owner, name] = repo.slug.split '/'
{ owner: owner, name: name }
else
# TODO: it would be nice to handle 404 somehow
{}
show: Ember.Route.extend
route: '/'
connectOutlets: (router) ->
router.get('repoController').activate('current')
initialState: 'default'
default: defaultRoute
lineNumber: lineNumberRoute
builds: Ember.Route.extend
route: '/builds'
index: Ember.Route.extend
route: '/'
connectOutlets: (router, repo) ->
router.get('repoController').activate 'builds'
show: Ember.Route.extend
route: '/:build_id'
connectOutlets: (router, build) ->
unless build.get
# TODO: apparently when I use id in url, it will pass it
# here, why doesn't it use deserialize?
build = Travis.Build.find(build)
router.get('repoController').set 'build', build
router.get('repoController').activate 'build'
serialize: (router, build) ->
if build.get
{ build_id: build.get('id') }
else
{ build_id: build }
deserialize: (router, params) ->
# Something is wrong here. If I don't use deferred, id is not
# initialized and url ends up being /jobs/null
# This should not be needed, as id should be immediately set on the
# record.
# TODO: find out why it happens
build = Travis.Build.find params.build_id
if build.get 'id'
build
else
deferred = $.Deferred()
observer = ->
if build.get 'id'
build.removeObserver 'id', observer
deferred.resolve build
build.addObserver 'id', observer
deferred.promise()
# TODO: this is not dry, but for some weird
# reason Mixins don't play nice with Ember.Route
initialState: 'default'
default: defaultRoute
lineNumber: lineNumberRoute
dynamicSegmentPattern: "([^/#]+)"
logRedirect: Ember.Route.extend
route: '/log.txt'
connectOutlets: (router) ->
build = router.get('repoController').get 'build'
observer = ->
if logId = build.get('jobs.firstObject.log.id')
window.location = Travis.Urls.plainTextLog(logId)
build.removeObserver('jobs.firstObject.log.id', observer)
build.addObserver('jobs.firstObject.log.id', observer)
pullRequests: Ember.Route.extend
route: '/pull_requests'
connectOutlets: (router, repo) ->
router.get('repoController').activate 'pull_requests'
branches: Ember.Route.extend
route: '/branches'
connectOutlets: (router, repo) ->
router.get('repoController').activate 'branches'
events: Ember.Route.extend
route: '/events'
connectOutlets: (router, repo) ->
router.get('repoController').activate 'events'
job: Ember.Route.extend
route: '/jobs/:job_id'
dynamicSegmentPattern: "([^/#]+)"
connectOutlets: (router, job) ->
unless job.get
# In case I use id
job = Travis.Job.find(job)
router.get('repoController').set 'job', job
router.get('repoController').activate 'job'
serialize: (router, job) ->
if job.get
{ job_id: job.get('id') }
else
{ job_id: job }
deserialize: (router, params) ->
job = Travis.Job.find params.job_id
if job.get 'id'
job
else
deferred = $.Deferred()
observer = ->
if job.get 'id'
job.removeObserver 'id', observer
deferred.resolve job
job.addObserver 'id', observer
deferred.promise()
initialState: 'default'
default: defaultRoute
lineNumber: lineNumberRoute
logRedirect: Ember.Route.extend
route: '/log.txt'
connectOutlets: (router, job) ->
job = router.get('repoController').get 'job'
observer = ->
if logId = job.get('log.id')
window.location = Travis.Urls.plainTextLog(logId)
job.removeObserver('log.id', observer)
job.addObserver('log.id', observer)
setupController: ->
@container.lookup('controller:application').connectLayout('simple')

View File

@ -1,30 +1,23 @@
require 'store/rest_adapter'
DATA_PROXY =
get: (name) ->
@savedData[name]
coerceId = (id) -> if id == null then null else id+''
Travis.Store = DS.Store.extend
revision: 4
revision: 12
adapter: Travis.RestAdapter.create()
init: ->
@_super.apply this, arguments
@_loadedData = {}
@clientIdToComplete = {}
load: (type, id, hash) ->
load: (type, data, prematerialized) ->
result = @_super.apply this, arguments
if result && result.clientId
if result && result.clientId && @clientIdToComplete[result.clientId] == undefined
# I assume that everything that goes through load is complete record
# representation, incomplete hashes from pusher go through merge()
record = @findByClientId type, result.clientId
record.set 'incomplete', false
record.set 'complete', true
# setting both incomplete and complete may be weird, but it's easier to
# work with both values. I need to check if record has already been completed
# and in order to do that, without having 'complete', I would need to check
# for incomplete == false, which looks worse
@clientIdToComplete[result.clientId] = true
result
@ -34,38 +27,26 @@ Travis.Store = DS.Store.extend
array.set('isLoaded', true) for array in @typeMapFor(type).recordArrays
result
merge: (type, id, hash) ->
if hash == undefined
hash = id
primaryKey = type.proto().primaryKey
Ember.assert("A data hash was loaded for a record of type " + type.toString() + " but no primary key '" + primaryKey + "' was provided.", hash[primaryKey])
id = hash[primaryKey]
merge: (type, data, incomplete) ->
id = coerceId data.id
typeMap = @typeMapFor(type)
dataCache = typeMap.cidToHash
clientId = typeMap.idToCid[id]
recordCache = @get('recordCache')
if clientId != undefined
if (data = dataCache[clientId]) && (typeof data == 'object')
for key, value of hash
if ( descriptor = Object.getOwnPropertyDescriptor(data, key) ) && descriptor.set
Ember.set(data, key, value)
else
data[key] = value
else
dataCache[clientId] = hash
if record = recordCache[clientId]
record.send('didChangeData')
typeMap = @typeMapFor(type)
clientId = typeMap.idToCid[id]
record = @recordCache[clientId]
if record
@get('adapter').merge(this, record, data)
else
clientId = @pushHash(hash, id, type)
if (savedData = @clientIdToData[clientId]) && savedData.id?
$.extend(savedData, data)
else
result = @load(type, data, {id: data.id})
if clientId
DATA_PROXY.savedData = hash
@updateRecordArrays(type, clientId, DATA_PROXY)
if result && result.clientId
clientId = result.clientId
if incomplete
@clientIdToComplete[result.clientId] = false
{ id: id, clientId: clientId }
{ clientId: clientId, id: id }
isInStore: (type, id) ->
!!@typeMapFor(type).idToCid[id]
@ -76,6 +57,7 @@ Travis.Store = DS.Store.extend
mappings = @adapter.get('mappings')
type = mappings[name]
if event == 'build:started' && data.build.commit
# TODO: commit should be a sideload record on build, not mixed with it
build = data.build
@ -96,8 +78,10 @@ Travis.Store = DS.Store.extend
if event == 'job:log'
if job = @find(Travis.Job, data['job']['id'])
job.appendLog(data['job']['_log'])
console.log 'store: received job:log event', data if Log.DEBUG
data = data.job
job = @find(Travis.Job, data.id)
job.appendLog(number: parseInt(data.number), content: data._log)
else if data[type.singularName()]
@_loadOne(this, type, data)
else if data[type.pluralName()]
@ -113,14 +97,29 @@ Travis.Store = DS.Store.extend
if type == Travis.Build && (json.repository || json.repo)
@loadIncomplete(Travis.Repo, json.repository || json.repo)
@loadIncomplete(type, json[root])
result = @loadIncomplete(type, json[root])
if result.id
@find(type, result.id)
addLoadedData: (type, clientId, hash) ->
id = hash.id
@_loadedData[type.toString()] ||= {}
loadedData = (@_loadedData[type][clientId] ||= [])
for key of hash
loadedData.pushObject key unless loadedData.contains(key)
serializer = @get('adapter.serializer')
Ember.get(type, 'attributes').forEach( (name, meta) ->
value = @extractAttribute(type, hash, name)
if value != undefined
loadedData.pushObject name unless loadedData.contains(name)
, serializer)
Ember.get(type, 'relationshipsByName').forEach( (name, relationship) ->
key = @_keyForBelongsTo(type, relationship.key)
value = @extractBelongsTo(type, hash, key)
if value != undefined
loadedData.pushObject name unless loadedData.contains(name)
, serializer)
isDataLoadedFor: (type, clientId, key) ->
if recordsData = @_loadedData[type.toString()]
@ -130,39 +129,48 @@ Travis.Store = DS.Store.extend
loadIncomplete: (type, hash, options) ->
options ?= {}
id = hash.id
id = coerceId hash.id
typeMap = @typeMapFor(type)
dataCache = typeMap.cidToHash
cidToData = @clientIdToData
clientId = typeMap.idToCid[id]
if dataCache[clientId] && options.skipIfExists
if clientId && cidToData[clientId] && options.skipIfExists
return
result = @merge(type, hash)
result = @merge(type, hash, true)
if result && result.clientId
@addLoadedData(type, result.clientId, hash)
record = @findByClientId(type, result.clientId)
unless record.get('complete')
record.loadedAsIncomplete()
#@_updateRelationships(type, hash)
@_updateAssociations(type, type.singularName(), hash)
result
record
materializeRecord: (type, clientId, id) ->
record = @_super.apply this, arguments
if @clientIdToComplete[clientId] != undefined && !@clientIdToComplete[clientId]
record.set 'incomplete', true
else
record.set 'incomplete', false
record
_loadMany: (store, type, json) ->
root = type.pluralName()
@adapter.sideload(store, type, json, root)
@loadMany(type, json[root])
_updateAssociations: (type, name, data) ->
Em.get(type, 'associationsByName').forEach (key, meta) =>
_updateRelationships: (type, data) ->
Em.get(type, 'relationshipsByName').forEach (key, meta) =>
if meta.kind == 'belongsTo'
id = data["#{key}_id"]
if clientId = @typeMapFor(meta.type).idToCid[id]
if parent = this.findByClientId(meta.type, clientId, id)
dataProxy = parent.get('data')
if ids = dataProxy.get("#{name}_ids")
ids.pushObject(data.id) unless data.id in ids
parent.send('didChangeData');
if ids = dataProxy['hasMany'][type.pluralName()]
unless data.id in ids
state = parent.get('stateManager.currentState.path')
unless state == "rootState.loaded.materializing"
parent.send('materializingData')
ids.pushObject(data.id)
parent.notifyPropertyChange('data')

View File

@ -1,7 +1,67 @@
require 'travis/ajax'
require 'models'
@Travis.RestAdapter = DS.RESTAdapter.extend
DS.JSONTransforms['object'] = {
deserialize: (serialized) -> serialized
serialize: (deserialized) -> deserialized
}
Travis.Serializer = DS.RESTSerializer.extend
# The next 3 methods specify the behavior of adding records to dirty sets
# (ie. which records will be treated as dirty on the next commit). We don't
# allow to change most of the records on the client, so for anything except
# the User, we ignore dirtyiness.
dirtyRecordsForAttributeChange: (dirtySet, record) ->
if record.constructor == Travis.User
@_super.apply this, arguments
dirtyRecordsForBelongsToChange: (dirtySet, record) ->
if record.constructor == Travis.User
@_super.apply this, arguments
dirtyRecordsForHasManyChange: (dirtySet, record) ->
if record.constructor == Travis.User
@_super.apply this, arguments
merge: (record, serialized) ->
data = record.get('data')
# TODO: write test that ensures that we go to materializingData
# only if we can
state = record.get('stateManager.currentState.path')
unless state == "rootState.loaded.materializing"
record.send('materializingData')
record.eachAttribute( (name, attribute) ->
value = @extractAttribute(record.constructor, serialized, name)
if value != undefined
value = @deserializeValue(value, attribute.type)
if value != data.attributes[name]
record.materializeAttribute(name, value)
record.notifyPropertyChange(name)
, this)
record.eachRelationship( (name, relationship) ->
if relationship.kind == 'belongsTo'
key = @_keyForBelongsTo(record.constructor, relationship.key)
value = @extractBelongsTo(record.constructor, serialized, key)
if value != undefined && data.belongsTo[name] != value
record.materializeBelongsTo name, value
record.notifyPropertyChange(name)
else if relationship.kind == 'hasMany'
key = @_keyForHasMany(record.constructor, relationship.key)
value = @extractHasMany(record.constructor, serialized, key)
if value != undefined
record.materializeHasMany name, value
record.notifyPropertyChange(name)
, this)
record.notifyPropertyChange('data')
Travis.RestAdapter = DS.RESTAdapter.extend
serializer: Travis.Serializer
mappings:
broadcasts: Travis.Broadcast
repositories: Travis.Repo
@ -38,3 +98,53 @@ require 'models'
return
else
@_super.apply this, arguments
merge: (store, record, serialized) ->
@get('serializer').merge(record, serialized)
didFindRecord: (store, type, payload, id) ->
if (type == Travis.Build || type == Travis.Job) && payload.commit?
payload.commits = payload.commit
delete payload.commit
@_super.apply this, arguments
didSaveRecord: (store, type, record, payload) ->
# API sometimes return { result: true } response
# which does not play nice with ember-data. For now
# let's just change payload to have serialized record
# included, but ideally it should be fixed in the API
# to be consistent across all the endpoints.
if payload?.result == true
payload = {}
payload[type.singularName()] = record.serialize()
@_super(store, type, record, payload)
Travis.RestAdapter.map 'Travis.Commit', {}
Travis.RestAdapter.map 'Travis.Build', {
repoId: { key: 'repository_id' }
repo: { key: 'repository_id' }
_duration: { key: 'duration' }
jobs: { key: 'job_ids' }
_config: { key: 'config' }
}
Travis.RestAdapter.map 'Travis.Repo', {
_lastBuildDuration: { key: 'last_build_duration' }
}
Travis.RestAdapter.map 'Travis.Job', {
repoId: { key: 'repository_id' }
repo: { key: 'repository_id' }
_config: { key: 'config' }
}
Travis.RestAdapter.map 'Travis.User', {
_name: { key: 'name' }
}
Travis.RestAdapter.map 'Travis.Sponsor', {
_image: { key: 'image' }
}

View File

@ -12,7 +12,7 @@ $.extend Travis.Tailing.prototype,
@positionButton()
Ember.run.later(@run.bind(this), @options.timeout) if @active()
toggle: (event) ->
toggle: ->
if @active() then @stop() else @start()
active: ->

View File

@ -6,6 +6,6 @@
{{else}}
<h1>Sign in</h1>
<p>
<a href="#" {{action signIn target="Travis.app"}}>Please sign in with GitHub.</a>
<a href="#" {{action signIn target="Travis"}}>Please sign in with GitHub.</a>
</p>
{{/if}}

View File

@ -1,4 +1,4 @@
{{#if builds.isLoaded}}
{{#if content.isLoaded}}
<table id="builds" class="list">
<thead>
<tr>
@ -21,14 +21,14 @@
</thead>
<tbody>
{{#each build in builds}}
{{#each build in controller}}
{{#view Travis.BuildsItemView contextBinding="build"}}
<td class="number">
<span class="status"></span>
{{#if id}}
<a {{action showBuild repo this href=true}}>
{{#linkTo "build" repo this}}
{{number}}
</a>
{{/linkTo}}
{{/if}}
</td>
<td class="message">
@ -42,7 +42,7 @@
<td class="committer">
{{commit.committerName}}
</td>
{{#if commit.pullRequestNumber}}
{{#if view.isPullRequestsList}}
<td>
<a {{bindAttr href="view.urlGithubPullRequest"}}>
#{{commit.pullRequestNumber}}
@ -59,9 +59,11 @@
{{/each}}
</tbody>
</table>
<p>
{{view view.ShowMoreButton}}
</p>
{{#if displayShowMoreButton}}
<p>
{{view view.ShowMoreButton}}
</p>
{{/if}}
{{else}}
<div class="loading"><span>Loading</span></div>
{{/if}}
{{/if}}

View File

@ -1,15 +1,17 @@
{{#with view}}
{{#if loading}}
<span>Loading</span>
{{else}}
{{#if loading}}
<span>Loading</span>
{{else}}
{{#if build}}
<dl id="summary">
<div class="left">
<dt>{{t builds.name}}</dt>
<dd class="number">
<span class="status"></span>
{{#with build}}
<a {{action showBuild repo this href=true}}>{{number}}</a>
{{/with}}
{{#if build.id}}
{{#if build.repo.slug}}
{{#linkTo build repo build}}{{build.number}}{{/linkTo}}
{{/if}}
{{/if}}
</dd>
<dt>{{t builds.state}}</dt>
<dd class="state">{{capitalize build.state}}</dd>
@ -19,30 +21,32 @@
<dd class="duration" {{bindAttr title="startedAt"}}>{{formatDuration build.duration}}</dd>
</div>
<div class="right">
<dt>{{t builds.commit}}</dt>
<dd class="commit"><a href="{{unbound urlGithubCommit}}">{{formatCommit build.commit}}</a></dd>
{{#if build.isPullRequest}}
<dt>{{t builds.pullRequest}}</dt>
<dd class="pull_request"><a href="{{unbound commit.compareUrl}}">#{{build.commit.pullRequestNumber}} {{build.commit.pullRequestTitle}}</a></dd>
{{else}}
{{#if commit.compareUrl}}
<dt>{{t builds.compare}}</dt>
<dd class="compare"><a href="{{unbound commit.compareUrl}}">{{pathFrom build.commit.compareUrl}}</a></dd>
{{#if commit}}
<div class="right">
<dt>{{t builds.commit}}</dt>
<dd class="commit"><a {{bindAttr href="urlGithubCommit"}}>{{formatCommit build.commit}}</a></dd>
{{#if build.isPullRequest}}
<dt>{{t builds.pullRequest}}</dt>
<dd class="pull_request"><a {{bindAttr href="commit.compareUrl"}}>#{{build.commit.pullRequestNumber}} {{build.commit.pullRequestTitle}}</a></dd>
{{else}}
{{#if commit.compareUrl}}
<dt>{{t builds.compare}}</dt>
<dd class="compare"><a {{bindAttr href="commit.compareUrl"}}>{{pathFrom build.commit.compareUrl}}</a></dd>
{{/if}}
{{/if}}
{{/if}}
{{#if commit.authorName}}
<dt>{{t builds.author}}</dt>
<dd class="author"><a href="{{unbound urlAuthor}}">{{build.commit.authorName}}</a></dd>
{{/if}}
{{#if commit.committerName}}
<dt>{{t builds.committer}}</dt>
<dd class="committer"><a href="{{unbound urlCommitter}}">{{build.commit.committerName}}</a></dd>
{{/if}}
</div>
{{#if commit.authorName}}
<dt>{{t builds.author}}</dt>
<dd class="author"><a {{bindAttr href="urlAuthor"}}>{{build.commit.authorName}}</a></dd>
{{/if}}
{{#if commit.committerName}}
<dt>{{t builds.committer}}</dt>
<dd class="committer"><a {{bindAttr href="urlCommitter"}}>{{build.commit.committerName}}</a></dd>
{{/if}}
</div>
{{/if}}
<dt>{{t builds.message}}</dt>
<dd class="message">{{{formatMessage build.commit.message}}}</dd>
<dd class="message">{{formatMessage build.commit.message}}</dd>
{{#unless isMatrix}}
<dt>{{t builds.config}}</dt>
@ -54,7 +58,9 @@
{{view Travis.JobsView jobsBinding="build.requiredJobs" required="true"}}
{{view Travis.JobsView jobsBinding="build.allowedFailureJobs"}}
{{else}}
{{view Travis.LogView contextBinding="build.jobs.firstObject"}}
{{view Travis.LogView jobBinding="build.jobs.firstObject"}}
{{/if}}
{{else}}
There are no builds for this repository.
{{/if}}
{{/with}}
{{/if}}

View File

@ -24,7 +24,9 @@
<td class="number">
<span class="status"></span>
{{#if job.id}}
<a {{action showJob repo job href=true}}>{{number}}</a>
{{#if job.repo.slug}}
{{#linkTo "job" repo job}}{{number}}{{/linkTo}}
{{/if}}
{{/if}}
</td>
<td class="duration" {{bindAttr title="startedAt"}}>

View File

@ -1,13 +1,7 @@
{{view.logSubscriber}}
{{#if view.log.isLoaded}}
{{view Travis.PreView logBinding="view.log"}}
{{#if view.job.log.isLoaded}}
{{! this #with + #if is needed because I want to rerender 'pre' when log changes to properly clean it up,
this should probably be refactored to use container view}}
{{#with view.job.log}}
{{view Travis.PreView logBinding="view.context.log" logUrlBinding="view.logUrl"}}
{{/with}}
{{#if sponsor.name}}
{{#if view.job.sponsor.name}}
<p class="sponsor">
{{t builds.messages.sponsored_by}}
<a {{bindAttr href="sponsor.url"}}>{{sponsor.name}}</a>

View File

@ -1,4 +1,14 @@
<pre id="log" class="ansi"><a href="#" id="tail" {{action toggleTailing target="view"}}>
<span class="status"></span>
<label>Follow logs</label>
</a></pre>
<div id="log-container">
<a href="#" id="tail" {{action toggleTailing target="view"}}>
<span class="status"></span>
<label>Follow logs</label>
</a>
<pre id="log" class="ansi"></pre>
{{#if view.limited}}
<p class="warning">
This log is too long to be displayed. Please reduce the verbosity of your
build or download the the <a {{bindAttr href="view.plainTextLogUrl"}}>raw log</a>.
</p>
{{/if}}
</div>

View File

@ -5,9 +5,9 @@
<ul class="jobs">
{{#each job in sortedJobs}}
<li class="job">
<a {{action showJob job.repoData job target="Travis.app.router" href=true}}>
{{#linkTo "job" job.repoData job}}
#{{job.number}}
</a>
{{/linkTo}}
</li>
{{/each}}
</ul>

View File

@ -1,23 +1,25 @@
{{#with view}}
{{#if job.isLoaded}}
<div {{bindAttr class="view.color"}}>
<dl id="summary">
<div class="left">
<dt>Job</dt>
<dd class="number">
<span class="status"></span>
{{#if job.id}}
<a {{action showJob repo job href=true}}>{{job.number}}</a>
{{#if job.isLoaded}}
<div {{bindAttr class="view.color"}}>
<dl id="summary">
<div class="left">
<dt>Job</dt>
<dd class="number">
<span class="status"></span>
{{#if job.id}}
{{#if job.repo.slug}}
{{#linkTo job repo job}}{{job.number}}{{/linkTo}}
{{/if}}
</dd>
<dt>{{t jobs.state}}</dt>
<dd class="state">{{capitalize job.state}}</dd>
<dt class="finished_at_label">{{t jobs.finished_at}}</dt>
<dd class="finished_at timeago" {{bindAttr title="finishedAt"}}>{{formatTime job.finishedAt}}</dd>
<dt>{{t jobs.duration}}</dt>
<dd class="duration" {{bindAttr title="startedAt"}}>{{formatDuration job.duration}}</dd>
</div>
{{/if}}
</dd>
<dt>{{t jobs.state}}</dt>
<dd class="state">{{capitalize job.state}}</dd>
<dt class="finished_at_label">{{t jobs.finished_at}}</dt>
<dd class="finished_at timeago" {{bindAttr title="finishedAt"}}>{{formatTime job.finishedAt}}</dd>
<dt>{{t jobs.duration}}</dt>
<dd class="duration" {{bindAttr title="startedAt"}}>{{formatDuration job.duration}}</dd>
</div>
{{#if commit}}
<div class="right">
<dt>{{t jobs.commit}}</dt>
<dd class="commit"><a {{bindAttr href="urlGithubCommit"}}>{{formatCommit commit}}</a></dd>
@ -34,18 +36,18 @@
<dd class="committer"><a {{bindAttr href="urlCommitter"}}>{{commit.committerName}}</a></dd>
{{/if}}
</div>
{{/if}}
<dt>{{t jobs.message}}</dt>
<dd class="message">{{formatMessage commit.message}}</dd>
<dt>{{t jobs.config}}</dt>
<dd class="config">{{formatConfig job.config}}</dd>
</dl>
<dt>{{t jobs.message}}</dt>
<dd class="message">{{formatMessage commit.message}}</dd>
<dt>{{t jobs.config}}</dt>
<dd class="config">{{formatConfig job.config}}</dd>
</dl>
{{view Travis.LogView contextBinding="job"}}}
</div>
{{else}}
<div id="job" class="loading">
<span>Loading</span>
</div>
{{/if}}
{{/with}}
{{view Travis.LogView jobBinding="job"}}
</div>
{{else}}
<div id="job" class="loading">
<span>Loading</span>
</div>
{{/if}}

View File

@ -18,7 +18,7 @@
</a>
</div>
<div id="slider" {{action toggle target="Travis.app.slider"}}>
<div id="slider" {{action toggle target="Travis.slider"}}>
<div class='icon'></div>&nbsp;
</div>

View File

@ -4,7 +4,7 @@
</a>
</div>
<div id="slider" {{action toggle target="Travis.app.slider"}}>
<div id="slider" {{action toggle target="Travis.slider"}}>
<div class='icon'></div>&nbsp;
</div>

View File

@ -1,13 +1,13 @@
<a {{action showRoot href=true}}>
{{#linkTo index.current}}
<h1>Travis</h1>
</a>
{{/linkTo}}
<ul id="navigation">
<li class="home">
<a {{action showRoot href=true}}>{{t layouts.top.home}}</a>
{{#linkTo index.current}}{{t layouts.top.home}}{{/linkTo}}
</li>
<li class="stats">
<a {{action showStats href=true}}>{{t layouts.top.stats}}</a>
{{#linkTo stats}}{{t layouts.top.stats}}{{/linkTo}}
</li>
<li>
<a href="http://about.travis-ci.org/blog">{{t layouts.top.blog}}</a>
@ -15,18 +15,21 @@
<li>
<a href="http://about.travis-ci.org/docs">{{t layouts.top.docs}}</a>
</li>
<li>
<a href="http://status.travis-ci.com">{{t layouts.top.status}}</a>
</li>
<li {{bindAttr class="view.classProfile"}}>
<p class="handle">
<a class="signed-out" href="#" {{action signIn target="Travis.app"}}>{{t layouts.top.github_login}}</a>
<a class="signed-in" {{action showProfile href=true}}><img {{bindAttr src="view.gravatarUrl"}}>{{view.userName}}</a>
<a class="signed-out" href="#" {{action signIn target="Travis"}}>{{t layouts.top.github_login}}</a>
{{#linkTo "profile.index" class="signed-in"}}<img {{bindAttr src="view.gravatarUrl"}}>{{view.userName}}{{/linkTo}}
<span class="signing-in">{{t layouts.top.signing_in}}</span>
</p>
<ul>
<li>
<a {{action showProfile href=true}}>{{t layouts.top.accounts}}</a>
{{#linkTo "profile.index" class="signed-in"}}{{t layouts.top.accounts}}{{/linkTo}}
</li>
<li>
<a href="/" {{action signOut target="Travis.app"}}>{{t layouts.top.sign_out}}</a>
<a href="/" {{action signOut target="Travis"}}>{{t layouts.top.sign_out}}</a>
</li>
</ul>
</li>

View File

@ -0,0 +1 @@
The requested page was not found.

View File

@ -9,7 +9,7 @@
<div class="tab">
{{#collection Travis.AccountsListView contentBinding="controller"}}
<a {{action showAccount view.account href=true}} class="name">{{view.name}}</a>
{{#linkTo "account.index" view.account class="name"}}{{view.name}}{{/linkTo}}
<p class="summary">
<span class="repos_label">Repositories:</span>
<abbr class="repos">{{view.account.reposCount}}</abbr>

View File

@ -1,13 +1,17 @@
<ul class="tabs">
<li id="tab_hooks" {{bindAttr class="view.classHooks"}}>
<h5>
<a {{action showAccount view.account href=true}}>Repositories</a>
{{#with view.account}}
{{#if login}}
{{#linkTo "account.index" this}}Repositories{{/linkTo}}
{{/if}}
{{/with}}
</h5>
</li>
{{#if view.displayUser}}
<li id="tab_user" {{bindAttr class="view.classUser"}}>
<h5>
<a {{action showUserProfile view.account href=true}}>Profile</a>
{{#linkTo "account.profile" view.account}}Profile{{/linkTo}}
</h5>
</li>
{{/if}}

View File

@ -10,7 +10,7 @@
{{else}}
<p class="message">
Last synchronized from GitHub: {{formatTime user.syncedAt}}
<a class="sync_now button" {{action sync target="user"}}>
<a class="sync_now button" {{action sync}}>
Sync now
</a>
</p>
@ -24,7 +24,7 @@
<div class="controls">
<a {{bindAttr href="hook.urlGithubAdmin"}} class="github-admin tool-tip" title="Github service hooks admin page"></a>
<a {{action toggle target="hook"}} class="switch">
<a {{action toggle hook}} class="switch">
{{#if hook.active}}
ON
{{else}}

View File

@ -32,7 +32,7 @@
<dd>
{{view Ember.Select id="locale"
contentBinding="view.locales"
valueBinding="Travis.app.currentUser.locale"
valueBinding="user.locale"
optionLabelPath="content.name"
optionValuePath="content.key"}}
</dd>

View File

@ -6,12 +6,12 @@
{{#each job in queue}}
{{#view Travis.QueueItemView jobBinding="job"}}
{{#if job.repoSlug}}
<a {{action showJob job.repoData job target="Travis.app.router" href=true}}>
{{#linkTo "job" job.repoData job}}
<span class="slug">
{{job.repoSlug}}
</span>
#{{job.number}}
</a>
{{/linkTo}}
{{/if}}
{{/view}}
{{else}}
@ -20,7 +20,7 @@
</ul>
{{#if queue.isMore}}
<a {{action showAll this.queue target="view"}} class="show-more-jobs">
<a {{action showAll queue}} class="show-more-jobs">
{{queue.leftLength}} more jobs - show all
</a>
{{/if}}

View File

@ -5,17 +5,17 @@
{{view Travis.ReposListTabsView}}
<div class="tab">
{{#collection Travis.ReposListView contentBinding="controller"}}
{{#collection Travis.ReposListView contentBinding="this"}}
{{#with view.repo}}
<div class="slug-and-status">
<span class="status"></span>
{{#if slug}}
<a {{action showRepo this href=true}} class="slug">{{slug}}</a>
{{#linkTo repo this class="slug"}}{{slug}}{{/linkTo}}
{{/if}}
</div>
{{#with lastBuildHash}}
{{#if id}}
<a {{action showBuild repo id href=true}} class="last_build">{{number}}</a>
{{#if repo.slug}}
{{#linkTo build repo id class="last_build"}}{{number}}{{/linkTo}}
{{/if}}
{{/with}}

View File

@ -1,13 +1,13 @@
<ul class="tabs">
<li id="tab_recent" {{bindAttr class="view.classRecent"}}>
<h5><a name="recent" {{action activate target="view"}}>{{t layouts.application.recent}}</a></h5>
<h5><a {{action activate "recent" target="view"}}>{{t layouts.application.recent}}</a></h5>
</li>
<li id="tab_owned" {{bindAttr class="view.classOwned"}}>
<h5><a name="owned" {{action activate target="view"}}>{{t layouts.application.my_repositories}}</a></h5>
<h5><a {{action activate "owned" target="view"}}>{{t layouts.application.my_repositories}}</a></h5>
</li>
<li id="tab_search" {{bindAttr class="view.classSearch"}}>
<h5><a name="search" {{action activate target="view"}}>{{t layouts.application.search}}</a></h5>
<h5><a {{action activate "search" target="view"}}>{{t layouts.application.search}}</a></h5>
</li>
<a {{action toggleInfo target="view"}} class="toggle-info"></a>
</ul>
</ul>

View File

@ -1,27 +1,29 @@
<div id="repo" {{bindAttr class="view.class"}}>
<div id="repo" {{bindAttr class="view.className"}}>
{{#if view.isEmpty}}
{{view Travis.ReposEmptyView}}
{{else}}
{{#if view.repo.isLoaded}}
{{#with view.repo}}
<h3>
<a {{bindAttr href="view.urlGithub"}}>{{slug}}</a>
</h3>
<p class="description">{{description}}</p>
{{view Travis.RepoShowStatsView}}
{{view Travis.RepoShowTabsView}}
{{view Travis.RepoShowToolsView}}
{{/with}}
{{#if isError}}
<span class="not-found">The repository at {{slug}} was not found.</span>
{{else}}
<span>Loading</span>
{{/if}}
{{#if repo.isLoaded}}
{{#with repo}}
<h3>
<a {{bindAttr href="controller.urlGithub"}}>{{slug}}</a>
</h3>
<div class="tab">
{{outlet pane}}
</div>
<p class="description">{{description}}</p>
{{view Travis.RepoShowTabsView}}
{{view Travis.RepoShowToolsView}}
{{/with}}
<div class="tab">
{{outlet pane}}
</div>
{{else}}
<span>Loading</span>
{{/if}}
{{/if}}
{{/if}}
</div>

View File

@ -1,17 +0,0 @@
<ul class="github-stats">
<!-- <li class="language">
{{lastBuildLanguage}}
</li> -->
<li>
<a class="watchers" title="Watchers" {{bindAttr href="view.urlGithubWatchers"}}>
{{view.stats.watchers}}
</a>
</li>
<li>
<a class="forks" title="Forks" {{bindAttr href="view.urlGithubNetwork"}}>
{{view.stats.forks}}
</a>
</li>
</ul>

View File

@ -1,60 +1,60 @@
<ul class="tabs">
<li id="tab_current" {{bindAttr class="view.classCurrent"}}>
<h5>
{{#if view.repo.slug}}
<a {{action showRepo view.repo href=true}}>
{{t repositories.tabs.current}}
</a>
{{#if slug}}
{{#linkTo "repo" this currentWhen="repo.index"}}
{{t repositories.tabs.current}}
{{/linkTo}}
{{/if}}
</h5>
</li>
<li id="tab_builds" {{bindAttr class="view.classBuilds"}}>
<h5>
{{#if view.repo.slug}}
<a {{action showBuilds view.repo href=true}}>
{{t repositories.tabs.build_history}}
</a>
{{#if slug}}
{{#linkTo "builds" this}}
{{t repositories.tabs.build_history}}
{{/linkTo}}
{{/if}}
</h5>
</li>
<li id="tab_pull_requests" {{bindAttr class="view.classPullRequests"}}>
<h5>
{{#if view.repo.slug}}
<a {{action showPullRequests view.repo href=true}}>
{{t repositories.tabs.pull_requests}}
</a>
{{#if slug}}
{{#linkTo "pullRequests" this}}
{{t repositories.tabs.pull_requests}}
{{/linkTo}}
{{/if}}
</h5>
</li>
<li id="tab_branches" {{bindAttr class="view.classBranches"}}>
<h5>
{{#if view.repo.slug}}
<a {{action showBranches view.repo href=true}}>
{{t repositories.tabs.branches}}
</a>
{{#if slug}}
{{#linkTo "branches" this}}
{{t repositories.tabs.branches}}
{{/linkTo}}
{{/if}}
</h5>
</li>
<li id="tab_build" {{bindAttr class="view.classBuild"}}>
<h5>
{{#with view.build}}
{{#if id}}
<a {{action showBuild repo this href=true}}>
{{t repositories.tabs.build}} #{{number}}
</a>
{{#if view.build.id}}
{{#if view.build.repo.slug}}
{{#linkTo "build" view.build.repo view.build}}
{{t repositories.tabs.build}} #{{view.build.number}}
{{/linkTo}}
{{/if}}
{{/with}}
{{/if}}
</h5>
</li>
<li id="tab_job" {{bindAttr class="view.classJob"}}>
<h5>
{{#with view.job}}
{{#if id}}
<a {{action showJob repo this href=true}}>
{{t repositories.tabs.job}} #{{number}}
</a>
{{#if view.job.id}}
{{#if view.job.repo.slug}}
{{#linkTo "job" view.job.repo view.job}}
{{t repositories.tabs.job}} #{{view.job.number}}
{{/linkTo}}
{{/if}}
{{/with}}
{{/if}}
</h5>
</li>
</ul>

View File

@ -1,4 +1,4 @@
<h4>{{t layouts.application.sponsers}}</h4>
<h4>{{t layouts.application.sponsors}}</h4>
<ul class="sponsors top">
{{#each deck in controller}}

View File

@ -1,5 +1,5 @@
<div class="box">
<h4>{{t layouts.application.sponsers}}</h4>
<h4>{{t layouts.application.sponsors}}</h4>
<ul class="sponsors bottom">
{{#each controller}}

View File

@ -1,7 +1,7 @@
{{#view Travis.WorkersView}}
<h4>
{{t workers}}
<a id="toggle-workers" {{action toggleWorkers target="parentView.parentView"}}></a>
<a id="toggle-workers" {{action toggleWorkers target="view"}}></a>
</h4>
<ul id="workers">
{{#each group in controller.groups}}
@ -17,9 +17,9 @@
<div class="status"></div>
{{#if worker.isWorking}}
{{#if worker.jobId}}
<a {{action showJob worker.repoData worker.jobId target="Travis.app.router" href=true}}>
{{#linkTo "job" worker.repoData worker.jobId}}
{{view.display}}
</a>
{{/linkTo}}
{{/if}}
{{else}}
{{view.display}}

View File

@ -2,11 +2,11 @@ require 'ext/ember/namespace'
@Travis.reopen
View: Em.View.extend
popup: (event) ->
popup: (name) ->
@popupCloseAll()
name = if event.target then event.target.name else event
name = event?.target?.name || name
$("##{name}").toggleClass('display')
popupClose: (event) ->
popupClose: ->
$(event.target).closest('.popup').removeClass('display')
popupCloseAll: ->
if view = Travis.View.currentPopupView
@ -21,6 +21,7 @@ require 'views/build'
require 'views/events'
require 'views/flash'
require 'views/job'
require 'views/log'
require 'views/repo'
require 'views/profile'
require 'views/sidebar'

View File

@ -1,24 +1,23 @@
@Travis.reopen
Travis.reopen
ApplicationView: Travis.View.extend
templateName: 'layouts/home'
templateName: (-> @get 'controller.templateName' ).property('controller.templateName')
classNames: ['application']
connectLayout: (name) ->
name = "layouts/#{name}"
if @get('templateName') != name
@set('templateName', name)
@rerender()
templateNameDidChange: (->
@rerender()
).observes('templateName')
# popup: (event) ->
# console.log event
click: (event) ->
click: ->
# TODO: this solves the case of closing menus and popups,
# but I would like to rewrite it later, not sure how
# yet, but this does not seem optimal
targetAndParents = $(event.target).parents().andSelf()
if ! ( targetAndParents.hasClass('open-popup') || targetAndParents.hasClass('popup') )
@popupCloseAll()
if ! targetAndParents.hasClass('menu')
# TODO: I needed to add second check to this conditional, because for some reason
# event.stopPropagation() in menu() function in RepoShowToolsView does
# not prevent calling following code
if ! targetAndParents.hasClass('menu') && !targetAndParents.is('#tools > a')
$('.menu').removeClass('display')

View File

@ -1,18 +1,12 @@
@Travis.reopen
Travis.reopen
BuildsView: Travis.View.extend
templateName: 'builds/list'
buildsBinding: 'controller.builds'
isPullRequestsList: (->
console.log @get('controller.tab')
@get('controller.tab') == 'pull_requests'
).property('controller.tab')
showMore: ->
id = @get('controller.repo.id')
number = @get('builds.lastObject.number')
@get('builds').load Travis.Build.olderThanNumber(id, number)
ShowMoreButton: Em.View.extend
tagName: 'button'
classNameBindings: ['isLoading']
@ -27,7 +21,7 @@
).property('isLoading')
click: ->
@get('parentView').showMore()
@get('controller').showMore()
BuildsItemView: Travis.View.extend
tagName: 'tr'
@ -36,6 +30,8 @@
buildBinding: 'context'
commitBinding: 'build.commit'
isPullRequestsList: ( -> @get('parentView.isPullRequestsList') ).property('parentView.isPullRequestsList')
color: (->
Travis.Helpers.colorForState(@get('build.state'))
).property('build.state')
@ -50,32 +46,10 @@
BuildView: Travis.View.extend
templateName: 'builds/show'
elementId: 'build'
classNameBindings: ['color', 'loading']
repoBinding: 'controller.repo'
buildBinding: 'controller.build'
commitBinding: 'build.commit'
currentItemBinding: 'build'
loading: (->
!@get('build.isLoaded')
).property('build.isLoaded')
loadingBinding: 'controller.loading'
color: (->
Travis.Helpers.colorForState(@get('build.state'))
).property('build.state')
urlGithubCommit: (->
Travis.Urls.githubCommit(@get('repo.slug'), @get('commit.sha'))
).property('repo.slug', 'commit.sha')
urlAuthor: (->
Travis.Urls.email(@get('commit.authorEmail'))
).property('commit.authorEmail')
urlCommitter: (->
Travis.Urls.email(@get('commit.committerEmail'))
).property('commit.committerEmail')
Travis.Helpers.colorForState(@get('controller.build.state'))
).property('controller.build.state')

View File

@ -12,5 +12,5 @@
@get('flash.type') || 'broadcast'
).property('flash.type')
close: (event) ->
close: ->
@get('controller').close(@get('flash'))

View File

@ -1,4 +1,4 @@
@Travis.reopen
Travis.reopen
JobsView: Travis.View.extend
templateName: 'jobs/list'
buildBinding: 'controller.build'
@ -37,204 +37,3 @@
urlCommitter: (->
Travis.Urls.email(@get('commit.committerEmail'))
).property('commit.committerEmail')
LogView: Travis.View.extend
templateName: 'jobs/log'
logBinding: 'job.log'
plainTextLogUrl: (->
if id = @get('job.log.id')
Travis.Urls.plainTextLog(id)
).property('job.log')
didInsertElement: ->
@_super.apply this, arguments
@tryScrollingToHashLineNumber()
scrollTo: (hash) ->
# and this is even more weird, when changing hash in URL in firefox
# to other value, for example #L10, it actually scrolls just #main
# element... this is probably some CSS issue, I don't have time to
# investigate at the moment
# TODO: fix this
$('#main').scrollTop 0
# weird, html works in chrome, body in firefox
$('html,body').scrollTop $(hash).offset().top
@set 'controller.lineNumberHash', null
lineNumberHashDidChange: (->
@tryScrollingToHashLineNumber()
).observes('controller.lineNumberHash')
tryScrollingToHashLineNumber: ->
if hash = @get 'controller.lineNumberHash'
self = this
checker = ->
return if self.get('isDestroyed')
if $(hash).length
self.scrollTo(hash)
else
setTimeout checker, 100
checker()
click: (event) ->
target = $(event.target)
target.closest('.fold').toggleClass('open')
if target.is('a') && target.attr('id') && target.attr('id').match(/^L\d+$/)
path = target.attr 'href'
Travis.app.get('router').route(path)
event.stopPropagation()
return false
toTop: () ->
$(window).scrollTop(0)
jobBinding: 'context'
logSubscriber: (->
# for some reason observing context does not work,
# TODO: find out why
job = @get('job')
job.subscribe() if job && !job.get('isFinished')
null
).property('job', 'job.state')
logUrl: (->
repo = @get('job.repo')
item = @get('parentView.currentItem')
if repo && item
event = if item.constructor == Travis.Build
'showBuild'
else
'showJob'
Travis.app.get('router').urlForEvent(event, repo, item)
).property('job.repo', 'parentView.currentItem')
PreView: Em.View.extend
templateName: 'jobs/pre'
init: ->
@_super.apply this, arguments
@set 'logManager', Travis.Log.create(target: this)
toggleTailing: (event) ->
Travis.app.tailing.toggle()
event.preventDefault()
didInsertElement: ->
@_super.apply this, arguments
Ember.run.next this, ->
if @get 'log.isInitialized'
@logDidChange()
willDestroy: ->
@get('logManager').destroy()
@get('log.parts').removeArrayObserver this,
didChange: 'logContentsDidChange'
willChange: 'logContentsWillChange'
version: (->
@rerender()
@set 'logManager', Travis.Log.create(target: this)
).observes('log.version')
logDidChange: (->
if @get('log.isInitialized') && @state == 'inDOM'
@attachLogObservers()
).observes('log', 'log.isInitialized')
attachLogObservers: ->
return if @get('logPartsObserversAttached') == Ember.guidFor(@get('log'))
@set 'logPartsObserversAttached', Ember.guidFor(@get('log'))
Ember.run.next this, ->
@get('logManager').append @get('log.parts')
@get('log.parts').addArrayObserver this,
didChange: 'logContentsDidChange'
willChange: 'logContentsWillChange'
logContentsDidChange: (lines, index, removedCount, addedCount) ->
addedLines = lines.slice(index, index + addedCount)
@get('logManager').append addedLines
logContentsWillChange: (-> )
appendLog: (payloads) ->
url = @get('logUrl')
leftOut = []
cut = false
fragment = document.createDocumentFragment()
# TODO: refactor this loop, it's getting messy
for payload in payloads
line = payload.content
number = payload.number
if payload.logWasCut
cut = true
else
unless payload.append
pathWithNumber = "#{url}#L#{number}"
p = document.createElement('p')
p.innerHTML = "<a href=\"#{pathWithNumber}\" id=\"L#{number}\">#{number}</a>#{line}"
line = p
if payload.fold && !payload.foldContinuation
div = document.createElement('div')
div.appendChild line
div.className = "fold #{payload.fold} show-first-line"
line = div
if payload.replace
if link = fragment.querySelector("#L#{number}")
link.parentElement.innerHTML = line.innerHTML
else
this.$("#L#{number}").parent().replaceWith line
else if payload.append
if link = fragment.querySelector("#L#{number}")
link.parentElement.innerHTML += line
else
this.$("#L#{number}").parent().append line
else if payload.foldContinuation
folds = fragment.querySelectorAll(".fold.#{payload.fold}")
if fold = folds[folds.length - 1]
fold.appendChild line
else
this.$("#log .fold.#{payload.fold}:last").append line
else
fragment.appendChild(line)
if payload.openFold
folds = fragment.querySelectorAll(".fold.#{payload.openFold}")
if fold = folds[folds.length - 1]
fold = $(fold)
else
fold = this.$(".fold.#{payload.openFold}:last")
fold.removeClass('show-first-line').addClass('open')
if payload.foldEnd
folds = fragment.querySelectorAll(".fold.#{payload.fold}")
if fold = folds[folds.length - 1]
fold = $(fold)
else
fold = this.$(".fold.#{payload.fold}:last")
fold.removeClass('show-first-line')
this.$('#log')[0].appendChild fragment
if cut
url = Travis.Urls.plainTextLog(@get('log.id'))
this.$("#log").append $("<p class=\"cut\">Log was too long to display. Download the <a href=\"#{url}\">the raw version</a> to get the full log.</p>")

View File

@ -10,9 +10,9 @@
classOwned: (->
classes = []
classes.push('active') if @get('tab') == 'owned'
classes.push('display') if Travis.app.get('currentUser')
classes.push('display') if @get('controller.currentUser')
classes.join(' ')
).property('tab', 'Travis.currentUser')
).property('tab', 'controller.currentUser')
classSearch: (->
'active' if @get('tab') == 'search'

View File

@ -0,0 +1,272 @@
require 'log'
require 'travis/ordered_log'
Log.DEBUG = true
Travis.UnorderedLogEngineMixin = Ember.Mixin.create
setupEngine: ->
console.log 'log view: create engine' if Log.DEBUG
@limit = new Log.Limit
@scroll = new Log.Scroll
@engine = Log.create(listeners: [new Log.FragmentRenderer, new Log.Folds, @scroll])
@observeParts()
@numberLineOnHover()
destroyEngine: ->
parts = @get('log.parts')
parts.removeArrayObserver(@, didChange: 'partsDidChange', willChange: 'noop')
observeParts: ->
parts = @get('log.parts')
parts.addArrayObserver(@, didChange: 'partsDidChange', willChange: 'noop')
parts = parts.slice(0)
@partsDidChange(parts, 0, null, parts.length)
partsDidChange: (parts, start, _, added) ->
console.log 'log view: parts did change' if Log.DEBUG
for part, i in parts.slice(start, start + added)
# console.log "limit in log view: #{@get('limited')}"
break if @get('limited')
@engine.set(part.number, part.content)
@propertyDidChange('limited')
lineNumberDidChange: (->
@scroll.set(number) if !@get('isDestroyed') && number = @get('controller.lineNumber')
).observes('controller.lineNumber')
limited: (->
@limit && @limit.limited
).property()
Travis.OrderedLogEngineMixin = Ember.Mixin.create
setupEngine: ->
@set('logManager', Travis.OrderedLog.create(target: this))
@get('logManager').append @get('log.parts')
@get('log.parts').addArrayObserver this,
didChange: 'partsDidChange'
willChange: 'noop'
destroyEngine: (view) ->
@get('logManager').destroy()
@get('log.parts').removeArrayObserver this,
didChange: 'partsDidChange'
willChange: 'noop'
partsDidChange: (parts, index, removedCount, addedCount) ->
addedParts = parts.slice(index, index + addedCount)
@get('logManager').append addedParts
lineNumberDidChange: (->
if number = @get('controller.lineNumber')
@tryScrollingToHashLineNumber(number)
).observes('controller.lineNumber')
scrollTo: (id) ->
# and this is even more weird, when changing hash in URL in firefox
# to other value, for example #L10, it actually scrolls just #main
# element... this is probably some CSS issue, I don't have time to
# investigate at the moment
# TODO: fix this
$('#main').scrollTop 0
# weird, html works in chrome, body in firefox
$('html,body').scrollTop $(id).offset().top
@set 'controller.lineNumber', null
tryScrollingToHashLineNumber: (number) ->
id = "#L#{number}"
checker = =>
return if @get('isDestroyed')
if $(id).length
@scrollTo(id)
else
setTimeout checker, 100
checker()
appendLog: (payloads) ->
url = @get('logUrl')
leftOut = []
cut = false
fragment = document.createDocumentFragment()
# TODO: refactor this loop, it's getting messy
for payload in payloads
line = payload.content
number = payload.number
if payload.logWasCut
cut = true
else
unless payload.append
pathWithNumber = "#{url}#L#{number}"
p = document.createElement('p')
p.innerHTML = "<a href=\"#{pathWithNumber}\" id=\"L#{number}\"></a>#{line}"
line = p
if payload.fold && !payload.foldContinuation
div = document.createElement('div')
div.appendChild line
div.className = "fold #{payload.fold} show-first-line"
line = div
if payload.replace
if link = fragment.querySelector("#L#{number}")
link.parentElement.innerHTML = line.innerHTML
else
this.$("#L#{number}").parent().replaceWith line
else if payload.append
if link = fragment.querySelector("#L#{number}")
link.parentElement.innerHTML += line
else
this.$("#L#{number}").parent().append line
else if payload.foldContinuation
folds = fragment.querySelectorAll(".fold.#{payload.fold}")
if fold = folds[folds.length - 1]
fold.appendChild line
else
this.$("#log .fold.#{payload.fold}:last").append line
else
fragment.appendChild(line)
if payload.openFold
folds = fragment.querySelectorAll(".fold.#{payload.openFold}")
if fold = folds[folds.length - 1]
fold = $(fold)
else
fold = this.$(".fold.#{payload.openFold}:last")
fold.removeClass('show-first-line').addClass('open')
if payload.foldEnd
folds = fragment.querySelectorAll(".fold.#{payload.fold}")
if fold = folds[folds.length - 1]
fold = $(fold)
else
fold = this.$(".fold.#{payload.fold}:last")
fold.removeClass('show-first-line')
this.$('#log')[0].appendChild fragment
if cut
url = Travis.Urls.plainTextLog(@get('log.id'))
this.$("#log").append $("<p class=\"cut\">Log was too long to display. Download the <a href=\"#{url}\">the raw version</a> to get the full log.</p>")
Travis.reopen
LogView: Travis.View.extend
templateName: 'jobs/log'
logBinding: 'job.log'
contextBinding: 'job'
didInsertElement: ->
job = @get('job')
job.subscribe() if job && !job.get('isFinished')
willDestroyElement: ->
job = @get('job')
job.unsubscribe() if job
toTop: () ->
$(window).scrollTop(0)
PreView: Em.View.extend(Travis.OrderedLogEngineMixin, {
templateName: 'jobs/pre'
didInsertElement: ->
console.log 'log view: did insert' if Log.DEBUG
@_super.apply this, arguments
@setupEngine()
@lineNumberDidChange()
willDestroyElement: ->
console.log 'log view: will destroy' if Log.DEBUG
@destroyEngine()
versionDidChange: (->
@rerender() if @get('inDOM')
).observes('log.version')
logDidChange: (->
console.log 'log view: log did change: rerender' if Log.DEBUG
@rerender() if @get('inDOM')
).observes('log')
plainTextLogUrl: (->
Travis.Urls.plainTextLog(id) if id = @get('log.job.id')
).property('job.log.id')
toggleTailing: ->
Travis.tailing.toggle()
event.preventDefault()
numberLineOnHover: ->
$('#log').on 'mouseenter', 'a', ->
$(this).attr('href', '#L' + ($(this.parentNode).prevAll('p:visible').length + 1))
click: ->
if (href = $(event.target).attr('href')) && matches = href?.match(/#L(\d+)$/)
@lineNumberClicked(matches[1])
event.stopPropagation()
false
else
target = $(event.target)
target.closest('.fold').toggleClass('open')
logUrl: (->
if item = @get('controller.currentItem')
if repo = item.get('repo')
name = if item.constructor == Travis.Build
'build'
else
'job'
Travis.__container__.lookup('router:main').generate(name, repo, item)
).property('controller.currentItem.repo', 'controller.currentItem')
lineNumberClicked: (number) ->
path = @get('logUrl') + "#L#{number}"
window.history.replaceState({ path: path }, null, path);
@set('controller.lineNumber', number)
noop: -> # TODO required?
})
Log.Scroll = ->
Log.Scroll.prototype = $.extend new Log.Listener,
set: (number) ->
return unless number
@number = number
@tryScroll()
insert: (log, line, pos) ->
@tryScroll() if @number
true
tryScroll: ->
if element = $("#log p:visible")[@number - 1]
$('#main').scrollTop(0)
$('html, body').scrollTop($(element).offset()?.top) # weird, html works in chrome, body in firefox
@highlight(element)
@number = undefined
highlight: (element) ->
$('#log p.highlight').removeClass('highlight')
$(element).addClass('highlight')
Log.Logger = ->
Log.Logger.prototype = $.extend new Log.Listener,
receive: (log, num, string) ->
@log("rcv #{num} #{JSON.stringify(string)}")
true
insert: (log, element, pos) ->
@log("ins #{element.id}, #{if pos.before then 'before' else 'after'}: #{pos.before || pos.after || '?'}, #{JSON.stringify(element)}")
remove: (log, element) ->
@log("rem #{element.id}")
log: (line) ->
console.log(line)

View File

@ -1,4 +1,4 @@
@Travis.reopen
Travis.reopen
ProfileView: Travis.View.extend
templateName: 'profile/show'
accountBinding: 'controller.account'
@ -11,7 +11,7 @@
templateName: 'profile/tabs'
tabBinding: 'controller.tab'
activate: (event) ->
activate: ->
@get('controller').activate(event.target.name)
classHooks: (->
@ -63,7 +63,7 @@
{ key: 'de', name: 'Deutsch' }
]
).property()
change: (event) ->
change: ->
return unless $('#locale').val()
@get('user').updateLocale($('#locale').val())

View File

@ -13,7 +13,9 @@
repoBinding: 'content'
classNames: ['repo']
classNameBindings: ['color', 'selected']
selectedBinding: 'repo.selected'
selected: (->
@get('content') == @get('controller.selectedRepo')
).property('controller.selectedRepo')
color: (->
Travis.Helpers.colorForState(@get('repo.lastBuildState'))
@ -22,9 +24,10 @@
ReposListTabsView: Travis.View.extend
templateName: 'repos/list/tabs'
tabBinding: 'controller.tab'
currentUserBinding: 'controller.currentUser.id'
activate: (event) ->
@get('controller').activate(event.target.name)
activate: (name) ->
@get('controller').activate(name)
classRecent: (->
'active' if @get('tab') == 'recent'
@ -33,13 +36,13 @@
classOwned: (->
classes = []
classes.push('active') if @get('tab') == 'owned'
classes.push('display-inline') if Travis.app.get('currentUser')
classes.push('display-inline') if @get('currentUser')
classes.join(' ')
).property('tab', 'Travis.app.currentUser')
).property('tab', 'currentUser')
classSearch: (->
'active' if @get('tab') == 'search'
).property('tab')
toggleInfo: (event) ->
toggleInfo: ->
$('#repos').toggleClass('open')

View File

@ -1,35 +1,15 @@
@Travis.reopen
Travis.reopen
RepoView: Travis.View.extend
templateName: 'repos/show'
reposBinding: 'controller.repos'
repoBinding: 'controller.repo'
reposBinding: 'controllers.repos'
class: (->
'loading' unless @get('repo.isLoaded')
).property('repo.isLoaded')
classNameBindings: ['controller.isLoading:loading']
isEmpty: (->
@get('repos.isLoaded') && @get('repos.length') == 0
).property('repos.isLoaded', 'repos.length')
urlGithub: (->
Travis.Urls.githubRepo(@get('repo.slug'))
).property('repo.slug'),
RepoShowStatsView: Travis.View.extend
templateName: 'repos/show/stats'
repoBinding: 'parentView.repo'
statsBinding: 'repo.stats'
urlGithubWatchers: (->
Travis.Urls.githubWatchers(@get('repo.slug'))
).property('repo.slug'),
urlGithubNetwork: (->
Travis.Urls.githubNetwork(@get('repo.slug'))
).property('repo.slug'),
ReposEmptyView: Travis.View.extend
template: ''
@ -81,13 +61,15 @@
buildBinding: 'controller.build'
jobBinding: 'controller.job'
tabBinding: 'controller.tab'
currentUserBinding: 'controller.currentUser'
closeMenu: ->
console.log 'closeMenu'
$('.menu').removeClass('display')
menu: (event) ->
menu: ->
@popupCloseAll()
element = $('#tools .menu').toggleClass('display')
$('#tools .menu').toggleClass('display')
event.stopPropagation()
requeue: ->
@ -104,7 +86,7 @@
@closeMenu()
@get('job').cancel()
statusImages: (event) ->
statusImages: ->
@set('active', true)
@closeMenu()
@popupCloseAll()
@ -115,7 +97,7 @@
view.appendTo($('body'))
event.stopPropagation()
regenerateKeyPopup: (event) ->
regenerateKeyPopup: ->
if @get('canRegenerateKey')
@set('active', true)
@closeMenu()
@ -134,13 +116,12 @@
regenerateKey: ->
@popupCloseAll()
self = this
@get('repo').regenerateKey
success: ->
self.popup('regeneration-success')
(@get('repo.content') || @get('repo')).regenerateKey
success: =>
@popup('regeneration-success')
error: ->
Travis.app.router.flashController.loadFlashes([{ error: 'Travis encountered an error while trying to regenerate the key, please try again.'}])
Travis.lookup('controller:flash').loadFlashes([{ error: 'Travis encountered an error while trying to regenerate the key, please try again.'}])
displayRequeueBuild: (->
@get('isBuildTab') && @get('build.isFinished')
@ -208,6 +189,6 @@
).property('tab')
hasPermission: (->
if permissions = Travis.app.get('currentUser.permissions')
permissions.contains @get('repo.id')
).property('Travis.app.currentUser.permissions.length', 'repo.id')
if permissions = @get('currentUser.permissions')
permissions.contains parseInt(@get('repo.id'))
).property('currentUser.permissions.length', 'repo.id')

View File

@ -4,64 +4,38 @@
DecksView: Em.View.extend
templateName: "sponsors/decks"
controller: Travis.SponsorsController.create
perPage: 1
didInsertElement: ->
controller = @get 'controller'
unless controller.get('content')
Travis.app.get('router.sidebarController').tickables.push(controller)
controller.set 'content', Travis.Sponsor.decks()
init: ->
@_super.apply this, arguments
@set 'controller', @get('controller').container.lookup('controller:decks')
LinksView: Em.View.extend
templateName: "sponsors/links"
controller: Travis.SponsorsController.create
perPage: 6
didInsertElement: ->
controller = @get 'controller'
unless controller.get('content')
controller.set 'content', Travis.Sponsor.links()
Travis.app.get('router.sidebarController').tickables.push(controller)
init: ->
@_super.apply this, arguments
@set 'controller', @get('controller').container.lookup('controller:links')
WorkersView: Em.View.extend
templateName: 'workers/list'
controller: Travis.WorkersController.create()
didInsertElement: ->
@set 'controller.content', Travis.Worker.find()
init: ->
@_super.apply this, arguments
@set 'controller', @get('controller').container.lookup('controller:workers')
QueuesView: Em.View.extend
templateName: 'queues/list'
controller: Em.ArrayController.create()
showAll: (event) ->
queue = event.context
queue.showAll()
didInsertElement: ->
queues = for queue in Travis.QUEUES
Travis.LimitedArray.create
content: Travis.Job.queued(queue.name), limit: 20
id: "queue_#{queue.name}"
name: queue.display
@set 'controller.content', queues
init: ->
@_super.apply this, arguments
@set 'controller', @get('controller').container.lookup('controller:queues')
RunningJobsView: Em.View.extend
templateName: 'jobs/running'
elementId: 'running-jobs'
controller: Travis.RunningJobsController.create()
init: ->
@_super.apply this, arguments
@set 'controller', @get('controller').container.lookup('controller:runningJobs')
groupsBinding: 'controller.sortedGroups'
jobsBinding: 'controller'
didInsertElement: ->
@get('controller').set 'content', Travis.Job.running()
GroupView: Em.View.extend
templateName: 'jobs/running/group'
tagName: 'li'
@ -74,7 +48,7 @@
WorkersView: Travis.View.extend
toggleWorkers: (event) ->
toggleWorkers: ->
handle = $(event.target).toggleClass('open')
if handle.hasClass('open')
$('#workers li').addClass('open')
@ -82,10 +56,12 @@
$('#workers li').removeClass('open')
WorkersListView: Travis.View.extend
toggle: (event) ->
$(event.target).closest('li').toggleClass('open')
toggle: ->
this.$().find('> li').toggleClass('open')
WorkersItemView: Travis.View.extend
classNameBindings: ['worker.state']
display: (->
name = (@get('worker.name') || '').replace('travis-', '')
state = @get('worker.state')
@ -96,6 +72,5 @@
"#{name}: #{state}"
).property('worker.state')
QueueItemView: Travis.View.extend
tagName: 'li'

View File

@ -3,5 +3,5 @@
templateName: 'auth/signin'
signingIn: (->
Travis.app.get('authState') == 'signing-in'
).property('Travis.app.authState')
Travis.get('authState') == 'signing-in'
).property('Travis.authState')

View File

@ -25,9 +25,9 @@
classProfile: (->
classes = ['profile menu']
classes.push('active') if @get('tab') == 'profile'
classes.push(Travis.app.get('authState'))
classes.push(Travis.get('authState'))
classes.join(' ')
).property('tab', 'Travis.app.authState')
).property('tab', 'Travis.authState')
showProfile: ->
$('#top .profile ul').show()

View File

@ -1,48 +1,49 @@
@Travis.SPONSORS = [
{ type: 'platinum', url: "http://www.wooga.com", image: "wooga-205x130.png" }
{ type: 'platinum', url: "http://bendyworks.com", image: "bendyworks-205x130.png" }
{ type: 'platinum', url: "http://cloudcontrol.com", image: "cloudcontrol-205x130.png" }
{ type: 'platinum', url: "http://xing.de", image: "xing-205x130.png" }
{ id: '1', type: 'platinum', url: "http://www.wooga.com", image: "wooga-205x130.png" }
{ id: '2', type: 'platinum', url: "http://bendyworks.com", image: "bendyworks-205x130.png" }
{ id: '3', type: 'platinum', url: "http://cloudcontrol.com", image: "cloudcontrol-205x130.png" }
{ id: '4', type: 'platinum', url: "http://xing.de", image: "xing-205x130.png" }
{ type: 'gold', url: "http://heroku.com", image: "heroku-205x60.png" }
{ type: 'gold', url: "http://soundcloud.com", image: "soundcloud-205x60.png" }
{ type: 'gold', url: "http://nedap.com", image: "nedap-205x60.png" }
{ type: 'gold', url: "http://mongohq.com", image: "mongohq-205x60.png" }
{ type: 'gold', url: "http://zweitag.de", image: "zweitag-205x60.png" }
{ type: 'gold', url: "http://kanbanery.com", image: "kanbanery-205x60.png" }
{ type: 'gold', url: "http://ticketevolution.com", image: "ticketevolution-205x60.jpg" }
{ type: 'gold', url: "http://plan.io/travis", image: "planio-205x60.png" }
{ id: '5', type: 'gold', url: "http://heroku.com", image: "heroku-205x60.png" }
{ id: '6', type: 'gold', url: "http://soundcloud.com", image: "soundcloud-205x60.png" }
{ id: '7', type: 'gold', url: "http://nedap.com", image: "nedap-205x60.png" }
{ id: '8', type: 'gold', url: "http://mongohq.com", image: "mongohq-205x60.png" }
{ id: '9', type: 'gold', url: "http://zweitag.de", image: "zweitag-205x60.png" }
{ id: '10', type: 'gold', url: "http://kanbanery.com", image: "kanbanery-205x60.png" }
{ id: '11', type: 'gold', url: "http://ticketevolution.com", image: "ticketevolution-205x60.jpg" }
{ id: '12', type: 'gold', url: "http://plan.io/travis", image: "planio-205x60.png" }
{ type: 'silver', link: "<a href=\"http://cobot.me\">Cobot</a><span>: The one tool to run your coworking space</span>" }
{ type: 'silver', link: "<a href=\"http://jumpstartlab.com\">JumpstartLab</a><span>: We build developers</span>" }
{ type: 'silver', link: "<a href=\"http://evilmartians.com\">Evil Martians</a><span>: Agile Ruby on Rails development</span>" }
{ type: 'silver', link: "<a href=\"http://zendesk.com\">Zendesk</a><span>: Love your helpdesk</span>" }
{ type: 'silver', link: "<a href=\"http://stripe.com\">Stripe</a><span>: Payments for developers</span>" }
{ type: 'silver', link: "<a href=\"http://basho.com\">Basho</a><span>: We make Riak!</span>" }
{ type: 'silver', link: "<a href=\"http://thinkrelevance.com\">Relevance</a><span>: We deliver software solutions</span>" }
{ type: 'silver', link: "<a href=\"http://mindmatters.de\">Mindmatters</a><span>: Software für Menschen</span>" }
{ type: 'silver', link: "<a href=\"http://amenhq.com\">Amen</a><span>: The best and worst of everything</span>" }
{ type: 'silver', link: "<a href=\"http://site5.com\">Site5</a><span>: Premium Web Hosting Solutions</span>" }
{ type: 'silver', link: "<a href=\"http://www.crowdint.com\">Crowd Interactive</a><span>: Leading Rails consultancy in Mexico</span>" }
{ type: 'silver', link: "<a href=\"http://www.atomicobject.com/detroit\">Atomic Object</a><span>: Work with really smart people</span>" }
{ type: 'silver', link: "<a href=\"http://codeminer.com.br\">Codeminer</a><span>: smart services for your startup</span>" }
{ type: 'silver', link: "<a href=\"http://cloudant.com\">Cloudant</a><span>: grow into your data layer, not out of it</span>" }
{ type: 'silver', link: "<a href=\"http://gidsy.com\">Gidsy</a><span>: Explore, organize &amp; book unique things to do!</span>" }
{ type: 'silver', link: "<a href=\"http://5apps.com\">5apps</a><span>: Package &amp; deploy HTML5 apps automatically</span>" }
{ type: 'silver', link: "<a href=\"http://meltmedia.com\">Meltmedia</a><span>: We are Interactive Superheroes</span>" }
{ type: 'silver', link: "<a href=\"http://www.fngtps.com\">Fingertips</a><span> offers design and development services</span>" }
{ type: 'silver', link: "<a href=\"http://www.engineyard.com\">Engine Yard</a><span>: Build epic apps, let us handle the rest</span>" }
{ type: 'silver', link: "<a href=\"http://malwarebytes.org\">Malwarebytes</a><span>: Defeat Malware once and for all.</span>" }
{ type: 'silver', link: "<a href=\"http://readmill.com\">Readmill</a><span>: The best reading app on the iPad.</span>" }
{ type: 'silver', link: "<a href=\"http://www.mdsol.com\">Medidata</a><span>: clinical tech improving quality of life</span>" }
{ type: 'silver', link: "<a href=\"http://coderwall.com/teams/4f27194e973bf000040005f0\">ESM</a><span>: Japan's best agile Ruby/Rails consultancy</span>" }
{ type: 'silver', link: "<a href=\"http://twitter.com\">Twitter</a><span>: instantly connects people everywhere</span>" }
{ type: 'silver', link: "<a href=\"http://agileanimal.com\">AGiLE ANiMAL</a><span>: we <3 Travis CI.</span>" }
{ type: 'silver', link: "<a href=\"http://tupalo.com\">Tupalo</a><span>: Discover, review &amp; share local businesses.</span>" }
{ type: 'silver', link: "<a href=\"http://pivotallabs.com\">Pivotal Labs</a>"}
{ type: 'silver', link: "<a href=\"http://fiksu.com\">Fiksu</a>"}
{ type: 'silver', link: "<a href=\"http://saucelabs.com\">Sauce Labs</a>"}
{ type: 'silver', link: "<a href=\"http://mogotest.com\">Mogotest</a><span>: Never be embarrassed by a visually broken site again.</span>"}
{ id: '13', type: 'silver', link: "<a href=\"http://cobot.me\">Cobot</a><span>: The one tool to run your coworking space</span>" }
{ id: '14', type: 'silver', link: "<a href=\"http://jumpstartlab.com\">JumpstartLab</a><span>: We build developers</span>" }
{ id: '15', type: 'silver', link: "<a href=\"http://evilmartians.com\">Evil Martians</a><span>: Agile Ruby on Rails development</span>" }
{ id: '16', type: 'silver', link: "<a href=\"http://zendesk.com\">Zendesk</a><span>: Love your helpdesk</span>" }
{ id: '17', type: 'silver', link: "<a href=\"http://stripe.com\">Stripe</a><span>: Payments for developers</span>" }
{ id: '18', type: 'silver', link: "<a href=\"http://basho.com\">Basho</a><span>: We make Riak!</span>" }
{ id: '19', type: 'silver', link: "<a href=\"http://thinkrelevance.com\">Relevance</a><span>: We deliver software solutions</span>" }
{ id: '20', type: 'silver', link: "<a href=\"http://mindmatters.de\">Mindmatters</a><span>: Software für Menschen</span>" }
{ id: '21', type: 'silver', link: "<a href=\"http://amenhq.com\">Amen</a><span>: The best and worst of everything</span>" }
{ id: '22', type: 'silver', link: "<a href=\"http://site5.com\">Site5</a><span>: Premium Web Hosting Solutions</span>" }
{ id: '23', type: 'silver', link: "<a href=\"http://www.crowdint.com\">Crowd Interactive</a><span>: Leading Rails consultancy in Mexico</span>" }
{ id: '24', type: 'silver', link: "<a href=\"http://www.atomicobject.com/detroit\">Atomic Object</a><span>: Work with really smart people</span>" }
{ id: '25', type: 'silver', link: "<a href=\"http://codeminer.com.br\">Codeminer</a><span>: smart services for your startup</span>" }
{ id: '26', type: 'silver', link: "<a href=\"http://cloudant.com\">Cloudant</a><span>: grow into your data layer, not out of it</span>" }
{ id: '27', type: 'silver', link: "<a href=\"http://gidsy.com\">Gidsy</a><span>: Explore, organize &amp; book unique things to do!</span>" }
{ id: '28', type: 'silver', link: "<a href=\"http://5apps.com\">5apps</a><span>: Package &amp; deploy HTML5 apps automatically</span>" }
{ id: '29', type: 'silver', link: "<a href=\"http://meltmedia.com\">Meltmedia</a><span>: We are Interactive Superheroes</span>" }
{ id: '30', type: 'silver', link: "<a href=\"http://www.fngtps.com\">Fingertips</a><span> offers design and development services</span>" }
{ id: '31', type: 'silver', link: "<a href=\"http://www.engineyard.com\">Engine Yard</a><span>: Build epic apps, let us handle the rest</span>" }
{ id: '32', type: 'silver', link: "<a href=\"http://malwarebytes.org\">Malwarebytes</a><span>: Defeat Malware once and for all.</span>" }
{ id: '33', type: 'silver', link: "<a href=\"http://readmill.com\">Readmill</a><span>: The best reading app on the iPad.</span>" }
{ id: '34', type: 'silver', link: "<a href=\"http://www.mdsol.com\">Medidata</a><span>: clinical tech improving quality of life</span>" }
{ id: '35', type: 'silver', link: "<a href=\"http://coderwall.com/teams/4f27194e973bf000040005f0\">ESM</a><span>: Japan's best agile Ruby/Rails consultancy</span>" }
{ id: '36', type: 'silver', link: "<a href=\"http://twitter.com\">Twitter</a><span>: instantly connects people everywhere</span>" }
{ id: '37', type: 'silver', link: "<a href=\"http://agileanimal.com\">AGiLE ANiMAL</a><span>: we <3 Travis CI.</span>" }
{ id: '38', type: 'silver', link: "<a href=\"http://tupalo.com\">Tupalo</a><span>: Discover, review &amp; share local businesses.</span>" }
{ id: '39', type: 'silver', link: "<a href=\"http://pivotallabs.com\">Pivotal Labs</a>"}
{ id: '40', type: 'silver', link: "<a href=\"http://fiksu.com\">Fiksu</a>"}
{ id: '41', type: 'silver', link: "<a href=\"http://saucelabs.com\">Sauce Labs</a>"}
{ id: '42', type: 'silver', link: "<a href=\"http://mogotest.com\">Mogotest</a><span>: Never be embarrassed by a visually broken site again.</span>"}
{ id: '43', type: 'silver', link: "<a href=\"http://busyconf.com\">BusyConf</a><span>: Conferences and Events Made Easy</span>"}
]
@Travis.WORKERS = {

View File

@ -1,6 +1,6 @@
jQuery.support.cors = true
@Travis.ajax = Em.Object.create
Travis.ajax = Em.Object.create
DEFAULT_OPTIONS:
accepts:
json: 'application/vnd.travis-ci.2+json'
@ -33,14 +33,14 @@ jQuery.support.cors = true
success = options.success || (->)
options.success = (data) =>
Travis.app.router.flashController.loadFlashes(data.flash) if Travis.app?.router && data.flash
delete data.flash
Travis.lookup('controller:flash').loadFlashes(data.flash) if data?.flash
delete data.flash if data?
success.apply(this, arguments)
error = options.error || (->)
options.error = (data) =>
Travis.app.router.flashController.pushObject(data.flash) if data.flash
delete data.flash
Travis.lookup('controller:flash').pushObject(data.flash) if data?.flash
delete data.flash if data?
error.apply(this, arguments)
$.ajax($.extend(options, Travis.ajax.DEFAULT_OPTIONS))

View File

@ -0,0 +1,92 @@
get = Ember.get
Travis.ChunkBuffer = Em.ArrayProxy.extend
timeout: 5000
checkTimeoutFrequency: 1000
start: 1
next: 1
init: ->
@_super.apply this, arguments
@lastInsert = 0
@set('next', @get('start'))
@checkTimeout()
if @get('content.length')
@get('queue.content').pushObjects @get('content').toArray()
arrangedContent: (->
[]
).property('content')
addObject: (obj) ->
@get('content').pushObject(obj)
removeObject: (obj) ->
@get('content').removeObject(obj)
replaceContent: (idx, amt, objects) ->
@get('content').replace(idx, amt, objects)
queue: (->
Em.ArrayProxy.extend(Em.SortableMixin, {
content: []
sortProperties: ['number']
sortAscending: true
}).create()
).property()
contentArrayDidChange: (array, index, removedCount, addedCount) ->
@_super.apply this, arguments
if addedCount
queue = @get('queue')
addedObjects = array.slice(index, index + addedCount)
console.log 'Added log parts with numbers:', addedObjects.map( (element) -> get(element, 'number') )+'', 'current', @get('next')
queue.pushObjects addedObjects
@check()
@inserted()
check: ->
queue = @get('queue')
next = @get('next')
arrangedContent = @get('arrangedContent')
toPush = []
while queue.get('firstObject.number') <= next
element = queue.shiftObject()
if get(element, 'number') == next
toPush.pushObject get(element, 'content')
next += 1
if toPush.length
arrangedContent.pushObjects toPush
@set('next', next)
inserted: ->
now = @now()
@lastInsert = now
checkTimeout: ->
now = @now()
if now - @lastInsert > @get('timeout')
@giveUpOnMissingParts()
@set 'runLaterId', Ember.run.later(this, @checkTimeout, @get('checkTimeoutFrequency'))
willDestroy: ->
Ember.run.cancel @get('runLaterId')
@_super.apply this, arguments
now: ->
(new Date()).getTime()
giveUpOnMissingParts: ->
if number = @get('queue.firstObject.number')
console.log 'Giving up on missing parts in the buffer, switching to:', number
@set('next', number)
@check()

View File

@ -33,10 +33,9 @@ Travis.ExpandableRecordArray = DS.RecordArray.extend
@pushObject object
pushObject: (record) ->
ids = @get 'content'
id = record.get 'id'
clientId = record.get 'clientId'
content = @get 'content'
id = record.get 'id'
clientId = record.get 'clientId'
reference = @get('store').referenceForClientId(clientId)
return if ids.contains clientId
ids.pushObject clientId
@addReference reference

View File

@ -54,7 +54,7 @@ Travis.LimitedArray = Em.ArrayProxy.extend
if addedCount
if index < limit
addedObjects = array.slice(index, index + addedCount)
@replaceContent(index, 0, addedObjects)
@get('arrangedContent').replace(index, 0, addedObjects)
@balanceArray()

View File

@ -0,0 +1,5 @@
Travis.LineNumberParser = Ember.Mixin.create
fetchLineNumber: ->
url = @container.lookup('router:main').get('url')
if match = url.match(/#L(\d+)$/)
match[1]

View File

@ -1,16 +1,4 @@
Travis.Location = Ember.HistoryLocation.extend
onUpdateURL: (callback) ->
guid = Ember.guidFor(this)
Ember.$(window).bind 'popstate.ember-location-'+guid, (e) ->
callback(location.pathname + location.hash)
getURL: ->
location = @get('location')
location.pathname + location.hash
initState: ->
@replaceState(@getURL());
Ember.set(this, 'history', window.history)
Ember.Location.implementations['travis'] = Travis.Location

View File

@ -1,15 +1,24 @@
@Travis.Model = DS.Model.extend
primaryKey: 'id'
id: DS.attr('number')
init: ->
@loadedAttributes = []
@_super.apply this, arguments
refresh: ->
if id = @get('id')
store = @get('store')
store.adapter.find store, @constructor, id
getAttr: (key, options) ->
@needsCompletionCheck(key)
@_super.apply this, arguments
getBelongsTo: (key, type, meta) ->
@needsCompletionCheck(key)
@_super.apply this, arguments
getHasMany: (key, type, meta) ->
@needsCompletionCheck(key)
@_super.apply this, arguments
needsCompletionCheck: (key) ->
if key && (@constructor.isAttribute(key) || @constructor.isRelationship(key)) &&
@get('incomplete') && !@isAttributeLoaded(key)
@loadTheRest(key)
update: (attrs) ->
$.each attrs, (key, value) =>
@ -17,14 +26,7 @@
this
isAttributeLoaded: (name) ->
key = null
if meta = Ember.get(this.constructor, 'attributes').get(name)
key = meta.key(this.constructor)
else if meta = Ember.get(this.constructor, 'associationsByName').get(name)
key = meta.options.key || @get('namingConvention').foreignKey(name)
if key
@get('store').isDataLoadedFor(this.constructor, @get('clientId'), key)
@get('store').isDataLoadedFor(this.constructor, @get('clientId'), name)
isComplete: (->
if @get 'incomplete'
@ -40,33 +42,32 @@
# undefined key
return if !key || key == 'undefined'
message = "Load missing fields for #{@constructor.toString()} because of missing key '#{key}', cid: #{@get('clientId')}"
message = "Load missing fields for #{@constructor.toString()} because of missing key '#{key}', cid: #{@get('clientId')}, id: #{@get('id')}"
if @constructor.isAttribute('state') && key != 'state'
message += ", in state: #{@get('state')}"
console.log message
return if @get('isCompleting')
@set 'isCompleting', true
@refresh()
unless @get('stateManager.currentState.path').match /^rootState.loaded.materializing/
@reload()
@set 'incomplete', false
select: ->
@constructor.select(@get('id'))
loadedAsIncomplete: () ->
@set 'incomplete', true
@Travis.Model.reopenClass
find: ->
if arguments.length == 0
Travis.app.store.findAll(this)
Travis.store.findAll(this)
else
@_super.apply(this, arguments)
filter: (callback) ->
Travis.app.store.filter(this, callback)
Travis.store.filter(this, callback)
load: (attrs) ->
Travis.app.store.load(this, attrs)
Travis.store.load(this, attrs)
select: (id) ->
@find().forEach (record) ->
@ -86,8 +87,18 @@
name.replace(/([A-Z])/g, '_$1').toLowerCase().slice(1)
pluralName: ->
Travis.app.store.adapter.pluralize(@singularName())
Travis.store.adapter.pluralize(@singularName())
isAttribute: (name) ->
Ember.get(this, 'attributes').has(name) ||
Ember.get(this, 'associationsByName').has(name)
Ember.get(this, 'attributes').has(name)
isRelationship: (name) ->
Ember.get(this, 'relationshipsByName').has(name)
isHasManyRelationship: (name) ->
if relationship = Ember.get(this, 'relationshipsByName').get(name)
relationship.kind == 'hasMany'
isBelongsToRelationship: (name) ->
if relationship = Ember.get(this, 'relationshipsByName').get(name)
relationship.kind == 'belongsTo'

View File

@ -5,7 +5,7 @@ FOLDS = [
Em.Object.create(name: 'bundle', startPattern: /^\$ bundle install/, endPattern: /^(<\/span>)?\$/)
]
@Travis.Log = Em.Object.extend
@Travis.OrderedLog = Em.Object.extend
init: ->
@set 'folds', []
@set 'line', 1

View File

@ -11,6 +11,4 @@
@schedule()
schedule: ->
Ember.run.later((=> @tick()), @get('interval') || Travis.app.TICK_INTERVAL)
Ember.run.later((=> @tick()), @get('interval') || Travis.TICK_INTERVAL)

View File

@ -1,13 +1,13 @@
describe 'on the "build" state', ->
beforeEach ->
app 'travis-ci/travis-core/builds/1'
app '/travis-ci/travis-core/builds/1'
console.log 'wait for repos'
waitFor reposRendered
runs ->
console.log 'wait for build'
waitFor buildRendered
afterEach ->
window.history.pushState({}, null, '/spec.html')
it 'displays the expected stuff', ->
listsRepos [
{ slug: 'travis-ci/travis-hub', build: { number: 4, url: '/travis-ci/travis-hub/builds/4', duration: '1 min', finishedAt: '-' } }
@ -52,14 +52,11 @@ describe 'on the "build" state', ->
describe 'on the "current" state', ->
beforeEach ->
app 'travis-ci/travis-core'
app '/travis-ci/travis-core'
waitFor reposRendered
runs ->
waitFor buildRendered
afterEach ->
window.history.pushState({}, null, '/spec.html')
it 'correctly updates values on pusher build:started event', ->
payload =
build:
@ -72,8 +69,8 @@ describe 'on the "current" state', ->
finished_at: '2012-07-02T00:02:55Z'
event_type: 'push'
result: 1
commit_message: 'commit message 3'
commit: '1234567'
message: 'commit message 3'
commit: 'foo1234'
state: 'started'
repository:
id: 1
@ -81,10 +78,19 @@ describe 'on the "current" state', ->
last_build_id: 11
Em.run ->
Travis.app.receive 'build:started', payload
Travis.receive 'build:started', payload
waits(100)
runs ->
displaysSummaryBuildLink '/travis-ci/travis-core/builds/11', '3'
displaysSummary
type: 'build'
id: 11
repo: 'travis-ci/travis-core'
commit: 'foo1234'
branch: 'master'
compare: '0123456..1234567'
finishedAt: 'less than a minute ago'
duration: '55 sec'
message: 'commit message 3'

View File

@ -1,11 +1,8 @@
describe 'on the "builds" state', ->
beforeEach ->
app 'travis-ci/travis-core/builds'
app '/travis-ci/travis-core/builds'
waitFor buildsRendered
afterEach ->
window.history.pushState({}, null, '/spec.html')
it 'displays the expected stuff', ->
listsRepos [
{ slug: 'travis-ci/travis-hub', build: { number: 4, url: '/travis-ci/travis-hub/builds/4', duration: '1 min', finishedAt: '-' } }

View File

@ -3,9 +3,6 @@ describe 'on the "current" state', ->
app 'travis-ci/travis-core'
waitFor buildRendered
afterEach ->
window.history.pushState({}, null, '/spec.html')
it 'displays the expected stuff', ->
listsRepos [
{ slug: 'travis-ci/travis-hub', build: { number: 4, url: '/travis-ci/travis-hub/builds/4', duration: '1 min', finishedAt: '-' } }

View File

@ -1,7 +1,4 @@
describe 'events', ->
afterEach ->
window.history.pushState({}, null, '/spec.html')
describe 'an event adding a repository', ->
beforeEach ->
app 'travis-ci/travis-core'
@ -24,7 +21,7 @@ describe 'events', ->
responseText: payload
Em.run ->
Travis.app.receive 'build:started',
Travis.receive 'build:started',
build:
id: 10
repository:
@ -57,13 +54,12 @@ describe 'events', ->
started_at: '2012-07-02T00:02:00Z'
finished_at: '2012-07-02T00:02:55Z'
event_type: 'push'
result: 1
message: 'commit message 3'
commit: '1234567'
state: 'started'
state: 'failed'
Em.run ->
Travis.app.receive 'build:started', payload
Travis.receive 'build:started', payload
waits(100)
runs ->
@ -74,43 +70,9 @@ describe 'events', ->
describe 'an event adding a job', ->
beforeEach ->
app 'travis-ci/travis-core'
waitFor jobsRendered
waitFor jobsRendered, 'jobs should be rendered'
runs ->
waitFor queuesRendered
it 'adds a job to the jobs matrix', ->
payload =
job:
id: 15
repository_id: 1
build_id: 1
commit_id: 1
log_id: 1
number: '1.4'
duration: 55
started_at: '2012-07-02T00:02:00Z'
finished_at: '2012-07-02T00:02:55Z'
config: { rvm: 'jruby' }
$.mockjax
url: '/jobs/15'
responseTime: 0
responseText: payload
Em.run ->
Travis.app.receive 'job:started',
job:
id: 15
repository_id: 1
build_id: 1
commit_id: 1
waits(100)
runs ->
listsJob
table: $('#jobs')
row: 3
item: { id: 15, number: '1.4', repo: 'travis-ci/travis-core', finishedAt: 'less than a minute ago', duration: '55 sec', rvm: 'jruby' }
waitFor queuesRendered, 'queues should be rendered'
it 'adds a job to the jobs queue', ->
payload =
@ -118,7 +80,7 @@ describe 'events', ->
id: 12
repository_id: 1
number: '1.4'
queue: 'builds.common'
queue: 'builds.linux'
$.mockjax
url: '/jobs/12'
@ -126,24 +88,25 @@ describe 'events', ->
responseText: payload
Em.run ->
Travis.app.receive 'job:started',
Travis.receive 'job:started',
job:
id: 12
repository_id: 1
repository_slug: 'travis-ci/travis-core'
number: '1.4'
queue: 'builds.common'
queue: 'builds.linux'
state: 'created'
waits(100)
waits(1000)
runs ->
listsQueuedJob
name: 'common'
name: 'linux'
row: 3
item: { number: '1.4', repo: 'travis-ci/travis-core' }
it 'updates only keys that are available', ->
Em.run ->
Travis.app.receive 'job:started',
Travis.receive 'job:started',
job:
id: 1
build_id: 1
@ -174,7 +137,7 @@ describe 'events', ->
responseText: payload
Em.run ->
Travis.app.receive 'worker:created',
Travis.receive 'worker:created',
worker:
id: 10
name: 'ruby-3'
@ -194,7 +157,7 @@ describe 'events', ->
app '/travis-ci/travis-core'
waitFor workersRendered
it 'does not update repository if it\'s already in store', ->
it 'does not update repository if it\'s already in the store', ->
payload =
worker:
id: 1
@ -208,7 +171,7 @@ describe 'events', ->
last_build_number: '999'
Em.run ->
Travis.app.receive 'worker:updated', payload
Travis.receive 'worker:updated', payload
waits(100)
runs ->

View File

@ -3,9 +3,6 @@ describe 'on the "index" state', ->
app 'travis-ci/travis-core'
waitFor buildRendered
afterEach ->
window.history.pushState({}, null, '/spec.html')
it 'displays the expected stuff', ->
listsRepos [
{ slug: 'travis-ci/travis-hub', build: { number: 4, url: '/travis-ci/travis-hub/builds/4', duration: '1 min', finishedAt: '-' } }

View File

@ -1,13 +1,16 @@
describe 'on the "job" state', ->
beforeEach ->
$.mockjax
url: '/jobs/1/log?cors_hax=true'
responseTime: 0
responseText: 'log 1'
app 'travis-ci/travis-core/jobs/1'
waitFor jobRendered
runs ->
waitFor hasText('#tab_build', 'Build #1')
afterEach ->
window.history.pushState({}, null, '/spec.html')
it 'displays the expected stuff', ->
listsRepos [
{ slug: 'travis-ci/travis-hub', build: { number: 4, url: '/travis-ci/travis-hub/builds/4', duration: '1 min', finishedAt: '-' } }
@ -15,26 +18,28 @@ describe 'on the "job" state', ->
{ slug: 'travis-ci/travis-assets', build: { number: 3, url: '/travis-ci/travis-assets/builds/3', duration: '30 sec', finishedAt: 'a day ago' } }
]
displaysRepository
href: 'http://github.com/travis-ci/travis-core'
waits 100
runs ->
displaysRepository
href: 'http://github.com/travis-ci/travis-core'
displaysSummary
id: 1
type: 'job'
repo: 'travis-ci/travis-core'
commit: '1234567'
branch: 'master'
compare: '0123456..1234567'
finishedAt: '3 minutes ago'
duration: '30 sec'
message: 'commit message 1'
displaysSummary
id: 1
type: 'job'
repo: 'travis-ci/travis-core'
commit: '1234567'
branch: 'master'
compare: '0123456..1234567'
finishedAt: '3 minutes ago'
duration: '30 sec'
message: 'commit message 1'
displaysTabs
current: { href: '/travis-ci/travis-core' }
builds: { href: '/travis-ci/travis-core/builds' }
build: { href: '/travis-ci/travis-core/builds/1' }
job: { href: '/travis-ci/travis-core/jobs/1', active: true }
displaysTabs
current: { href: '/travis-ci/travis-core' }
builds: { href: '/travis-ci/travis-core/builds' }
build: { href: '/travis-ci/travis-core/builds/1' }
job: { href: '/travis-ci/travis-core/jobs/1', active: true }
displaysLog [
'log 1'
]
displaysLog [
'log 1'
]

View File

@ -5,11 +5,8 @@ describe 'the sidebar', ->
runs ->
waitFor hasText('#tab_build', 'Build #1')
afterEach ->
window.history.pushState({}, null, '/spec.html')
it 'displays the expected stuff', ->
listsQueues [
{ name: 'common', item: { number: '5.1', repo: 'travis-ci/travis-core' } }
{ name: 'common', item: { number: '5.2', repo: 'travis-ci/travis-core' } }
{ name: 'linux', item: { number: '5.1', repo: 'travis-ci/travis-core' } }
{ name: 'linux', item: { number: '5.2', repo: 'travis-ci/travis-core' } }
]

View File

@ -1,55 +1,15 @@
minispade.require 'app'
@reset = ->
Em.run ->
if Travis.app
if Travis.app.store
Travis.app.store.destroy()
Travis.app.destroy()
delete Travis.app
delete Travis.store
waits(500) # TODO not sure what we need to wait for here
$('#application').remove()
$('body').append( $('<div id="application"></div>') )
@app = (url) ->
reset()
Em.run ->
Travis.run(rootElement: $('#application'))
waitFor -> Travis.app
# TODO: so much waiting here, I'm sure we can minimize this
runs ->
url = "/#{url}" if url && !url.match(/^\//)
Travis.app.router.route(url)
waits 500
runs ->
foo = 'bar'
# TODO: this should wait till app is initialized, not some
# arbitrary amount of time
waits(50)
runs ->
Travis.reset()
url = "/#{url}" unless url.match /^\//
Travis.__container__.lookup('router:main').handleURL(url)
_Date = Date
@Date = (date) ->
new _Date(date || '2012-07-02T00:03:00Z')
@Date.UTC = _Date.UTC
# hacks for missing features in webkit
unless Function::bind
Function::bind = (oThis) ->
# closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable") if typeof this isnt "function"
aArgs = Array::slice.call(arguments, 1)
fToBind = this
fNOP = ->
fBound = ->
fToBind.apply (if this instanceof fNOP and oThis then this else oThis), aArgs.concat(Array::slice.call(arguments_))
fNOP.prototype = @.prototype
fBound.prototype = new fNOP()
fBound
window.history.state = {}
oldPushState = window.history.pushState
window.history.pushState = (state, title, href) ->
window.history.state = state
oldPushState.apply this, arguments
now = -> new Date('2012-07-02T00:03:00Z')
$.timeago.settings.nowFunction = -> now().getTime()
Travis.currentDate = now

View File

@ -9,6 +9,6 @@
@buildsRendered = notEmpty('#builds .number')
@jobRendered = notEmpty('#summary .number')
@jobsRendered = notEmpty('#jobs .number')
@queuesRendered = notEmpty('#queue_common li')
@queuesRendered = notEmpty('#queue_linux li')
@workersRendered = notEmpty('.worker')

View File

@ -41,9 +41,8 @@
expect(element.text()).toEqual data.message
@displaysLog = (lines) ->
ix = 0
log = $.map(lines, (line) -> ix += 1; "#{ix}#{line}").join("\n")
expect($('#log p').text().trim()).toEqual log
log = lines.join()
expect($('#log').text().trim()).toEqual log
@listsRepos = (items) ->
listsItems('repo', items)

View File

@ -8,4 +8,3 @@
@waitFor = waitsFor

View File

@ -1,45 +1,45 @@
responseTime = 0
repos = [
{ id: 1, owner: 'travis-ci', name: 'travis-core', slug: 'travis-ci/travis-core', build_ids: [1, 2], last_build_id: 1, last_build_number: 1, last_build_result: 0, last_build_duration: 30, last_build_started_at: '2012-07-02T00:00:00Z', last_build_finished_at: '2012-07-02T00:00:30Z', description: 'Description of travis-core' },
{ id: 2, owner: 'travis-ci', name: 'travis-assets', slug: 'travis-ci/travis-assets', build_ids: [3], last_build_id: 3, last_build_number: 3, last_build_result: 1, last_build_duration: 30, last_build_started_at: '2012-07-02T00:01:00Z', last_build_finished_at: '2012-07-01T00:01:30Z', description: 'Description of travis-assets'},
{ id: 3, owner: 'travis-ci', name: 'travis-hub', slug: 'travis-ci/travis-hub', build_ids: [4], last_build_id: 4, last_build_number: 4, last_build_result: undefined, last_build_duration: undefined, last_build_started_at: '2012-07-02T00:02:00Z', last_build_finished_at: undefined, description: 'Description of travis-hub'},
{ id: '1', owner: 'travis-ci', name: 'travis-core', slug: 'travis-ci/travis-core', build_ids: [1, 2], last_build_id: 1, last_build_number: 1, last_build_result: 0, last_build_duration: 30, last_build_started_at: '2012-07-02T00:00:00Z', last_build_finished_at: '2012-07-02T00:00:30Z', description: 'Description of travis-core' },
{ id: '2', owner: 'travis-ci', name: 'travis-assets', slug: 'travis-ci/travis-assets', build_ids: [3], last_build_id: 3, last_build_number: 3, last_build_result: 1, last_build_duration: 30, last_build_started_at: '2012-07-02T00:01:00Z', last_build_finished_at: '2012-07-01T00:01:30Z', description: 'Description of travis-assets'},
{ id: '3', owner: 'travis-ci', name: 'travis-hub', slug: 'travis-ci/travis-hub', build_ids: [4], last_build_id: 4, last_build_number: 4, last_build_result: undefined, last_build_duration: undefined, last_build_started_at: '2012-07-02T00:02:00Z', last_build_finished_at: undefined, description: 'Description of travis-hub'},
]
builds = [
{ id: 1, repository_id: '1', commit_id: 1, job_ids: [1, 2, 3], number: 1, pull_request: false, config: { rvm: ['rbx', '1.9.3', 'jruby'] }, duration: 30, started_at: '2012-07-02T00:00:00Z', finished_at: '2012-07-02T00:00:30Z', result: 0 },
{ id: 2, repository_id: '1', commit_id: 2, job_ids: [4], number: 2, pull_request: false, config: { rvm: ['rbx'] } },
{ id: 3, repository_id: '2', commit_id: 3, job_ids: [5], number: 3, pull_request: false, config: { rvm: ['rbx'] }, duration: 30, started_at: '2012-07-02T00:01:00Z', finished_at: '2012-07-01T00:01:30Z', result: 1 },
{ id: 4, repository_id: '3', commit_id: 4, job_ids: [6], number: 4, pull_request: false, config: { rvm: ['rbx'] }, started_at: '2012-07-02T00:02:00Z' },
{ id: '1', repository_id: '1', commit_id: 1, job_ids: [1, 2, 3], number: 1, pull_request: false, config: { rvm: ['rbx', '1.9.3', 'jruby'] }, duration: 30, started_at: '2012-07-02T00:00:00Z', finished_at: '2012-07-02T00:00:30Z', state: 'passed' },
{ id: '2', repository_id: '1', commit_id: 2, job_ids: [4], number: 2, pull_request: false, config: { rvm: ['rbx'] } },
{ id: '3', repository_id: '2', commit_id: 3, job_ids: [5], number: 3, pull_request: false, config: { rvm: ['rbx'] }, duration: 30, started_at: '2012-07-02T00:01:00Z', finished_at: '2012-07-01T00:01:30Z', state: 'failed' },
{ id: '4', repository_id: '3', commit_id: 4, job_ids: [6], number: 4, pull_request: false, config: { rvm: ['rbx'] }, started_at: '2012-07-02T00:02:00Z' },
]
commits = [
{ id: 1, sha: '1234567', branch: 'master', message: 'commit message 1', author_name: 'author name', author_email: 'author@email.com', committer_name: 'committer name', committer_email: 'committer@email.com', compare_url: 'http://github.com/compare/0123456..1234567' },
{ id: 2, sha: '2345678', branch: 'feature', message: 'commit message 2', author_name: 'author name', author_email: 'author@email.com', committer_name: 'committer name', committer_email: 'committer@email.com', compare_url: 'http://github.com/compare/0123456..2345678' },
{ id: 3, sha: '3456789', branch: 'master', message: 'commit message 3', author_name: 'author name', author_email: 'author@email.com', committer_name: 'committer name', committer_email: 'committer@email.com', compare_url: 'http://github.com/compare/0123456..3456789' },
{ id: 4, sha: '4567890', branch: 'master', message: 'commit message 4', author_name: 'author name', author_email: 'author@email.com', committer_name: 'committer name', committer_email: 'committer@email.com', compare_url: 'http://github.com/compare/0123456..4567890' },
{ id: '1', sha: '1234567', branch: 'master', message: 'commit message 1', author_name: 'author name', author_email: 'author@email.com', committer_name: 'committer name', committer_email: 'committer@email.com', compare_url: 'http://github.com/compare/0123456..1234567' },
{ id: '2', sha: '2345678', branch: 'feature', message: 'commit message 2', author_name: 'author name', author_email: 'author@email.com', committer_name: 'committer name', committer_email: 'committer@email.com', compare_url: 'http://github.com/compare/0123456..2345678' },
{ id: '3', sha: '3456789', branch: 'master', message: 'commit message 3', author_name: 'author name', author_email: 'author@email.com', committer_name: 'committer name', committer_email: 'committer@email.com', compare_url: 'http://github.com/compare/0123456..3456789' },
{ id: '4', sha: '4567890', branch: 'master', message: 'commit message 4', author_name: 'author name', author_email: 'author@email.com', committer_name: 'committer name', committer_email: 'committer@email.com', compare_url: 'http://github.com/compare/0123456..4567890' },
]
jobs = [
{ id: 1, repository_id: 1, build_id: 1, commit_id: 1, log_id: 1, number: '1.1', config: { rvm: 'rbx' }, duration: 30, started_at: '2012-07-02T00:00:00Z', finished_at: '2012-07-02T00:00:30Z', result: 0 }
{ id: 2, repository_id: 1, build_id: 1, commit_id: 1, log_id: 2, number: '1.2', config: { rvm: '1.9.3' }, duration: 40, started_at: '2012-07-02T00:00:00Z', finished_at: '2012-07-02T00:00:40Z', result: 1 }
{ id: 3, repository_id: 1, build_id: 1, commit_id: 1, log_id: 3, number: '1.3', config: { rvm: 'jruby' }, allow_failure: true }
{ id: 4, repository_id: 1, build_id: 2, commit_id: 2, log_id: 4, number: '2.1', config: { rvm: 'rbx' } }
{ id: 5, repository_id: 2, build_id: 3, commit_id: 3, log_id: 5, number: '3.1', config: { rvm: 'rbx' }, duration: 30, started_at: '2012-07-02T00:01:00Z', finished_at: '2012-07-02T00:01:30Z', result: 1 }
{ id: 6, repository_id: 3, build_id: 4, commit_id: 4, log_id: 6, number: '4.1', config: { rvm: 'rbx' }, started_at: '2012-07-02T00:02:00Z' }
{ id: 7, repository_id: 1, build_id: 5, commit_id: 5, log_id: 7, number: '5.1', config: { rvm: 'rbx' }, state: 'created', queue: 'builds.common' }
{ id: 8, repository_id: 1, build_id: 5, commit_id: 5, log_id: 8, number: '5.2', config: { rvm: 'rbx' }, state: 'created', queue: 'builds.common' }
{ id: '1', repository_id: 1, repository_slug: 'travis-ci/travis-core', build_id: 1, commit_id: 1, log_id: 1, number: '1.1', config: { rvm: 'rbx' }, duration: 30, started_at: '2012-07-02T00:00:00Z', finished_at: '2012-07-02T00:00:30Z', state: 'passed' }
{ id: '2', repository_id: 1, repository_slug: 'travis-ci/travis-core', build_id: 1, commit_id: 1, log_id: 2, number: '1.2', config: { rvm: '1.9.3' }, duration: 40, started_at: '2012-07-02T00:00:00Z', finished_at: '2012-07-02T00:00:40Z', state: 'failed' }
{ id: '3', repository_id: 1, repository_slug: 'travis-ci/travis-core', build_id: 1, commit_id: 1, log_id: 3, number: '1.3', config: { rvm: 'jruby' }, allow_failure: true }
{ id: '4', repository_id: 1, repository_slug: 'travis-ci/travis-core', build_id: 2, commit_id: 2, log_id: 4, number: '2.1', config: { rvm: 'rbx' } }
{ id: '5', repository_id: 2, repository_slug: 'travis-ci/travis-assets', build_id: 3, commit_id: 3, log_id: 5, number: '3.1', config: { rvm: 'rbx' }, duration: 30, started_at: '2012-07-02T00:01:00Z', finished_at: '2012-07-02T00:01:30Z', state: 'failed' }
{ id: '6', repository_id: 3, repository_slug: 'travis-ci/travis-hub', build_id: 4, commit_id: 4, log_id: 6, number: '4.1', config: { rvm: 'rbx' }, started_at: '2012-07-02T00:02:00Z' }
{ id: '7', repository_id: 1, repository_slug: 'travis-ci/travis-core', build_id: 5, commit_id: 5, log_id: 7, number: '5.1', config: { rvm: 'rbx' }, state: 'created', queue: 'builds.linux' }
{ id: '8', repository_id: 1, repository_slug: 'travis-ci/travis-core', build_id: 5, commit_id: 5, log_id: 8, number: '5.2', config: { rvm: 'rbx' }, state: 'created', queue: 'builds.linux' }
]
artifacts = [
{ id: 1, body: 'log 1' }
{ id: 2, body: 'log 2' }
{ id: 3, body: 'log 3' }
{ id: 4, body: 'log 4' }
{ id: 5, body: 'log 5' }
{ id: 6, body: 'log 6' }
{ id: 7, body: 'log 7' }
{ id: 8, body: 'log 8' }
{ id: '1', body: 'log 1' }
{ id: '2', body: 'log 2' }
{ id: '3', body: 'log 3' }
{ id: '4', body: 'log 4' }
{ id: '5', body: 'log 5' }
{ id: '6', body: 'log 6' }
{ id: '7', body: 'log 7' }
{ id: '8', body: 'log 8' }
]
branches = [
@ -49,8 +49,8 @@ branches = [
]
workers = [
{ id: 1, name: 'ruby-1', host: 'worker.travis-ci.org', state: 'ready' }
{ id: 2, name: 'ruby-2', host: 'worker.travis-ci.org', state: 'ready' }
{ id: '1', name: 'ruby-1', host: 'worker.travis-ci.org', state: 'ready' }
{ id: '2', name: 'ruby-2', host: 'worker.travis-ci.org', state: 'ready' }
]
hooks = [

View File

@ -1,36 +0,0 @@
store = null
record = null
describe 'Travis.Artifact', ->
beforeEach ->
store = Travis.Store.create()
afterEach ->
store.destroy()
describe 'with part of the body loaded', ->
beforeEach =>
store.load Travis.Artifact, 1, { id: 1, body: 'first\nsecond\n' }
record = store.find(Travis.Artifact, 1)
it 'packs the existing part of the body to parts', ->
expect( record.get('parts').toArray() ).toEqual( ['first\nsecond\n'] )
it 'adds new chunks of log to parts', ->
record.append('third\n')
expect( record.get('parts').toArray() ).toEqual( ['first\nsecond\n', 'third\n'] )
it 'properly handles array observers', ->
called = 0
observer = {
arrayDidChange: -> called += 1
arrayWillChange: -> called += 1
}
record.get('parts').addArrayObserver observer,
willChange: 'arrayWillChange'
didChange: 'arrayDidChange'
record.append('something')
expect(called).toEqual 2

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