go back to sc-router

This commit is contained in:
Sven Fuchs 2012-06-27 17:52:49 +02:00
parent 948807eedd
commit 50ff39d5c0
28 changed files with 1611 additions and 444 deletions

View File

@ -16,6 +16,7 @@ input 'assets/javascripts' do
vendor/ansiparse.js
vendor/i18n.js
vendor/jquery.timeago.js
vendor/sc-routes.js
)
concat files, 'vendor.js'
end

View File

@ -1,16 +1,54 @@
$.mockjaxSettings.log = false
require 'hax0rs'
# $.mockjaxSettings.log = false
@Travis = Em.Namespace.create
run: ->
@app = Travis.App.create(this)
@app.initialize()
App: Em.Application.extend
initialize: (router) ->
$.extend(this, Travis.Controllers)
$.extend(this, Travis.Views)
@store = Travis.Store.create()
@_super(router || Travis.Router.create())
# ember wants this dependencies setup for connectOutlet
$.extend this, Travis.Controllers
$.extend this, Travis.Views
for name, controller of Travis.Controllers
name = name.charAt(0).toLowerCase() + name.substr(1)
this[name] = controller.create(namespace: this, controllers: this)
run: ->
@app = Travis.App.create()
@app.initialize()
@store = Travis.Store.create()
@routes = Travis.Router.create(app: this)
@_super(Em.Object.create())
@routes.start()
connectLayout: ->
view = Travis.Views.ApplicationView.create()
view.set('controller', @applicationController)
view.appendTo(@get('rootElement') || 'body')
connectLeft: (repositories) ->
@set('repositories', repositories)
@get('applicationController').connectOutlet(outletName: 'left', name: 'repositories', context: repositories)
connectRepository: (repository) ->
@set('repository', repository)
@get('applicationController').connectOutlet(outletName: 'main', name: 'repository', context: repository)
connectTabs: (build, job) ->
@setPath('tabsController.repository', @get('repository'))
@setPath('tabsController.build', build)
@setPath('tabsController.job', job)
@get('repositoryController').connectOutlet(outletName: 'tabs', name: 'tabs')
connectBuilds: (builds) ->
@get('repositoryController').connectOutlet(outletName: 'tab', name: 'history', context: builds)
connectBuild: (build) ->
@get('repositoryController').connectOutlet(outletName: 'tab', name: 'build', context: build)
connectJob: (job) ->
@get('repositoryController').connectOutlet(outletName: 'tab', name: 'job', context: job)
require 'ext/jquery'

View File

@ -1,35 +1,50 @@
@Travis.Urls =
repository: (repository) ->
"#!/#{repository.get('slug')}" if repository
lastBuild: (repository) ->
"#!/#{repository.get('slug')}/builds/#{repository.get('lastBuildId')}" if repository
builds: (repository) ->
"#!/#{repository.get('slug')}/builds" if repository
build: (repository, build) ->
"#!/#{repository.get('slug')}/builds/#{build.get('id')}" if repository && build
job: (repository, job) ->
"#!/#{repository.get('slug')}/jobs/#{job.get('id')}" if repository && job
Repository:
urlGithub: (->
'http://github.com/%@'.fmt @get('slug')
"http://github.com/#{@get('slug')}"
).property('slug'),
urlGithubWatchers: (->
'http://github.com/%@/watchers'.fmt @get('slug')
"http://github.com/#{@get('slug')}/watchers"
).property('slug'),
urlGithubNetwork: (->
'http://github.com/%@/network'.fmt @get('slug')
"http://github.com/#{@get('slug')}/network"
).property('slug'),
urlGithubAdmin: (->
'http://github.com/%@/admin/hooks#travis_minibucket'.fmt @get('slug')
"http://github.com/#{@get('slug')}/admin/hooks#travis_minibucket"
).property('slug')
statusImage: (->
'%@.png'.fmt @get('slug')
"#{@get('slug')}.png"
).property('slug')
Commit:
urlGithubCommit: (->
'http://github.com/%@/commit/%@'.fmt @getPath('repository.slug'), @getPath('commit.sha')
"http://github.com/#{@getPath('repository.slug')}/commit/#{@getPath('commit.sha')}"
).property('repository.slug', 'commit.sha')
urlAuthor: (->
'mailto:%@'.fmt @getPath('commit.authorEmail')
).property()
"mailto:#{@getPath('commit.authorEmail')}"
).property('commit.authorEmail')
urlCommitter: (->
'mailto:%@'.fmt @getPath('commit.committerEmail')
).property()
"mailto:#{@getPath('commit.committerEmail')}"
).property('commit.committerEmail')

View File

@ -2,6 +2,8 @@ require 'travis/model'
@Travis.Repository = Travis.Model.extend
slug: DS.attr('string')
owner: DS.attr('string')
name: DS.attr('string')
description: DS.attr('string')
lastBuildId: DS.attr('number')
lastBuildNumber: DS.attr('string')
@ -9,8 +11,6 @@ require 'travis/model'
lastBuildStarted_at: DS.attr('string')
lastBuildFinished_at: DS.attr('string')
primaryKey: 'slug'
lastBuild: DS.belongsTo('Travis.Build')
builds: (->
@ -21,14 +21,6 @@ require 'travis/model'
Travis.Build.byRepositoryId @get('id'), event_type: 'pull_request'
).property()
owner: (->
(@get('slug') || @_id).split('/')[0]
).property('owner', 'name'),
name: (->
(@get('slug') || @_id).split('/')[1]
).property('owner', 'name'),
lastBuildDuration: (->
duration = @getPath('data.lastBuildDuration')
duration = Travis.Helpers.durationFrom(@get('lastBuildStarted_at'), @get('lastBuildFinished_at')) unless duration

View File

@ -1,119 +1,71 @@
require 'hax0rs'
Travis.Router = Em.Object.extend
ROUTES:
'!/:owner/:name/jobs/:id/:line': 'job'
'!/:owner/:name/jobs/:id': 'job'
'!/:owner/:name/builds/:id': 'build'
'!/:owner/:name/builds': 'builds'
'!/:owner/:name/pull_requests': 'pullRequests'
'!/:owner/:name/branch_summary': 'branches'
'!/:owner/:name': 'current'
'': 'index'
@Travis.Router = Em.Router.extend
# enableLogging: true
location: 'hash'
init: () ->
@app = @get('app')
root: Em.Route.extend
# common "layout" state for all states that show a repo list on the left.
# there also will be "profile" and "stats" states next to "default" that do
# not have a 3-column layout
default: Em.Route.extend
route: '/'
start: ->
@app.connectLayout()
@app.connectLeft(Travis.Repository.find())
@route(route, action) for route, action of @ROUTES
viewCurrent: Ember.Route.transitionTo('repository.current')
viewBuilds: Ember.Route.transitionTo('repository.builds')
viewBuild: Ember.Route.transitionTo('repository.build')
viewJob: Ember.Route.transitionTo('repository.job')
route: (route, tab) ->
Em.routes.add(route, (params) => this[tab](params))
connectOutlets: (router) ->
repositories = Travis.Repository.find()
router.set('repositories', repositories)
router.set('job', undefined)
router.connectLeft(repositories)
index: (params) ->
repositories = @app.get('repositories')
onceLoaded repositories, =>
repository = repositories.get('firstObject')
@app.connectRepository(repository)
@app.connectTabs()
@app.connectBuild(repository.get('lastBuild'))
index: Em.Route.extend
route: '/'
current: (params) ->
@repository params, (repository) =>
@app.connectTabs()
@app.connectBuild(repository.get('lastBuild'))
# on / we show the most recent repository from the repos list, so we
# have to wait until it's loaded
connectOutlets: (router) ->
repositories = router.get('repositories')
onceLoaded repositories, =>
repository = repositories.get('firstObject')
build = Travis.Build.find(repository.get('lastBuildId'))
router.connectRepository(repository)
router.connectTabs()
router.connectBuild(build)
builds: (params) ->
@repository params, (repository) =>
@app.connectTabs()
@app.connectBuilds(repository.get('builds'))
repository: Em.Route.extend
route: '/:owner/:name'
build: (params) ->
@repository params
@buildBy params.id, (build) =>
@app.connectTabs(build)
@app.connectBuild(build)
serialize: (router, repository) ->
router.serializeRepository(repository)
job: (params) ->
@repository params
@jobBy params.id, (job) =>
@app.connectTabs(job.get('build'), job)
@app.connectJob(job)
deserialize: (router, params) ->
router.deserializeRepository(params)
repository: (params, callback) ->
@repositoryBy params, (repository) =>
@app.connectRepository(repository)
callback(repository) if callback
connectOutlets: (router, repository) ->
router.connectRepository(repository)
repositoryBy: (params, callback) ->
repositories = Travis.Repository.bySlug("#{params.owner}/#{params.name}")
onceLoaded repositories, =>
callback(repositories.get('firstObject'))
current: Em.Route.extend
route: '/'
connectOutlets: (router) ->
repository = router.get('repository')
onceLoaded repository, -> # TODO should not need to wait here, right?
build = repository.get('lastBuild')
router.connectTabs()
router.connectBuild(build)
builds: Em.Route.extend
route: '/builds'
connectOutlets: (router) ->
repository = router.get('repository')
onceLoaded repository, => # TODO hrm, otherwise it gets builds?repository_id=null
router.connectTabs()
router.connectBuilds(repository.get('builds'))
build: Em.Route.extend
route: '/builds/:build_id'
connectOutlets: (router, build) ->
build = Travis.Build.find(build.id) unless build instanceof Travis.Build # what?
router.connectTabs(build)
router.connectBuild(build)
job: Em.Route.extend
route: '/jobs/:job_id'
connectOutlets: (router, job) ->
job = Travis.Job.find(job.id) unless job instanceof Travis.Job # what?
router.connectTabs(job.get('build'), job)
router.connectJob(job)
connectLeft: (repositories) ->
@get('applicationController').connectOutlet(outletName: 'left', name: 'repositories', context: repositories)
connectRepository: (repository) ->
@set('repository', repository)
@get('applicationController').connectOutlet(outletName: 'main', name: 'repository', context: repository)
connectTabs: (build, job) ->
@setPath('tabsController.repository', @get('repository'))
@setPath('tabsController.build', build)
@setPath('tabsController.job', job)
@get('repositoryController').connectOutlet(outletName: 'tabs', name: 'tabs')
connectBuilds: (builds) ->
@get('repositoryController').connectOutlet(outletName: 'tab', name: 'history', context: builds)
connectBuild: (build) ->
@get('repositoryController').connectOutlet(outletName: 'tab', name: 'build', context: build)
connectJob: (job) ->
@get('repositoryController').connectOutlet(outletName: 'tab', name: 'job', context: job)
serializeRepository: (object) ->
if object instanceof Travis.Repository
slug = object.get('slug') || object._id # wat.
{ owner: slug.split('/')[0], name: slug.split('/')[1] }
else
object
deserializeRepository: (params) ->
Travis.Repository.find("#{params.owner}/#{params.name}")
buildBy: (id, callback) =>
build = Travis.Build.find(id)
onceLoaded build, =>
callback(build)
jobBy: (id, callback) ->
job = Travis.Job.find(id)
onceLoaded job, =>
callback(job)

View File

@ -13,7 +13,7 @@
{{#each build in content}}
{{#view Travis.Views.BuildsItemView contextBinding="build"}}
<tr>
<td class="number"><a {{action viewBuild href=true}}>{{number}}</a></td>
<td class="number"><a {{bindAttr href="view.urlBuild"}}>#{{number}}</a></td>
<td class="commit"><a {{bindAttr href="view.urlGithubCommit"}}>{{formatCommit commit}}</a></td>
<td class="message">{{{formatMessage commit.message short="true"}}}</td>
<td class="duration" {{bindAttr title="started_at"}}>{{formatDuration duration}}</td>

View File

@ -1,46 +1,48 @@
<div id="build" {{bindAttr class="classes"}}>
<dl class="summary clearfix">
<div class="left">
<dt>{{t builds.name}}</dt>
<dd class="number"><a {{action viewBuild href=true}}>{{number}}</a></dd>
<dt class="finished_at_label">{{t builds.finished_at}}</dt>
<dd class="finished_at timeago" {{bindAttr title="finished_at"}}>{{formatTime finished_at}}</dd>
<dt>{{t builds.duration}}</dt>
<dd class="duration" {{bindAttr title="started_at"}}>{{formatDuration duration}}</dd>
</div>
{{#unless isLoaded}}
Loading ...
{{else}}
<div id="build" {{bindAttr class="classes"}}>
<dl class="summary clearfix">
<div class="left">
<dt>{{t builds.name}}</dt>
<dd class="number"><a {{bindAttr href="view.urlBuild"}}>{{number}}</a></dd>
<dt class="finished_at_label">{{t builds.finished_at}}</dt>
<dd class="finished_at timeago" {{bindAttr title="finished_at"}}>{{formatTime finished_at}}</dd>
<dt>{{t builds.duration}}</dt>
<dd class="duration" {{bindAttr title="started_at"}}>{{formatDuration duration}}</dd>
</div>
<div class="right">
<dt>{{t builds.commit}}</dt>
<dd class="commit"><a {{bindAttr href="urlGithubCommit"}}>{{formatCommit commit}}</a></dd>
{{#if commit.compareUrl}}
<dt>{{t builds.compare}}</dt>
<dd class="compare"><a {{bindAttr href="commit.compareUrl"}}>{{pathFrom commit.compareUrl}}</a></dd>
{{/if}}
{{#if commit.authorName}}
<dt>{{t builds.author}}</dt>
<dd class="author"><a {{bindAttr href="urlAuthor"}}>{{commit.authorName}}</a></dd>
{{/if}}
{{#if commit.committerName}}
<dt>{{t builds.committer}}</dt>
<dd class="committer"><a {{bindAttr href="urlCommitter"}}>{{commit.committerName}}</a></dd>
{{/if}}
</div>
<div class="right">
<dt>{{t builds.commit}}</dt>
<dd class="commit"><a {{bindAttr href="urlGithubCommit"}}>{{formatCommit commit}}</a></dd>
{{#if commit.compareUrl}}
<dt>{{t builds.compare}}</dt>
<dd class="compare"><a {{bindAttr href="commit.compareUrl"}}>{{pathFrom commit.compareUrl}}</a></dd>
{{/if}}
{{#if commit.authorName}}
<dt>{{t builds.author}}</dt>
<dd class="author"><a {{bindAttr href="urlAuthor"}}>{{commit.authorName}}</a></dd>
{{/if}}
{{#if commit.committerName}}
<dt>{{t builds.committer}}</dt>
<dd class="committer"><a {{bindAttr href="urlCommitter"}}>{{commit.committerName}}</a></dd>
{{/if}}
</div>
<dt>{{t builds.message}}</dt>
<dd class="message">{{{formatMessage commit.message}}}</dd>
<dt>{{t builds.message}}</dt>
<dd class="message">{{{formatMessage commit.message}}}</dd>
{{#unless isMatrix}}
<dt>{{t builds.config}}</dt>
<dd class="config">{{formatConfig config}}</dd>
{{/unless}}
</dl>
{{#unless isMatrix}}
<dt>{{t builds.config}}</dt>
<dd class="config">{{formatConfig config}}</dd>
{{/unless}}
</dl>
{{#if isLoaded}}
{{#if isMatrix}}
{{view Travis.Views.JobsView jobsBinding="view.requiredJobs" required="true"}}
{{view Travis.Views.JobsView jobsBinding="view.allowedFailureJobs"}}
{{else}}
{{view Travis.Views.LogView contextBinding="jobs.firstObject"}}
{{/if}}
{{/if}}
</div>
</div>
{{/unless}}

View File

@ -15,15 +15,17 @@
</tr>
</thead>
<tbody>
{{#each view.jobs}}
<tr {{bindAttr class="color"}}>
<td class="number"><a {{action viewJob href=true}}>#{{number}}</a></td>
<td class="duration" {{bindAttr title="started_at"}}>{{formatDuration duration}}</td>
<td class="finished_at timeago" {{bindAttr title="finished_at"}}>{{formatTime finished_at}}</td>
{{#each configValues}}
<td>{{this}}</td>
{{/each}}
</tr>
{{#each job in view.jobs}}
{{#view Travis.Views.JobsItemView contextBinding="job"}}
<tr {{bindAttr class="color"}}>
<td class="number"><a {{bindAttr href="view.urlJob"}}>#{{number}}</a></td>
<td class="duration" {{bindAttr title="started_at"}}>{{formatDuration duration}}</td>
<td class="finished_at timeago" {{bindAttr title="finished_at"}}>{{formatTime finished_at}}</td>
{{#each configValues}}
<td>{{this}}</td>
{{/each}}
</tr>
{{/view}}
{{/each}}
</tbody>
</table>

View File

@ -2,7 +2,7 @@
<dl class="summary clearfix">
<div class="left">
<dt>Job</dt>
<dd class="number"><a {{action viewJob href=true}}>{{number}}</a></dd>
<dd class="number"><a {{bindAttr href="view.urlJob"}}>{{number}}</a></dd>
<dt class="finished_at_label">{{t jobs.finished_at}}</dt>
<dd class="finished_at timeago" {{bindAttr title="finished_at"}}>{{formatTime finished_at}}</dd>
<dt>{{t jobs.duration}}</dt>

View File

@ -1,10 +1,12 @@
{{#if content.lastObject.isLoaded}}
{{#unless content.lastObject.isLoaded}}
Loading ...
{{else}}
<ul id="repositories">
{{#each repository in content}}
{{#view Travis.Views.RepositoriesItemView tagName="li" classBinding="classes" contextBinding="repository"}}
<div class="wrapper">
<a {{action viewCurrent href=true}} class="current">{{slug}}</a>
<a {{action viewBuild href=true context="lastBuild"}} class="last_build">#{{lastBuildNumber}}</a>
<a {{bindAttr href="view.urlRepository"}} class="current">{{slug}}</a>
<a {{bindAttr href="view.urlLastBuild"}} class="last_build">#{{lastBuildNumber}}</a>
</div>
<p class="summary">
@ -20,4 +22,4 @@
{{/view}}
{{/each}}
<ul>
{{/if}}
{{/unless}}

View File

@ -1,19 +1,23 @@
<div id="repository">
<h3>
<a {{bindAttr href="urlGithub"}}>{{slug}}</a>
</h3>
{{#unless isLoaded}}
Loading ...
{{else}}
<div id="repository">
<h3>
<a {{bindAttr href="urlGithub"}}>{{slug}}</a>
</h3>
<p class="description">{{description}}</p>
<p class="description">{{description}}</p>
<ul class="github-stats">
<li class="language">{{last_build_language}}</li>
<li><a class="watchers" title="Watches" {{bindAttr href="urlGithubWatchers"}}>{{stats.watchers}}</a></li>
<li><a class="forks" title="Forks" {{bindAttr href="urlGithubNetwork"}}>{{stats.forks}}</a></li>
</ul>
<ul class="github-stats">
<li class="language">{{last_build_language}}</li>
<li><a class="watchers" title="Watches" {{bindAttr href="urlGithubWatchers"}}>{{stats.watchers}}</a></li>
<li><a class="forks" title="Forks" {{bindAttr href="urlGithubNetwork"}}>{{stats.forks}}</a></li>
</ul>
{{outlet tabs}}
{{outlet tabs}}
<div class="tab">
{{outlet tab}}
<div class="tab">
{{outlet tab}}
</div>
</div>
</div>
{{/unless}}

View File

@ -1,10 +1,10 @@
<ul class="tabs">
<li><a {{action viewCurrent href=true context="repository"}} class="current">Current</a></li>
<li><a {{action viewBuilds href=true context="repository"}} class="history">History</a></li>
<li><a {{bindAttr href="view.urlRepository"}} class="current">Current</a></li>
<li><a {{bindAttr href="view.urlBuilds"}} class="history">History</a></li>
{{#if build}}
<li><a {{action viewBuild href=true context="build"}} class="build">Build #{{build.number}}</a></li>
<li><a {{bindAttr href="view.urlBuild"}} class="build">Build #{{build.number}}</a></li>
{{/if}}
{{#if job}}
<li><a {{action viewJob href=true context="job"}} class="job">Job #{{job.number}}</a></li>
<li><a {{bindAttr href="view.urlJob"}} class="job">Job #{{job.number}}</a></li>
{{/if}}
</ul>

View File

@ -13,11 +13,13 @@ Travis.Views =
classes.join(' ')
).property('repository.lastBuildResult', 'repository.selected')
lastBuild: (->
owner: @getPath('repository.owner')
name: @getPath('repository.name')
id: @getPath('repository.lastBuildId')
).property('repository.owner', 'repository.name', 'repository.lastBuildId')
urlRepository: (->
Travis.Urls.repository(@get('context'))
).property('context')
urlLastBuild: (->
Travis.Urls.lastBuild(@get('context'))
).property('context')
RepositoryView: Em.View.extend
templateName: 'repositories/show'
@ -25,19 +27,39 @@ Travis.Views =
TabsView: Em.View.extend
templateName: 'repositories/tabs'
urlRepository: (->
Travis.Urls.repository(@getPath('controller.repository'))
).property('controller.repository.id')
urlBuilds: (->
Travis.Urls.builds(@getPath('controller.repository'))
).property('controller.repository.id')
urlBuild: (->
Travis.Urls.build(@getPath('controller.repository'), @getPath('controller.build'))
).property('controller.repository.slug', 'controller.build.id')
urlJob: (->
Travis.Urls.job(@getPath('controller.repository'), @getPath('controller.job'))
).property('controller.repository.slug', 'controller.job.id')
HistoryView: Em.View.extend
templateName: 'builds/list'
BuildsItemView: Em.View.extend
classes: (->
Travis.Helpers.colorForResult(@getPath('content.result'))
).property('content.result')
Travis.Helpers.colorForResult(@getPath('context.result'))
).property('context.result')
urlBuild: (->
Travis.Urls.build(@getPath('context.repository'), @get('context'))
).property('context.repository.slug', 'context')
BuildView: Em.View.extend
templateName: 'builds/show'
classes: (->
Helpers.colorForResult(@get('result'))
Travis.Helpers.colorForResult(@get('result'))
).property('result')
requiredJobs: (->
@ -48,9 +70,18 @@ Travis.Views =
@getPath('context.jobs').filter((job) -> job.get('allow_failure'))
).property()
urlBuild: (->
Travis.Urls.build(@getPath('context.repository'), @get('context'))
).property('controller.content.repository.id', 'controller.content.id')
JobsView: Em.View.extend
templateName: 'jobs/list'
JobsItemView: Em.View.extend
urlJob: (->
Travis.Urls.job(@getPath('context.repository'), @get('context'))
).property('context.repository', 'context')
JobView: Em.View.extend
templateName: 'jobs/show'
@ -58,6 +89,10 @@ Travis.Views =
Travis.Helpers.colorForResult(@get('result'))
).property('result')
urlJob: (->
Travis.Urls.job(@getPath('context.repository'), @get('context'))
).property('controller.content.repository.id', 'controller.content.id')
LogView: Em.View.extend
templateName: 'jobs/log'

View File

@ -1,3 +1,5 @@
responseTime = 0
repositories = [
{ 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 },
{ id: 2, owner: 'travis-ci', name: 'travis-assets', slug: 'travis-ci/travis-assets', build_ids: [3], last_build_id: 3, last_build_number: 3},
@ -5,10 +7,10 @@ repositories = [
]
builds = [
{ id: 1, repository_id: 'travis-ci/travis-core', commit_id: 1, job_ids: [1, 2], number: 1, event_type: 'push', config: { rvm: ['rbx', '1.9.3'] }, finished_at: '2012-06-20T00:21:20Z', duration: 35, result: 0 },
{ id: 2, repository_id: 'travis-ci/travis-core', commit_id: 2, job_ids: [3], number: 2, event_type: 'push', config: { rvm: ['rbx'] } },
{ id: 3, repository_id: 'travis-ci/travis-assets', commit_id: 3, job_ids: [4], number: 3, event_type: 'push', config: { rvm: ['rbx'] }, finished_at: '2012-06-20T00:21:20Z', duration: 35, result: 0 },
{ id: 4, repository_id: 'travis-ci/travis-hub', commit_id: 4, job_ids: [5], number: 4, event_type: 'push', config: { rvm: ['rbx'] } },
{ id: 1, repository_id: '1', commit_id: 1, job_ids: [1, 2], number: 1, event_type: 'push', config: { rvm: ['rbx', '1.9.3'] }, finished_at: '2012-06-20T00:21:20Z', duration: 35, result: 0 },
{ id: 2, repository_id: '1', commit_id: 2, job_ids: [3], number: 2, event_type: 'push', config: { rvm: ['rbx'] } },
{ id: 3, repository_id: '2', commit_id: 3, job_ids: [4], number: 3, event_type: 'push', config: { rvm: ['rbx'] }, finished_at: '2012-06-20T00:21:20Z', duration: 35, result: 0 },
{ id: 4, repository_id: '3', commit_id: 4, job_ids: [5], number: 4, event_type: 'push', config: { rvm: ['rbx'] } },
]
commits = [
@ -36,19 +38,19 @@ artifacts = [
$.mockjax
url: '/repositories'
responseTime: 0
responseTime: responseTime
responseText: { repositories: repositories }
for repository in repositories
$.mockjax
url: '/' + repository.slug
responseTime: 0
responseTime: responseTime
responseText: { repository: repository }
for build in builds
$.mockjax
url: '/builds/' + build.id
responseTime: 0
responseTime: responseTime
responseText:
build: build,
commit: commits[build.commit_id - 1]
@ -57,8 +59,8 @@ for build in builds
for repository in repositories
$.mockjax
url: '/builds'
data: { repository_id: 1, event_type: 'push', orderBy: 'number DESC' }
responseTime: 0
data: { repository_id: repository.id, event_type: 'push', orderBy: 'number DESC' }
responseTime: responseTime
responseText:
builds: (builds[id - 1] for id in repository.build_ids)
commits: (commits[builds[id - 1].commit_id - 1] for id in repository.build_ids)
@ -66,7 +68,7 @@ for repository in repositories
for job in jobs
$.mockjax
url: '/jobs/' + job.id
responseTime: 0
responseTime: responseTime
responseText:
job: job,
commit: commits[job.commit_id - 1]
@ -74,7 +76,7 @@ for job in jobs
for artifact in artifacts
$.mockjax
url: '/artifacts/' + artifact.id
responseTime: 0
responseTime: responseTime
responseText:
artifact: artifact

View File

@ -1,7 +1,7 @@
describe 'The current build tab', ->
describe 'on the "index" state', ->
beforeEach ->
app '/'
app ''
waitFor buildRendered
it 'displays the build summary', ->
@ -19,13 +19,13 @@ describe 'The current build tab', ->
displaysBuildMatrix
headers: ['Job', 'Duration', 'Finished', 'Rvm']
jobs: [
{ number: '#1.1', repo: 'travis-ci/travis-core', finishedAt: /\d+ (\w+) ago/, duration: '35 sec', rvm: 'rbx' },
{ number: '#1.2', repo: 'travis-ci/travis-core', finishedAt: '-', duration: '-', rvm: '1.9.3' }
{ id: 1, number: '#1.1', repo: 'travis-ci/travis-core', finishedAt: /\d+ (\w+) ago/, duration: '35 sec', rvm: 'rbx' },
{ id: 2, number: '#1.2', repo: 'travis-ci/travis-core', finishedAt: '-', duration: '-', rvm: '1.9.3' }
]
describe 'on the "current" state', ->
beforeEach ->
app '/travis-ci/travis-core'
app '!/travis-ci/travis-core'
waitFor repositoriesRendered
waitFor buildRendered
@ -44,6 +44,6 @@ describe 'The current build tab', ->
displaysBuildMatrix
headers: ['Job', 'Duration', 'Finished', 'Rvm']
jobs: [
{ number: '#1.1', repo: 'travis-ci/travis-core', finishedAt: /\d+ (\w+) ago/, duration: '35 sec', rvm: 'rbx' },
{ number: '#1.2', repo: 'travis-ci/travis-core', finishedAt: '-', duration: '-', rvm: '1.9.3' }
{ id: 1, number: '#1.1', repo: 'travis-ci/travis-core', finishedAt: /\d+ (\w+) ago/, duration: '35 sec', rvm: 'rbx' },
{ id: 2, number: '#1.2', repo: 'travis-ci/travis-core', finishedAt: '-', duration: '-', rvm: '1.9.3' }
]

View File

@ -1,13 +1,13 @@
describe 'The repositories list', ->
beforeEach ->
app '/'
app ''
waitFor repositoriesRendered
it 'lists repositories', ->
href = $('#repositories a.slug').attr('href')
expect(href).toEqual '#/travis-ci/travis-core'
href = $('#repositories a.current').attr('href')
expect(href).toEqual '#!/travis-ci/travis-core'
it "links to the repository's last build action", ->
href = $('#repositories a.last_build').attr('href')
expect(href).toEqual '#/travis-ci/travis-core/builds/1'
expect(href).toEqual '#!/travis-ci/travis-core/builds/1'

View File

@ -1,6 +1,6 @@
describe 'The repository view', ->
beforeEach ->
app '/'
app ''
waitFor repositoriesRendered
it 'displays the repository header', ->

View File

@ -2,17 +2,13 @@ minispade.require 'app'
@reset = ->
Travis.app.destroy() if Travis.app
$('body #content').empty()
$('body #content').remove()
@app = (url) ->
router = Travis.Router.create
location: Em.NoneLocation.create()
Travis.app = Travis.App.create()
Travis.app.set('rootElement', '#content')
Travis.app.initialize(router)
router.route(url)
$('body').append('<div id="content"></div>')
Travis.app = Travis.App.create(rootElement: '#content')
Travis.app.initialize()
Em.routes.set('location', url)
beforeEach ->
reset()

View File

@ -1,5 +1,5 @@
@repositoriesRendered = ->
$('#repositories li').length > 0
$('#repositories li a.current').text() != ''
@buildRendered = ->
$('#build .summary .number').text() != ''

View File

@ -1,6 +1,6 @@
@displaysBuildSummary = (data) ->
element = $('#build .summary .number a')
expect(element.attr('href')).toEqual "#/#{data.repo}/builds/#{data.id}"
expect(element.attr('href')).toEqual "#!/#{data.repo}/builds/#{data.id}"
element = $('#build .summary .finished_at')
expect(element.text()).toMatch /\d+ (\w+) ago/
@ -34,7 +34,7 @@
expect(element.text()).toEqual job.number
element = $("#jobs tr:nth-child(#{ix}) td.number a")
expect(element.attr('href')).toEqual "#/#{job.repo}/jobs/#{job.id}"
expect(element.attr('href')).toEqual "#!/#{job.repo}/jobs/#{job.id}"
element = $("#jobs tr:nth-child(#{ix}) td.duration")
expect(element.text()).toEqual job.duration

View File

@ -1,23 +1,25 @@
# describe 'The tabs view', ->
# describe 'on the "index" state', ->
# beforeEach ->
# app '/'
# waitFor repositoriesRendered
#
# it 'has a "current" tab linking to the current build', ->
# href = $('#main .tabs a.current').attr('href')
# expect(href).toEqual '/travis-ci/travis-core'
#
# it 'has a "history" tab linking to the builds list', ->
# href = $('#main .tabs a.history').attr('href')
# expect(href).toEqual '/travis-ci/travis-core/builds'
#
# describe 'on the "current" state', ->
# app '/travis-ci/travis-core'
# waitFor repositoriesRendered
#
# it 'has a "current" tab linking to the current build', ->
# href = $('#main .tabs a.current').attr('href')
# expect(href).toEqual '/travis-ci/travis-core'
#
#
describe 'The tabs view', ->
describe 'on the "index" state', ->
beforeEach ->
app ''
waitFor repositoriesRendered
it 'has a "current" tab linking to the current build', ->
href = $('#main .tabs a.current').attr('href')
expect(href).toEqual '#!/travis-ci/travis-core'
it 'has a "history" tab linking to the builds list', ->
href = $('#main .tabs a.history').attr('href')
expect(href).toEqual '#!/travis-ci/travis-core/builds'
describe 'on the "current" state', ->
beforeEach ->
app '!/travis-ci/travis-core'
waitFor repositoriesRendered
waitFor buildRendered
it 'has a "current" tab linking to the current build', ->
href = $('#main .tabs a.current').attr('href')
expect(href).toEqual '#!/travis-ci/travis-core'

View File

@ -944,7 +944,7 @@ Ember.isArray = function(obj) {
Ember.makeArray(); => []
Ember.makeArray(null); => []
Ember.makeArray(undefined); => []
Ember.makeArray('lindsay'); => ['lindsay']
Ember.makeArray('lindsay'); => ['lindsay']
Ember.makeArray([1,2,42]); => [1,2,42]
var controller = Ember.ArrayProxy.create({ content: [] });
@ -3646,7 +3646,7 @@ Ember.RunLoop = RunLoop;
call.
Ember.run(function(){
// code to be execute within a RunLoop
// code to be execute within a RunLoop
});
@name run
@ -3684,7 +3684,7 @@ var run = Ember.run;
an lower-level way to use a RunLoop instead of using Ember.run().
Ember.run.begin();
// code to be execute within a RunLoop
// code to be execute within a RunLoop
Ember.run.end();
@ -3700,7 +3700,7 @@ Ember.run.begin = function() {
instead of using Ember.run().
Ember.run.begin();
// code to be execute within a RunLoop
// code to be execute within a RunLoop
Ember.run.end();
@returns {void}
@ -5448,7 +5448,7 @@ Ember.inspect = function(obj) {
/**
Compares two objects, returning true if they are logically equal. This is
a deeper comparison than a simple triple equal. For sets it will compare the
internal objects. For any other object that implements `isEqual()` it will
internal objects. For any other object that implements `isEqual()` it will
respect that method.
Ember.isEqual('hello', 'hello'); => true
@ -5630,7 +5630,7 @@ Ember.String = {
> beta
> gamma
@param {String} str
@param {String} str
The string to split
@returns {String} split string
@ -5639,7 +5639,7 @@ Ember.String = {
/**
Converts a camelized string into all lower case separated by underscores.
'innerHTML'.decamelize() => 'inner_html'
'action_name'.decamelize() => 'action_name'
'css-class-name'.decamelize() => 'css-class-name'
@ -5656,7 +5656,7 @@ Ember.String = {
/**
Replaces underscores or spaces with dashes.
'innerHTML'.dasherize() => 'inner-html'
'action_name'.dasherize() => 'action-name'
'css-class-name'.dasherize() => 'css-class-name'
@ -5823,7 +5823,7 @@ if (Ember.EXTEND_PROTOTYPES) {
/**
The `property` extension of Javascript's Function prototype is available
when Ember.EXTEND_PROTOTYPES is true, which is the default.
when Ember.EXTEND_PROTOTYPES is true, which is the default.
Computed properties allow you to treat a function like a property:
@ -5878,7 +5878,7 @@ if (Ember.EXTEND_PROTOTYPES) {
/**
The `observes` extension of Javascript's Function prototype is available
when Ember.EXTEND_PROTOTYPES is true, which is the default.
when Ember.EXTEND_PROTOTYPES is true, which is the default.
You can observe property changes simply by adding the `observes`
call to the end of your method declarations in classes that you write.
@ -5889,7 +5889,7 @@ if (Ember.EXTEND_PROTOTYPES) {
// Executes whenever the "value" property changes
}.observes('value')
});
@see Ember.Observable
*/
Function.prototype.observes = function() {
@ -5899,7 +5899,7 @@ if (Ember.EXTEND_PROTOTYPES) {
/**
The `observesBefore` extension of Javascript's Function prototype is
available when Ember.EXTEND_PROTOTYPES is true, which is the default.
available when Ember.EXTEND_PROTOTYPES is true, which is the default.
You can get notified when a property changes is about to happen by
by adding the `observesBefore` call to the end of your method
@ -5910,7 +5910,7 @@ if (Ember.EXTEND_PROTOTYPES) {
// Executes whenever the "value" property is about to change
}.observesBefore('value')
});
@see Ember.Observable
*/
Function.prototype.observesBefore = function() {
@ -6509,9 +6509,9 @@ Ember.Enumerable = Ember.Mixin.create(
/**
Returns a copy of the array with all null elements removed.
var arr = ["a", null, "c", null];
arr.compact(); => ["a", "c"]
arr.compact(); => ["a", "c"]
@returns {Array} the array without null elements.
*/
@ -7514,7 +7514,7 @@ Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable,
colors.clear(); => []
colors.length(); => 0
@returns {Ember.Array} An empty Array.
@returns {Ember.Array} An empty Array.
*/
clear: function () {
var len = get(this, 'length');
@ -7708,15 +7708,15 @@ var get = Ember.get, set = Ember.set;
@class
## Overview
This mixin provides properties and property observing functionality, core
features of the Ember object model.
Properties and observers allow one object to observe changes to a
property on another object. This is one of the fundamental ways that
models, controllers and views communicate with each other in an Ember
application.
Any object that has this mixin applied can be used in observer
operations. That includes Ember.Object and most objects you will
interact with as you write your Ember application.
@ -7724,16 +7724,16 @@ var get = Ember.get, set = Ember.set;
Note that you will not generally apply this mixin to classes yourself,
but you will use the features provided by this module frequently, so it
is important to understand how to use it.
## Using get() and set()
Because of Ember's support for bindings and observers, you will always
access properties using the get method, and set properties using the
set method. This allows the observing objects to be notified and
computed properties to be handled properly.
More documentation about `get` and `set` are below.
## Observing Property Changes
You typically observe property changes simply by adding the `observes`
@ -7745,7 +7745,7 @@ var get = Ember.get, set = Ember.set;
// Executes whenever the "value" property changes
}.observes('value')
});
Although this is the most common way to add an observer, this capability
is actually built into the Ember.Object class on top of two methods
defined in this mixin: `addObserver` and `removeObserver`. You can use
@ -7758,12 +7758,12 @@ var get = Ember.get, set = Ember.set;
This will call the `targetAction` method on the `targetObject` to be called
whenever the value of the `propertyKey` changes.
Note that if `propertyKey` is a computed property, the observer will be
called when any of the property dependencies are changed, even if the
Note that if `propertyKey` is a computed property, the observer will be
called when any of the property dependencies are changed, even if the
resulting value of the computed property is unchanged. This is necessary
because computed properties are not computed until `get` is called.
@extends Ember.Mixin
*/
Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
@ -7777,7 +7777,7 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
This method is usually similar to using object[keyName] or object.keyName,
however it supports both computed properties and the unknownProperty
handler.
Because `get` unifies the syntax for accessing all these kinds
of properties, it can make many refactorings easier, such as replacing a
simple property with a computed property, or vice versa.
@ -7973,11 +7973,11 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
Ember.propertyDidChange(this, keyName);
return this;
},
/**
Convenience method to call `propertyWillChange` and `propertyDidChange` in
succession.
@param {String} keyName The property key to be notified about.
@returns {Ember.Observable}
*/
@ -8069,7 +8069,7 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
This method will be called when a client attempts to get the value of a
property that has not been defined in one of the typical ways. Override
this method to create "virtual" properties.
@param {String} key The name of the unknown property that was requested.
@returns {Object} The property value or undefined. Default is undefined.
*/
@ -8081,7 +8081,7 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
This method will be called when a client attempts to set the value of a
property that has not been defined in one of the typical ways. Override
this method to create "virtual" properties.
@param {String} key The name of the unknown property to be set.
@param {Object} value The value the unknown property is to be set to.
*/
@ -8092,7 +8092,7 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
/**
This is like `get`, but allows you to pass in a dot-separated property
path.
person.getPath('address.zip'); // return the zip
person.getPath('children.firstObject.age'); // return the first kid's age
@ -8108,7 +8108,7 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
/**
This is like `set`, but allows you to specify the property you want to
set as a dot-separated property path.
person.setPath('address.zip', 10011); // set the zip to 10011
person.setPath('children.firstObject.age', 6); // set the first kid's age to 6
@ -8126,9 +8126,9 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
/**
Retrieves the value of a property, or a default value in the case that the property
returns undefined.
person.getWithDefault('lastName', 'Doe');
@param {String} keyName The name of the property to retrieve
@param {Object} defaultValue The value to return if the property value is undefined
@returns {Object} The property value or the defaultValue.
@ -8139,10 +8139,10 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
/**
Set the value of a property to the current value plus some amount.
person.incrementProperty('age');
team.incrementProperty('score', 2);
@param {String} keyName The name of the property to increment
@param {Object} increment The amount to increment by. Defaults to 1
@returns {Object} The new property value
@ -8152,13 +8152,13 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
set(this, keyName, (get(this, keyName) || 0)+increment);
return get(this, keyName);
},
/**
Set the value of a property to the current value minus some amount.
player.decrementProperty('lives');
orc.decrementProperty('health', 5);
@param {String} keyName The name of the property to decrement
@param {Object} increment The amount to decrement by. Defaults to 1
@returns {Object} The new property value
@ -8172,9 +8172,9 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
/**
Set the value of a boolean property to the opposite of it's
current value.
starship.toggleProperty('warpDriveEnaged');
@param {String} keyName The name of the property to toggle
@returns {Object} The new property value
*/
@ -11603,7 +11603,7 @@ var invokeForState = {
`Ember.View` is the class in Ember responsible for encapsulating templates of HTML
content, combining templates with data to render as sections of a page's DOM, and
registering and responding to user-initiated events.
## HTML Tag
The default HTML tag name used for a view's DOM representation is `div`. This can be
customized by setting the `tagName` property. The following view class:
@ -11629,7 +11629,7 @@ var invokeForState = {
<div id="ember1" class="ember-view my-class my-other-class"></div>
`class` attribute values can also be set by providing a `classNameBindings` property
set to an array of properties names for the view. The return value of these properties
set to an array of properties names for the view. The return value of these properties
will be added as part of the value for the view's `class` attribute. These properties
can be computed properties:
@ -11658,7 +11658,7 @@ var invokeForState = {
<div id="ember1" class="ember-view hovered"></div>
When using boolean class name bindings you can supply a string value other than the
When using boolean class name bindings you can supply a string value other than the
property name for use as the `class` HTML attribute by appending the preferred value after
a ":" character when defining the binding:
@ -11699,11 +11699,11 @@ var invokeForState = {
<div id="ember1" class="ember-view empty"></div>
Updates to the the value of a class name binding will result in automatic update
Updates to the the value of a class name binding will result in automatic update
of the HTML `class` attribute in the view's rendered HTML representation.
If the value becomes `false` or `undefined` the class name will be removed.
Both `classNames` and `classNameBindings` are concatenated properties.
Both `classNames` and `classNameBindings` are concatenated properties.
See `Ember.Object` documentation for more information about concatenated properties.
## HTML Attributes
@ -11749,7 +11749,7 @@ var invokeForState = {
}.property()
})
Updates to the the property of an attribute binding will result in automatic update
Updates to the the property of an attribute binding will result in automatic update
of the HTML attribute in the view's rendered HTML representation.
`attributeBindings` is a concatenated property. See `Ember.Object` documentation
@ -11840,7 +11840,7 @@ var invokeForState = {
primary templates, layouts can be any function that accepts an optional context
parameter and returns a string of HTML that will be inserted inside view's tag. Views whose HTML
element is self closing (e.g. `<input />`) cannot have a layout and this property will be ignored.
Most typically in Ember a layout will be a compiled Ember.Handlebars template.
A view's layout can be set directly with the `layout` property or reference an
@ -11865,7 +11865,7 @@ var invokeForState = {
See `Handlebars.helpers.yield` for more information.
## Responding to Browser Events
Views can respond to user-initiated events in one of three ways: method implementation,
Views can respond to user-initiated events in one of three ways: method implementation,
through an event manager, and through `{{action}}` helper use in their template or layout.
### Method Implementation
@ -11882,8 +11882,8 @@ var invokeForState = {
### Event Managers
Views can define an object as their `eventManager` property. This object can then
implement methods that match the desired event names. Matching events that occur
on the view's rendered HTML or the rendered HTML of any of its DOM descendants
will trigger this method. A `jQuery.Event` object will be passed as the first
on the view's rendered HTML or the rendered HTML of any of its DOM descendants
will trigger this method. A `jQuery.Event` object will be passed as the first
argument to the method and an `Ember.View` object as the second. The `Ember.View`
will be the view whose rendered HTML was interacted with. This may be the view with
the `eventManager` property or one of its descendent views.
@ -11917,7 +11917,7 @@ var invokeForState = {
Similarly a view's event manager will take precedence for events of any views
rendered as a descendent. A method name that matches an event name will not be called
if the view instance was rendered inside the HTML representation of a view that has
if the view instance was rendered inside the HTML representation of a view that has
an `eventManager` property defined that handles events of the name. Events not handled
by the event manager will still trigger method calls on the descendent.
@ -11939,7 +11939,7 @@ var invokeForState = {
// eventManager doesn't handle click events
},
mouseEnter: function(event){
// will never be called if rendered inside
// will never be called if rendered inside
// an OuterView.
}
})
@ -11960,7 +11960,7 @@ var invokeForState = {
Form events: 'submit', 'change', 'focusIn', 'focusOut', 'input'
HTML5 drag and drop events: 'dragStart', 'drag', 'dragEnter', 'dragLeave', 'drop', 'dragEnd'
## Handlebars `{{view}}` Helper
Other `Ember.View` instances can be included as part of a view's template by using the `{{view}}`
Handlebars helper. See `Handlebars.helpers.view` for additional information.
@ -14338,7 +14338,7 @@ var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt;
@class
`Ember.CollectionView` is an `Ember.View` descendent responsible for managing a
collection (an array or array-like object) by maintaing a child view object and
collection (an array or array-like object) by maintaing a child view object and
associated DOM representation for each item in the array and ensuring that child
views and their associated rendered HTML are updated when items in the array
are added, removed, or replaced.
@ -14382,7 +14382,7 @@ var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt;
## Automatic matching of parent/child tagNames
Setting the `tagName` property of a `CollectionView` to any of
Setting the `tagName` property of a `CollectionView` to any of
"ul", "ol", "table", "thead", "tbody", "tfoot", "tr", or "select" will result
in the item views receiving an appropriately matched `tagName` property.
@ -15229,15 +15229,15 @@ var arrayForEach = Ember.ArrayPolyfills.forEach;
robotManager.getPath('currentState.name') // 'rampaging'
Transition actions can also be created using the `transitionTo` method of the Ember.State class. The
following example StateManagers are equivalent:
following example StateManagers are equivalent:
aManager = Ember.StateManager.create({
stateOne: Ember.State.create({
changeToStateTwo: Ember.State.transitionTo('stateTwo')
}),
stateTwo: Ember.State.create({})
})
bManager = Ember.StateManager.create({
stateOne: Ember.State.create({
changeToStateTwo: function(manager, context){
@ -15318,7 +15318,7 @@ Ember.StateManager = Ember.State.extend(
@default true
*/
errorOnUnhandledEvent: true,
send: function(event, context) {
Ember.assert('Cannot send event "' + event + '" while currentState is ' + get(this, 'currentState'), get(this, 'currentState'));
if (arguments.length === 1) { context = {}; }
@ -18396,7 +18396,7 @@ EmberHandlebars.ViewHelper = Ember.Object.create({
Will result in HTML structure:
<body>
<!-- Note: the handlebars template script
<!-- Note: the handlebars template script
also results in a rendered Ember.View
which is the outer <div> here -->
@ -18418,7 +18418,7 @@ EmberHandlebars.ViewHelper = Ember.Object.create({
})
aView.appendTo('body')
Will result in HTML structure:
<div id="ember1" class="ember-view">
@ -18492,7 +18492,7 @@ EmberHandlebars.ViewHelper = Ember.Object.create({
Will result in the following HTML:
<div id="ember1" class="ember-view">
<div id="ember2" class="ember-view a-custom-view-class-as-property">
<div id="ember2" class="ember-view a-custom-view-class-as-property">
hi
</div>
</div>
@ -18652,7 +18652,7 @@ var get = Ember.get, getPath = Ember.Handlebars.getPath, fmt = Ember.String.fmt;
<p class="ember-view greeting">Howdy Mary</p>
<p class="ember-view greeting">Howdy Sara</p>
</div>
@name Handlebars.helpers.collection
@param {String} path
@param {Hash} options
@ -19266,7 +19266,7 @@ var set = Ember.set, get = Ember.get;
/**
@class
Creates an HTML input of type 'checkbox' with HTML related properties
Creates an HTML input of type 'checkbox' with HTML related properties
applied directly to the input.
{{view Ember.Checkbox classNames="applicaton-specific-checkbox"}}
@ -19285,7 +19285,7 @@ var set = Ember.set, get = Ember.get;
through the Ember object or by interacting with its rendered element representation
via the mouse, keyboard, or touch. Updating the value of the checkbox via jQuery will
result in the checked value of the object and its element losing synchronization.
## Layout and LayoutName properties
Because HTML `input` elements are self closing `layout` and `layoutName` properties will
not be applied. See `Ember.View`'s layout section for more information.
@ -19397,7 +19397,7 @@ var get = Ember.get, set = Ember.set;
## Layout and LayoutName properties
Because HTML `input` elements are self closing `layout` and `layoutName` properties will
not be applied. See `Ember.View`'s layout section for more information.
@extends Ember.TextSupport
*/
Ember.TextField = Ember.View.extend(Ember.TextSupport,
@ -19574,7 +19574,7 @@ var get = Ember.get, set = Ember.set;
## Layout and LayoutName properties
Because HTML `textarea` elements do not contain inner HTML the `layout` and `layoutName`
Because HTML `textarea` elements do not contain inner HTML the `layout` and `layoutName`
properties will not be applied. See `Ember.View`'s layout section for more information.
@extends Ember.TextSupport

546
assets/javascripts/vendor/sc-routes.js vendored Normal file
View File

@ -0,0 +1,546 @@
// ==========================================================================
// Project: SproutCore - JavaScript Application Framework
// Copyright: ©2006-2011 Strobe Inc. and contributors.
// Portions ©2008-2011 Apple Inc. All rights reserved.
// License: Licensed under MIT license (see license.js)
// ==========================================================================
var get = Ember.get, set = Ember.set;
/**
Wether the browser supports HTML5 history.
*/
var supportsHistory = !!(window.history && window.history.pushState);
/**
Wether the browser supports the hashchange event.
*/
var supportsHashChange = ('onhashchange' in window) && (document.documentMode === undefined || document.documentMode > 7);
/**
@class
Route is a class used internally by Ember.routes. The routes defined by your
application are stored in a tree structure, and this is the class for the
nodes.
*/
var Route = Ember.Object.extend(
/** @scope Route.prototype */ {
target: null,
method: null,
staticRoutes: null,
dynamicRoutes: null,
wildcardRoutes: null,
add: function(parts, target, method) {
var part, nextRoute;
// clone the parts array because we are going to alter it
parts = Ember.copy(parts);
if (!parts || parts.length === 0) {
this.target = target;
this.method = method;
} else {
part = parts.shift();
// there are 3 types of routes
switch (part.slice(0, 1)) {
// 1. dynamic routes
case ':':
part = part.slice(1, part.length);
if (!this.dynamicRoutes) this.dynamicRoutes = {};
if (!this.dynamicRoutes[part]) this.dynamicRoutes[part] = this.constructor.create();
nextRoute = this.dynamicRoutes[part];
break;
// 2. wildcard routes
case '*':
part = part.slice(1, part.length);
if (!this.wildcardRoutes) this.wildcardRoutes = {};
nextRoute = this.wildcardRoutes[part] = this.constructor.create();
break;
// 3. static routes
default:
if (!this.staticRoutes) this.staticRoutes = {};
if (!this.staticRoutes[part]) this.staticRoutes[part] = this.constructor.create();
nextRoute = this.staticRoutes[part];
}
// recursively add the rest of the route
if (nextRoute) nextRoute.add(parts, target, method);
}
},
routeForParts: function(parts, params) {
var part, key, route;
// clone the parts array because we are going to alter it
parts = Ember.copy(parts);
// if parts is empty, we are done
if (!parts || parts.length === 0) {
return this.method ? this : null;
} else {
part = parts.shift();
// try to match a static route
if (this.staticRoutes && this.staticRoutes[part]) {
return this.staticRoutes[part].routeForParts(parts, params);
} else {
// else, try to match a dynamic route
for (key in this.dynamicRoutes) {
route = this.dynamicRoutes[key].routeForParts(parts, params);
if (route) {
params[key] = part;
return route;
}
}
// else, try to match a wilcard route
for (key in this.wildcardRoutes) {
parts.unshift(part);
params[key] = parts.join('/');
return this.wildcardRoutes[key].routeForParts(null, params);
}
// if nothing was found, it means that there is no match
return null;
}
}
}
});
/**
@class
Ember.routes manages the browser location. You can change the hash part of the
current location. The following code
Ember.routes.set('location', 'notes/edit/4');
will change the location to http://domain.tld/my_app#notes/edit/4. Adding
routes will register a handler that will be called whenever the location
changes and matches the route:
Ember.routes.add(':controller/:action/:id', MyApp, MyApp.route);
You can pass additional parameters in the location hash that will be relayed
to the route handler:
Ember.routes.set('location', 'notes/show/4?format=xml&language=fr');
The syntax for the location hash is described in the location property
documentation, and the syntax for adding handlers is described in the
add method documentation.
Browsers keep track of the locations in their history, so when the user
presses the 'back' or 'forward' button, the location is changed, Ember.route
catches it and calls your handler. Except for Internet Explorer versions 7
and earlier, which do not modify the history stack when the location hash
changes.
Ember.routes also supports HTML5 history, which uses a '/' instead of a '#'
in the URLs, so that all your website's URLs are consistent.
*/
var routes = Ember.routes = Ember.Object.create(
/** @scope Ember.routes.prototype */{
/**
Set this property to true if you want to use HTML5 history, if available on
the browser, instead of the location hash.
HTML 5 history uses the history.pushState method and the window's popstate
event.
By default it is false, so your URLs will look like:
http://domain.tld/my_app#notes/edit/4
If set to true and the browser supports pushState(), your URLs will look
like:
http://domain.tld/my_app/notes/edit/4
You will also need to make sure that baseURI is properly configured, as
well as your server so that your routes are properly pointing to your
SproutCore application.
@see http://dev.w3.org/html5/spec/history.html#the-history-interface
@property
@type {Boolean}
*/
wantsHistory: false,
/**
A read-only boolean indicating whether or not HTML5 history is used. Based
on the value of wantsHistory and the browser's support for pushState.
@see wantsHistory
@property
@type {Boolean}
*/
usesHistory: null,
/**
The base URI used to resolve routes (which are relative URLs). Only used
when usesHistory is equal to true.
The build tools automatically configure this value if you have the
html5_history option activated in the Buildfile:
config :my_app, :html5_history => true
Alternatively, it uses by default the value of the href attribute of the
<base> tag of the HTML document. For example:
<base href="http://domain.tld/my_app">
The value can also be customized before or during the exectution of the
main() method.
@see http://www.w3.org/TR/html5/semantics.html#the-base-element
@property
@type {String}
*/
baseURI: document.baseURI,
/** @private
A boolean value indicating whether or not the ping method has been called
to setup the Ember.routes.
@property
@type {Boolean}
*/
_didSetup: false,
/** @private
Internal representation of the current location hash.
@property
@type {String}
*/
_location: null,
/** @private
Routes are stored in a tree structure, this is the root node.
@property
@type {Route}
*/
_firstRoute: null,
/** @private
An internal reference to the Route class.
@property
*/
_Route: Route,
/** @private
Internal method used to extract and merge the parameters of a URL.
@returns {Hash}
*/
_extractParametersAndRoute: function(obj) {
var params = {},
route = obj.route || '',
separator, parts, i, len, crumbs, key;
separator = (route.indexOf('?') < 0 && route.indexOf('&') >= 0) ? '&' : '?';
parts = route.split(separator);
route = parts[0];
if (parts.length === 1) {
parts = [];
} else if (parts.length === 2) {
parts = parts[1].split('&');
} else if (parts.length > 2) {
parts.shift();
}
// extract the parameters from the route string
len = parts.length;
for (i = 0; i < len; ++i) {
crumbs = parts[i].split('=');
params[crumbs[0]] = crumbs[1];
}
// overlay any parameter passed in obj
for (key in obj) {
if (obj.hasOwnProperty(key) && key !== 'route') {
params[key] = '' + obj[key];
}
}
// build the route
parts = [];
for (key in params) {
parts.push([key, params[key]].join('='));
}
params.params = separator + parts.join('&');
params.route = route;
return params;
},
/**
The current location hash. It is the part in the browser's location after
the '#' mark.
The following code
Ember.routes.set('location', 'notes/edit/4');
will change the location to http://domain.tld/my_app#notes/edit/4 and call
the correct route handler if it has been registered with the add method.
You can also pass additional parameters. They will be relayed to the route
handler. For example, the following code
Ember.routes.add(':controller/:action/:id', MyApp, MyApp.route);
Ember.routes.set('location', 'notes/show/4?format=xml&language=fr');
will change the location to
http://domain.tld/my_app#notes/show/4?format=xml&language=fr and call the
MyApp.route method with the following argument:
{ route: 'notes/show/4',
params: '?format=xml&language=fr',
controller: 'notes',
action: 'show',
id: '4',
format: 'xml',
language: 'fr' }
The location can also be set with a hash, the following code
Ember.routes.set('location',
{ route: 'notes/edit/4', format: 'xml', language: 'fr' });
will change the location to
http://domain.tld/my_app#notes/show/4?format=xml&language=fr.
The 'notes/show/4&format=xml&language=fr' syntax for passing parameters,
using a '&' instead of a '?', as used in SproutCore 1.0 is still supported.
@property
@type {String}
*/
location: function(key, value) {
this._skipRoute = false;
return this._extractLocation(key, value);
}.property(),
_extractLocation: function(key, value) {
var crumbs, encodedValue;
if (value !== undefined) {
if (value === null) {
value = '';
}
if (typeof(value) === 'object') {
crumbs = this._extractParametersAndRoute(value);
value = crumbs.route + crumbs.params;
}
if (!Ember.empty(value) || (this._location && this._location !== value)) {
encodedValue = encodeURI(value);
if (this.usesHistory) {
if (encodedValue.length > 0) {
encodedValue = '/' + encodedValue;
}
window.history.pushState(null, null, get(this, 'baseURI') + encodedValue);
} else {
window.location.hash = encodedValue;
}
}
this._location = value;
}
return this._location;
},
/**
You usually don't need to call this method. It is done automatically after
the application has been initialized.
It registers for the hashchange event if available. If not, it creates a
timer that looks for location changes every 150ms.
*/
ping: function() {
var that;
if (!this._didSetup) {
this._didSetup = true;
if (get(this, 'wantsHistory') && supportsHistory) {
this.usesHistory = true;
popState();
jQuery(window).bind('popstate', popState);
} else {
this.usesHistory = false;
if (supportsHashChange) {
hashChange();
jQuery(window).bind('hashchange', hashChange);
} else {
// we don't use a Ember.Timer because we don't want
// a run loop to be triggered at each ping
that = this;
this._invokeHashChange = function() {
that.hashChange();
setTimeout(that._invokeHashChange, 100);
};
this._invokeHashChange();
}
}
}
},
/**
Adds a route handler. Routes have the following format:
- 'users/show/5' is a static route and only matches this exact string,
- ':action/:controller/:id' is a dynamic route and the handler will be
called with the 'action', 'controller' and 'id' parameters passed in a
hash,
- '*url' is a wildcard route, it matches the whole route and the handler
will be called with the 'url' parameter passed in a hash.
Route types can be combined, the following are valid routes:
- 'users/:action/:id'
- ':controller/show/:id'
- ':controller/ *url' (ignore the space, because of jslint)
@param {String} route the route to be registered
@param {Object} target the object on which the method will be called, or
directly the function to be called to handle the route
@param {Function} method the method to be called on target to handle the
route, can be a function or a string
*/
add: function(route, target, method) {
if (!this._didSetup) {
Ember.run.once(this, 'ping');
}
if (method === undefined && Ember.typeOf(target) === 'function') {
method = target;
target = null;
} else if (Ember.typeOf(method) === 'string') {
method = target[method];
}
if (!this._firstRoute) this._firstRoute = Route.create();
this._firstRoute.add(route.split('/'), target, method);
return this;
},
/**
Observer of the 'location' property that calls the correct route handler
when the location changes.
*/
locationDidChange: function() {
this.trigger();
}.observes('location'),
/**
Triggers a route even if already in that route (does change the location, if it
is not already changed, as well).
If the location is not the same as the supplied location, this simply lets "location"
handle it (which ends up coming back to here).
*/
trigger: function() {
var location = get(this, 'location'),
params, route;
if (this._firstRoute) {
params = this._extractParametersAndRoute({ route: location });
location = params.route;
delete params.route;
delete params.params;
route = this.getRoute(location, params);
if (route && route.method) {
route.method.call(route.target || this, params);
}
}
},
getRoute: function(route, params) {
var firstRoute = this._firstRoute;
if (params == null) {
params = {}
}
return firstRoute.routeForParts(route.split('/'), params);
},
exists: function(route, params) {
route = this.getRoute(route, params);
return route != null && route.method != null;
}
});
/**
Event handler for the hashchange event. Called automatically by the browser
if it supports the hashchange event, or by our timer if not.
*/
function hashChange(event) {
var loc = window.location.hash;
// Remove the '#' prefix
loc = (loc && loc.length > 0) ? loc.slice(1, loc.length) : '';
if (!jQuery.browser.mozilla) {
// because of bug https://bugzilla.mozilla.org/show_bug.cgi?id=483304
loc = decodeURI(loc);
}
if (get(routes, 'location') !== loc && !routes._skipRoute) {
Ember.run.once(function() {
set(routes, 'location', loc);
});
}
routes._skipRoute = false;
}
function popState(event) {
var base = get(routes, 'baseURI'),
loc = document.location.href;
if (loc.slice(0, base.length) === base) {
// Remove the base prefix and the extra '/'
loc = loc.slice(base.length + 1, loc.length);
if (get(routes, 'location') !== loc && !routes._skipRoute) {
Ember.run.once(function() {
set(routes, 'location', loc);
});
}
}
routes._skipRoute = false;
}

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,7 @@
(function() {
var artifact, artifacts, build, builds, commits, id, job, jobs, repositories, repository, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m;
var artifact, artifacts, build, builds, commits, id, job, jobs, repositories, repository, responseTime, _i, _j, _k, _l, _len, _len1, _len2, _len3, _len4, _m;
responseTime = 0;
repositories = [
{
@ -33,7 +35,7 @@
builds = [
{
id: 1,
repository_id: 'travis-ci/travis-core',
repository_id: '1',
commit_id: 1,
job_ids: [1, 2],
number: 1,
@ -46,7 +48,7 @@
result: 0
}, {
id: 2,
repository_id: 'travis-ci/travis-core',
repository_id: '1',
commit_id: 2,
job_ids: [3],
number: 2,
@ -56,7 +58,7 @@
}
}, {
id: 3,
repository_id: 'travis-ci/travis-assets',
repository_id: '2',
commit_id: 3,
job_ids: [4],
number: 3,
@ -69,7 +71,7 @@
result: 0
}, {
id: 4,
repository_id: 'travis-ci/travis-hub',
repository_id: '3',
commit_id: 4,
job_ids: [5],
number: 4,
@ -197,7 +199,7 @@
$.mockjax({
url: '/repositories',
responseTime: 0,
responseTime: responseTime,
responseText: {
repositories: repositories
}
@ -207,7 +209,7 @@
repository = repositories[_i];
$.mockjax({
url: '/' + repository.slug,
responseTime: 0,
responseTime: responseTime,
responseText: {
repository: repository
}
@ -218,7 +220,7 @@
build = builds[_j];
$.mockjax({
url: '/builds/' + build.id,
responseTime: 0,
responseTime: responseTime,
responseText: {
build: build,
commit: commits[build.commit_id - 1],
@ -241,11 +243,11 @@
$.mockjax({
url: '/builds',
data: {
repository_id: 1,
repository_id: repository.id,
event_type: 'push',
orderBy: 'number DESC'
},
responseTime: 0,
responseTime: responseTime,
responseText: {
builds: (function() {
var _l, _len3, _ref, _results;
@ -275,7 +277,7 @@
job = jobs[_l];
$.mockjax({
url: '/jobs/' + job.id,
responseTime: 0,
responseTime: responseTime,
responseText: {
job: job,
commit: commits[job.commit_id - 1]
@ -287,7 +289,7 @@
artifact = artifacts[_m];
$.mockjax({
url: '/artifacts/' + artifact.id,
responseTime: 0,
responseTime: responseTime,
responseText: {
artifact: artifact
}

View File

@ -3,7 +3,7 @@
describe('The current build tab', function() {
describe('on the "index" state', function() {
beforeEach(function() {
app('/');
app('');
return waitFor(buildRendered);
});
it('displays the build summary', function() {
@ -23,12 +23,14 @@
headers: ['Job', 'Duration', 'Finished', 'Rvm'],
jobs: [
{
id: 1,
number: '#1.1',
repo: 'travis-ci/travis-core',
finishedAt: /\d+ (\w+) ago/,
duration: '35 sec',
rvm: 'rbx'
}, {
id: 2,
number: '#1.2',
repo: 'travis-ci/travis-core',
finishedAt: '-',
@ -42,7 +44,7 @@
});
return describe('on the "current" state', function() {
beforeEach(function() {
app('/travis-ci/travis-core');
app('!/travis-ci/travis-core');
waitFor(repositoriesRendered);
return waitFor(buildRendered);
});
@ -63,12 +65,14 @@
headers: ['Job', 'Duration', 'Finished', 'Rvm'],
jobs: [
{
id: 1,
number: '#1.1',
repo: 'travis-ci/travis-core',
finishedAt: /\d+ (\w+) ago/,
duration: '35 sec',
rvm: 'rbx'
}, {
id: 2,
number: '#1.2',
repo: 'travis-ci/travis-core',
finishedAt: '-',
@ -87,18 +91,18 @@
describe('The repositories list', function() {
beforeEach(function() {
app('/');
app('');
return waitFor(repositoriesRendered);
});
it('lists repositories', function() {
var href;
href = $('#repositories a.slug').attr('href');
return expect(href).toEqual('#/travis-ci/travis-core');
href = $('#repositories a.current').attr('href');
return expect(href).toEqual('#!/travis-ci/travis-core');
});
return it("links to the repository's last build action", function() {
var href;
href = $('#repositories a.last_build').attr('href');
return expect(href).toEqual('#/travis-ci/travis-core/builds/1');
return expect(href).toEqual('#!/travis-ci/travis-core/builds/1');
});
});
@ -107,7 +111,7 @@
describe('The repository view', function() {
beforeEach(function() {
app('/');
app('');
return waitFor(repositoriesRendered);
});
return it('displays the repository header', function() {
@ -126,18 +130,16 @@
if (Travis.app) {
Travis.app.destroy();
}
return $('body #content').empty();
return $('body #content').remove();
};
this.app = function(url) {
var router;
router = Travis.Router.create({
location: Em.NoneLocation.create()
$('body').append('<div id="content"></div>');
Travis.app = Travis.App.create({
rootElement: '#content'
});
Travis.app = Travis.App.create();
Travis.app.set('rootElement', '#content');
Travis.app.initialize(router);
return router.route(url);
Travis.app.initialize();
return Em.routes.set('location', url);
};
beforeEach(function() {
@ -148,7 +150,7 @@
(function() {
this.repositoriesRendered = function() {
return $('#repositories li').length > 0;
return $('#repositories li a.current').text() !== '';
};
this.buildRendered = function() {
@ -165,7 +167,7 @@
this.displaysBuildSummary = function(data) {
var element;
element = $('#build .summary .number a');
expect(element.attr('href')).toEqual("#/" + data.repo + "/builds/" + data.id);
expect(element.attr('href')).toEqual("#!/" + data.repo + "/builds/" + data.id);
element = $('#build .summary .finished_at');
expect(element.text()).toMatch(/\d+ (\w+) ago/);
element = $('#build .summary .duration');
@ -200,7 +202,7 @@
element = $("#jobs tr:nth-child(" + ix + ") td.number");
expect(element.text()).toEqual(job.number);
element = $("#jobs tr:nth-child(" + ix + ") td.number a");
expect(element.attr('href')).toEqual("#/" + job.repo + "/jobs/" + job.id);
expect(element.attr('href')).toEqual("#!/" + job.repo + "/jobs/" + job.id);
element = $("#jobs tr:nth-child(" + ix + ") td.duration");
expect(element.text()).toEqual(job.duration);
element = $("#jobs tr:nth-child(" + ix + ") td.finished_at");
@ -232,6 +234,35 @@
}).call(this);
(function() {
describe('The tabs view', function() {
describe('on the "index" state', function() {
beforeEach(function() {
app('');
return waitFor(repositoriesRendered);
});
it('has a "current" tab linking to the current build', function() {
var href;
href = $('#main .tabs a.current').attr('href');
return expect(href).toEqual('#!/travis-ci/travis-core');
});
return it('has a "history" tab linking to the builds list', function() {
var href;
href = $('#main .tabs a.history').attr('href');
return expect(href).toEqual('#!/travis-ci/travis-core/builds');
});
});
return describe('on the "current" state', function() {
beforeEach(function() {
app('!/travis-ci/travis-core');
waitFor(repositoriesRendered);
return waitFor(buildRendered);
});
return it('has a "current" tab linking to the current build', function() {
var href;
href = $('#main .tabs a.current').attr('href');
return expect(href).toEqual('#!/travis-ci/travis-core');
});
});
});
}).call(this);

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,6 @@
<script src="javascripts/specs/specs.js"></script>
</head>
<body>
<div id="content"></div>
<script>
for(key in minispade.modules)
if(key.match(/_spec$/))