go back to sc-router
This commit is contained in:
parent
948807eedd
commit
50ff39d5c0
|
@ -16,6 +16,7 @@ input 'assets/javascripts' do
|
||||||
vendor/ansiparse.js
|
vendor/ansiparse.js
|
||||||
vendor/i18n.js
|
vendor/i18n.js
|
||||||
vendor/jquery.timeago.js
|
vendor/jquery.timeago.js
|
||||||
|
vendor/sc-routes.js
|
||||||
)
|
)
|
||||||
concat files, 'vendor.js'
|
concat files, 'vendor.js'
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,16 +1,54 @@
|
||||||
$.mockjaxSettings.log = false
|
require 'hax0rs'
|
||||||
|
|
||||||
|
# $.mockjaxSettings.log = false
|
||||||
|
|
||||||
@Travis = Em.Namespace.create
|
@Travis = Em.Namespace.create
|
||||||
|
run: ->
|
||||||
|
@app = Travis.App.create(this)
|
||||||
|
@app.initialize()
|
||||||
|
|
||||||
App: Em.Application.extend
|
App: Em.Application.extend
|
||||||
initialize: (router) ->
|
initialize: (router) ->
|
||||||
$.extend(this, Travis.Controllers)
|
# ember wants this dependencies setup for connectOutlet
|
||||||
$.extend(this, Travis.Views)
|
$.extend this, Travis.Controllers
|
||||||
@store = Travis.Store.create()
|
$.extend this, Travis.Views
|
||||||
@_super(router || Travis.Router.create())
|
for name, controller of Travis.Controllers
|
||||||
|
name = name.charAt(0).toLowerCase() + name.substr(1)
|
||||||
|
this[name] = controller.create(namespace: this, controllers: this)
|
||||||
|
|
||||||
run: ->
|
@store = Travis.Store.create()
|
||||||
@app = Travis.App.create()
|
@routes = Travis.Router.create(app: this)
|
||||||
@app.initialize()
|
|
||||||
|
@_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'
|
require 'ext/jquery'
|
||||||
|
|
|
@ -1,35 +1,50 @@
|
||||||
@Travis.Urls =
|
@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:
|
Repository:
|
||||||
urlGithub: (->
|
urlGithub: (->
|
||||||
'http://github.com/%@'.fmt @get('slug')
|
"http://github.com/#{@get('slug')}"
|
||||||
).property('slug'),
|
).property('slug'),
|
||||||
|
|
||||||
urlGithubWatchers: (->
|
urlGithubWatchers: (->
|
||||||
'http://github.com/%@/watchers'.fmt @get('slug')
|
"http://github.com/#{@get('slug')}/watchers"
|
||||||
).property('slug'),
|
).property('slug'),
|
||||||
|
|
||||||
urlGithubNetwork: (->
|
urlGithubNetwork: (->
|
||||||
'http://github.com/%@/network'.fmt @get('slug')
|
"http://github.com/#{@get('slug')}/network"
|
||||||
).property('slug'),
|
).property('slug'),
|
||||||
|
|
||||||
urlGithubAdmin: (->
|
urlGithubAdmin: (->
|
||||||
'http://github.com/%@/admin/hooks#travis_minibucket'.fmt @get('slug')
|
"http://github.com/#{@get('slug')}/admin/hooks#travis_minibucket"
|
||||||
).property('slug')
|
).property('slug')
|
||||||
|
|
||||||
statusImage: (->
|
statusImage: (->
|
||||||
'%@.png'.fmt @get('slug')
|
"#{@get('slug')}.png"
|
||||||
).property('slug')
|
).property('slug')
|
||||||
|
|
||||||
Commit:
|
Commit:
|
||||||
urlGithubCommit: (->
|
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')
|
).property('repository.slug', 'commit.sha')
|
||||||
|
|
||||||
urlAuthor: (->
|
urlAuthor: (->
|
||||||
'mailto:%@'.fmt @getPath('commit.authorEmail')
|
"mailto:#{@getPath('commit.authorEmail')}"
|
||||||
).property()
|
).property('commit.authorEmail')
|
||||||
|
|
||||||
urlCommitter: (->
|
urlCommitter: (->
|
||||||
'mailto:%@'.fmt @getPath('commit.committerEmail')
|
"mailto:#{@getPath('commit.committerEmail')}"
|
||||||
).property()
|
).property('commit.committerEmail')
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@ require 'travis/model'
|
||||||
|
|
||||||
@Travis.Repository = Travis.Model.extend
|
@Travis.Repository = Travis.Model.extend
|
||||||
slug: DS.attr('string')
|
slug: DS.attr('string')
|
||||||
|
owner: DS.attr('string')
|
||||||
|
name: DS.attr('string')
|
||||||
description: DS.attr('string')
|
description: DS.attr('string')
|
||||||
lastBuildId: DS.attr('number')
|
lastBuildId: DS.attr('number')
|
||||||
lastBuildNumber: DS.attr('string')
|
lastBuildNumber: DS.attr('string')
|
||||||
|
@ -9,8 +11,6 @@ require 'travis/model'
|
||||||
lastBuildStarted_at: DS.attr('string')
|
lastBuildStarted_at: DS.attr('string')
|
||||||
lastBuildFinished_at: DS.attr('string')
|
lastBuildFinished_at: DS.attr('string')
|
||||||
|
|
||||||
primaryKey: 'slug'
|
|
||||||
|
|
||||||
lastBuild: DS.belongsTo('Travis.Build')
|
lastBuild: DS.belongsTo('Travis.Build')
|
||||||
|
|
||||||
builds: (->
|
builds: (->
|
||||||
|
@ -21,14 +21,6 @@ require 'travis/model'
|
||||||
Travis.Build.byRepositoryId @get('id'), event_type: 'pull_request'
|
Travis.Build.byRepositoryId @get('id'), event_type: 'pull_request'
|
||||||
).property()
|
).property()
|
||||||
|
|
||||||
owner: (->
|
|
||||||
(@get('slug') || @_id).split('/')[0]
|
|
||||||
).property('owner', 'name'),
|
|
||||||
|
|
||||||
name: (->
|
|
||||||
(@get('slug') || @_id).split('/')[1]
|
|
||||||
).property('owner', 'name'),
|
|
||||||
|
|
||||||
lastBuildDuration: (->
|
lastBuildDuration: (->
|
||||||
duration = @getPath('data.lastBuildDuration')
|
duration = @getPath('data.lastBuildDuration')
|
||||||
duration = Travis.Helpers.durationFrom(@get('lastBuildStarted_at'), @get('lastBuildFinished_at')) unless duration
|
duration = Travis.Helpers.durationFrom(@get('lastBuildStarted_at'), @get('lastBuildFinished_at')) unless duration
|
||||||
|
|
|
@ -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
|
init: () ->
|
||||||
# enableLogging: true
|
@app = @get('app')
|
||||||
location: 'hash'
|
|
||||||
|
|
||||||
root: Em.Route.extend
|
start: ->
|
||||||
# common "layout" state for all states that show a repo list on the left.
|
@app.connectLayout()
|
||||||
# there also will be "profile" and "stats" states next to "default" that do
|
@app.connectLeft(Travis.Repository.find())
|
||||||
# not have a 3-column layout
|
@route(route, action) for route, action of @ROUTES
|
||||||
default: Em.Route.extend
|
|
||||||
route: '/'
|
|
||||||
|
|
||||||
viewCurrent: Ember.Route.transitionTo('repository.current')
|
route: (route, tab) ->
|
||||||
viewBuilds: Ember.Route.transitionTo('repository.builds')
|
Em.routes.add(route, (params) => this[tab](params))
|
||||||
viewBuild: Ember.Route.transitionTo('repository.build')
|
|
||||||
viewJob: Ember.Route.transitionTo('repository.job')
|
|
||||||
|
|
||||||
connectOutlets: (router) ->
|
index: (params) ->
|
||||||
repositories = Travis.Repository.find()
|
repositories = @app.get('repositories')
|
||||||
router.set('repositories', repositories)
|
|
||||||
router.set('job', undefined)
|
|
||||||
router.connectLeft(repositories)
|
|
||||||
|
|
||||||
index: Em.Route.extend
|
|
||||||
route: '/'
|
|
||||||
|
|
||||||
# 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, =>
|
onceLoaded repositories, =>
|
||||||
repository = repositories.get('firstObject')
|
repository = repositories.get('firstObject')
|
||||||
build = Travis.Build.find(repository.get('lastBuildId'))
|
@app.connectRepository(repository)
|
||||||
router.connectRepository(repository)
|
@app.connectTabs()
|
||||||
router.connectTabs()
|
@app.connectBuild(repository.get('lastBuild'))
|
||||||
router.connectBuild(build)
|
|
||||||
|
|
||||||
repository: Em.Route.extend
|
current: (params) ->
|
||||||
route: '/:owner/:name'
|
@repository params, (repository) =>
|
||||||
|
@app.connectTabs()
|
||||||
|
@app.connectBuild(repository.get('lastBuild'))
|
||||||
|
|
||||||
serialize: (router, repository) ->
|
builds: (params) ->
|
||||||
router.serializeRepository(repository)
|
@repository params, (repository) =>
|
||||||
|
@app.connectTabs()
|
||||||
|
@app.connectBuilds(repository.get('builds'))
|
||||||
|
|
||||||
deserialize: (router, params) ->
|
build: (params) ->
|
||||||
router.deserializeRepository(params)
|
@repository params
|
||||||
|
@buildBy params.id, (build) =>
|
||||||
|
@app.connectTabs(build)
|
||||||
|
@app.connectBuild(build)
|
||||||
|
|
||||||
connectOutlets: (router, repository) ->
|
job: (params) ->
|
||||||
router.connectRepository(repository)
|
@repository params
|
||||||
|
@jobBy params.id, (job) =>
|
||||||
|
@app.connectTabs(job.get('build'), job)
|
||||||
|
@app.connectJob(job)
|
||||||
|
|
||||||
current: Em.Route.extend
|
repository: (params, callback) ->
|
||||||
route: '/'
|
@repositoryBy params, (repository) =>
|
||||||
|
@app.connectRepository(repository)
|
||||||
|
callback(repository) if callback
|
||||||
|
|
||||||
connectOutlets: (router) ->
|
repositoryBy: (params, callback) ->
|
||||||
repository = router.get('repository')
|
repositories = Travis.Repository.bySlug("#{params.owner}/#{params.name}")
|
||||||
onceLoaded repository, -> # TODO should not need to wait here, right?
|
onceLoaded repositories, =>
|
||||||
build = repository.get('lastBuild')
|
callback(repositories.get('firstObject'))
|
||||||
router.connectTabs()
|
|
||||||
router.connectBuild(build)
|
|
||||||
|
|
||||||
builds: Em.Route.extend
|
buildBy: (id, callback) =>
|
||||||
route: '/builds'
|
build = Travis.Build.find(id)
|
||||||
|
onceLoaded build, =>
|
||||||
connectOutlets: (router) ->
|
callback(build)
|
||||||
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}")
|
|
||||||
|
|
||||||
|
jobBy: (id, callback) ->
|
||||||
|
job = Travis.Job.find(id)
|
||||||
|
onceLoaded job, =>
|
||||||
|
callback(job)
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
{{#each build in content}}
|
{{#each build in content}}
|
||||||
{{#view Travis.Views.BuildsItemView contextBinding="build"}}
|
{{#view Travis.Views.BuildsItemView contextBinding="build"}}
|
||||||
<tr>
|
<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="commit"><a {{bindAttr href="view.urlGithubCommit"}}>{{formatCommit commit}}</a></td>
|
||||||
<td class="message">{{{formatMessage commit.message short="true"}}}</td>
|
<td class="message">{{{formatMessage commit.message short="true"}}}</td>
|
||||||
<td class="duration" {{bindAttr title="started_at"}}>{{formatDuration duration}}</td>
|
<td class="duration" {{bindAttr title="started_at"}}>{{formatDuration duration}}</td>
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
{{#unless isLoaded}}
|
||||||
|
Loading ...
|
||||||
|
{{else}}
|
||||||
<div id="build" {{bindAttr class="classes"}}>
|
<div id="build" {{bindAttr class="classes"}}>
|
||||||
<dl class="summary clearfix">
|
<dl class="summary clearfix">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<dt>{{t builds.name}}</dt>
|
<dt>{{t builds.name}}</dt>
|
||||||
<dd class="number"><a {{action viewBuild href=true}}>{{number}}</a></dd>
|
<dd class="number"><a {{bindAttr href="view.urlBuild"}}>{{number}}</a></dd>
|
||||||
<dt class="finished_at_label">{{t builds.finished_at}}</dt>
|
<dt class="finished_at_label">{{t builds.finished_at}}</dt>
|
||||||
<dd class="finished_at timeago" {{bindAttr title="finished_at"}}>{{formatTime finished_at}}</dd>
|
<dd class="finished_at timeago" {{bindAttr title="finished_at"}}>{{formatTime finished_at}}</dd>
|
||||||
<dt>{{t builds.duration}}</dt>
|
<dt>{{t builds.duration}}</dt>
|
||||||
|
@ -35,12 +38,11 @@
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
{{#if isLoaded}}
|
|
||||||
{{#if isMatrix}}
|
{{#if isMatrix}}
|
||||||
{{view Travis.Views.JobsView jobsBinding="view.requiredJobs" required="true"}}
|
{{view Travis.Views.JobsView jobsBinding="view.requiredJobs" required="true"}}
|
||||||
{{view Travis.Views.JobsView jobsBinding="view.allowedFailureJobs"}}
|
{{view Travis.Views.JobsView jobsBinding="view.allowedFailureJobs"}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{view Travis.Views.LogView contextBinding="jobs.firstObject"}}
|
{{view Travis.Views.LogView contextBinding="jobs.firstObject"}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
|
{{/unless}}
|
||||||
|
|
|
@ -15,15 +15,17 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{{#each view.jobs}}
|
{{#each job in view.jobs}}
|
||||||
|
{{#view Travis.Views.JobsItemView contextBinding="job"}}
|
||||||
<tr {{bindAttr class="color"}}>
|
<tr {{bindAttr class="color"}}>
|
||||||
<td class="number"><a {{action viewJob href=true}}>#{{number}}</a></td>
|
<td class="number"><a {{bindAttr href="view.urlJob"}}>#{{number}}</a></td>
|
||||||
<td class="duration" {{bindAttr title="started_at"}}>{{formatDuration duration}}</td>
|
<td class="duration" {{bindAttr title="started_at"}}>{{formatDuration duration}}</td>
|
||||||
<td class="finished_at timeago" {{bindAttr title="finished_at"}}>{{formatTime finished_at}}</td>
|
<td class="finished_at timeago" {{bindAttr title="finished_at"}}>{{formatTime finished_at}}</td>
|
||||||
{{#each configValues}}
|
{{#each configValues}}
|
||||||
<td>{{this}}</td>
|
<td>{{this}}</td>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</tr>
|
</tr>
|
||||||
|
{{/view}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<dl class="summary clearfix">
|
<dl class="summary clearfix">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<dt>Job</dt>
|
<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>
|
<dt class="finished_at_label">{{t jobs.finished_at}}</dt>
|
||||||
<dd class="finished_at timeago" {{bindAttr title="finished_at"}}>{{formatTime finished_at}}</dd>
|
<dd class="finished_at timeago" {{bindAttr title="finished_at"}}>{{formatTime finished_at}}</dd>
|
||||||
<dt>{{t jobs.duration}}</dt>
|
<dt>{{t jobs.duration}}</dt>
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
{{#if content.lastObject.isLoaded}}
|
{{#unless content.lastObject.isLoaded}}
|
||||||
|
Loading ...
|
||||||
|
{{else}}
|
||||||
<ul id="repositories">
|
<ul id="repositories">
|
||||||
{{#each repository in content}}
|
{{#each repository in content}}
|
||||||
{{#view Travis.Views.RepositoriesItemView tagName="li" classBinding="classes" contextBinding="repository"}}
|
{{#view Travis.Views.RepositoriesItemView tagName="li" classBinding="classes" contextBinding="repository"}}
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<a {{action viewCurrent href=true}} class="current">{{slug}}</a>
|
<a {{bindAttr href="view.urlRepository"}} class="current">{{slug}}</a>
|
||||||
<a {{action viewBuild href=true context="lastBuild"}} class="last_build">#{{lastBuildNumber}}</a>
|
<a {{bindAttr href="view.urlLastBuild"}} class="last_build">#{{lastBuildNumber}}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="summary">
|
<p class="summary">
|
||||||
|
@ -20,4 +22,4 @@
|
||||||
{{/view}}
|
{{/view}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
<ul>
|
<ul>
|
||||||
{{/if}}
|
{{/unless}}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
{{#unless isLoaded}}
|
||||||
|
Loading ...
|
||||||
|
{{else}}
|
||||||
<div id="repository">
|
<div id="repository">
|
||||||
<h3>
|
<h3>
|
||||||
<a {{bindAttr href="urlGithub"}}>{{slug}}</a>
|
<a {{bindAttr href="urlGithub"}}>{{slug}}</a>
|
||||||
|
@ -17,3 +20,4 @@
|
||||||
{{outlet tab}}
|
{{outlet tab}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{/unless}}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<ul class="tabs">
|
<ul class="tabs">
|
||||||
<li><a {{action viewCurrent href=true context="repository"}} class="current">Current</a></li>
|
<li><a {{bindAttr href="view.urlRepository"}} class="current">Current</a></li>
|
||||||
<li><a {{action viewBuilds href=true context="repository"}} class="history">History</a></li>
|
<li><a {{bindAttr href="view.urlBuilds"}} class="history">History</a></li>
|
||||||
{{#if build}}
|
{{#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}}
|
||||||
{{#if job}}
|
{{#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}}
|
{{/if}}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -13,11 +13,13 @@ Travis.Views =
|
||||||
classes.join(' ')
|
classes.join(' ')
|
||||||
).property('repository.lastBuildResult', 'repository.selected')
|
).property('repository.lastBuildResult', 'repository.selected')
|
||||||
|
|
||||||
lastBuild: (->
|
urlRepository: (->
|
||||||
owner: @getPath('repository.owner')
|
Travis.Urls.repository(@get('context'))
|
||||||
name: @getPath('repository.name')
|
).property('context')
|
||||||
id: @getPath('repository.lastBuildId')
|
|
||||||
).property('repository.owner', 'repository.name', 'repository.lastBuildId')
|
urlLastBuild: (->
|
||||||
|
Travis.Urls.lastBuild(@get('context'))
|
||||||
|
).property('context')
|
||||||
|
|
||||||
RepositoryView: Em.View.extend
|
RepositoryView: Em.View.extend
|
||||||
templateName: 'repositories/show'
|
templateName: 'repositories/show'
|
||||||
|
@ -25,19 +27,39 @@ Travis.Views =
|
||||||
TabsView: Em.View.extend
|
TabsView: Em.View.extend
|
||||||
templateName: 'repositories/tabs'
|
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
|
HistoryView: Em.View.extend
|
||||||
templateName: 'builds/list'
|
templateName: 'builds/list'
|
||||||
|
|
||||||
BuildsItemView: Em.View.extend
|
BuildsItemView: Em.View.extend
|
||||||
classes: (->
|
classes: (->
|
||||||
Travis.Helpers.colorForResult(@getPath('content.result'))
|
Travis.Helpers.colorForResult(@getPath('context.result'))
|
||||||
).property('content.result')
|
).property('context.result')
|
||||||
|
|
||||||
|
urlBuild: (->
|
||||||
|
Travis.Urls.build(@getPath('context.repository'), @get('context'))
|
||||||
|
).property('context.repository.slug', 'context')
|
||||||
|
|
||||||
BuildView: Em.View.extend
|
BuildView: Em.View.extend
|
||||||
templateName: 'builds/show'
|
templateName: 'builds/show'
|
||||||
|
|
||||||
classes: (->
|
classes: (->
|
||||||
Helpers.colorForResult(@get('result'))
|
Travis.Helpers.colorForResult(@get('result'))
|
||||||
).property('result')
|
).property('result')
|
||||||
|
|
||||||
requiredJobs: (->
|
requiredJobs: (->
|
||||||
|
@ -48,9 +70,18 @@ Travis.Views =
|
||||||
@getPath('context.jobs').filter((job) -> job.get('allow_failure'))
|
@getPath('context.jobs').filter((job) -> job.get('allow_failure'))
|
||||||
).property()
|
).property()
|
||||||
|
|
||||||
|
urlBuild: (->
|
||||||
|
Travis.Urls.build(@getPath('context.repository'), @get('context'))
|
||||||
|
).property('controller.content.repository.id', 'controller.content.id')
|
||||||
|
|
||||||
JobsView: Em.View.extend
|
JobsView: Em.View.extend
|
||||||
templateName: 'jobs/list'
|
templateName: 'jobs/list'
|
||||||
|
|
||||||
|
JobsItemView: Em.View.extend
|
||||||
|
urlJob: (->
|
||||||
|
Travis.Urls.job(@getPath('context.repository'), @get('context'))
|
||||||
|
).property('context.repository', 'context')
|
||||||
|
|
||||||
JobView: Em.View.extend
|
JobView: Em.View.extend
|
||||||
templateName: 'jobs/show'
|
templateName: 'jobs/show'
|
||||||
|
|
||||||
|
@ -58,6 +89,10 @@ Travis.Views =
|
||||||
Travis.Helpers.colorForResult(@get('result'))
|
Travis.Helpers.colorForResult(@get('result'))
|
||||||
).property('result')
|
).property('result')
|
||||||
|
|
||||||
|
urlJob: (->
|
||||||
|
Travis.Urls.job(@getPath('context.repository'), @get('context'))
|
||||||
|
).property('controller.content.repository.id', 'controller.content.id')
|
||||||
|
|
||||||
LogView: Em.View.extend
|
LogView: Em.View.extend
|
||||||
templateName: 'jobs/log'
|
templateName: 'jobs/log'
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
responseTime = 0
|
||||||
|
|
||||||
repositories = [
|
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: 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},
|
{ 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 = [
|
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: 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: 'travis-ci/travis-core', commit_id: 2, job_ids: [3], number: 2, event_type: 'push', config: { rvm: ['rbx'] } },
|
{ id: 2, repository_id: '1', 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: 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: 'travis-ci/travis-hub', commit_id: 4, job_ids: [5], number: 4, event_type: 'push', config: { rvm: ['rbx'] } },
|
{ id: 4, repository_id: '3', commit_id: 4, job_ids: [5], number: 4, event_type: 'push', config: { rvm: ['rbx'] } },
|
||||||
]
|
]
|
||||||
|
|
||||||
commits = [
|
commits = [
|
||||||
|
@ -36,19 +38,19 @@ artifacts = [
|
||||||
|
|
||||||
$.mockjax
|
$.mockjax
|
||||||
url: '/repositories'
|
url: '/repositories'
|
||||||
responseTime: 0
|
responseTime: responseTime
|
||||||
responseText: { repositories: repositories }
|
responseText: { repositories: repositories }
|
||||||
|
|
||||||
for repository in repositories
|
for repository in repositories
|
||||||
$.mockjax
|
$.mockjax
|
||||||
url: '/' + repository.slug
|
url: '/' + repository.slug
|
||||||
responseTime: 0
|
responseTime: responseTime
|
||||||
responseText: { repository: repository }
|
responseText: { repository: repository }
|
||||||
|
|
||||||
for build in builds
|
for build in builds
|
||||||
$.mockjax
|
$.mockjax
|
||||||
url: '/builds/' + build.id
|
url: '/builds/' + build.id
|
||||||
responseTime: 0
|
responseTime: responseTime
|
||||||
responseText:
|
responseText:
|
||||||
build: build,
|
build: build,
|
||||||
commit: commits[build.commit_id - 1]
|
commit: commits[build.commit_id - 1]
|
||||||
|
@ -57,8 +59,8 @@ for build in builds
|
||||||
for repository in repositories
|
for repository in repositories
|
||||||
$.mockjax
|
$.mockjax
|
||||||
url: '/builds'
|
url: '/builds'
|
||||||
data: { repository_id: 1, event_type: 'push', orderBy: 'number DESC' }
|
data: { repository_id: repository.id, event_type: 'push', orderBy: 'number DESC' }
|
||||||
responseTime: 0
|
responseTime: responseTime
|
||||||
responseText:
|
responseText:
|
||||||
builds: (builds[id - 1] for id in repository.build_ids)
|
builds: (builds[id - 1] for id in repository.build_ids)
|
||||||
commits: (commits[builds[id - 1].commit_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
|
for job in jobs
|
||||||
$.mockjax
|
$.mockjax
|
||||||
url: '/jobs/' + job.id
|
url: '/jobs/' + job.id
|
||||||
responseTime: 0
|
responseTime: responseTime
|
||||||
responseText:
|
responseText:
|
||||||
job: job,
|
job: job,
|
||||||
commit: commits[job.commit_id - 1]
|
commit: commits[job.commit_id - 1]
|
||||||
|
@ -74,7 +76,7 @@ for job in jobs
|
||||||
for artifact in artifacts
|
for artifact in artifacts
|
||||||
$.mockjax
|
$.mockjax
|
||||||
url: '/artifacts/' + artifact.id
|
url: '/artifacts/' + artifact.id
|
||||||
responseTime: 0
|
responseTime: responseTime
|
||||||
responseText:
|
responseText:
|
||||||
artifact: artifact
|
artifact: artifact
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
describe 'The current build tab', ->
|
describe 'The current build tab', ->
|
||||||
describe 'on the "index" state', ->
|
describe 'on the "index" state', ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
app '/'
|
app ''
|
||||||
waitFor buildRendered
|
waitFor buildRendered
|
||||||
|
|
||||||
it 'displays the build summary', ->
|
it 'displays the build summary', ->
|
||||||
|
@ -19,13 +19,13 @@ describe 'The current build tab', ->
|
||||||
displaysBuildMatrix
|
displaysBuildMatrix
|
||||||
headers: ['Job', 'Duration', 'Finished', 'Rvm']
|
headers: ['Job', 'Duration', 'Finished', 'Rvm']
|
||||||
jobs: [
|
jobs: [
|
||||||
{ number: '#1.1', repo: 'travis-ci/travis-core', finishedAt: /\d+ (\w+) ago/, duration: '35 sec', rvm: 'rbx' },
|
{ id: 1, 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: 2, number: '#1.2', repo: 'travis-ci/travis-core', finishedAt: '-', duration: '-', rvm: '1.9.3' }
|
||||||
]
|
]
|
||||||
|
|
||||||
describe 'on the "current" state', ->
|
describe 'on the "current" state', ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
app '/travis-ci/travis-core'
|
app '!/travis-ci/travis-core'
|
||||||
waitFor repositoriesRendered
|
waitFor repositoriesRendered
|
||||||
waitFor buildRendered
|
waitFor buildRendered
|
||||||
|
|
||||||
|
@ -44,6 +44,6 @@ describe 'The current build tab', ->
|
||||||
displaysBuildMatrix
|
displaysBuildMatrix
|
||||||
headers: ['Job', 'Duration', 'Finished', 'Rvm']
|
headers: ['Job', 'Duration', 'Finished', 'Rvm']
|
||||||
jobs: [
|
jobs: [
|
||||||
{ number: '#1.1', repo: 'travis-ci/travis-core', finishedAt: /\d+ (\w+) ago/, duration: '35 sec', rvm: 'rbx' },
|
{ id: 1, 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: 2, number: '#1.2', repo: 'travis-ci/travis-core', finishedAt: '-', duration: '-', rvm: '1.9.3' }
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
describe 'The repositories list', ->
|
describe 'The repositories list', ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
app '/'
|
app ''
|
||||||
waitFor repositoriesRendered
|
waitFor repositoriesRendered
|
||||||
|
|
||||||
it 'lists repositories', ->
|
it 'lists repositories', ->
|
||||||
href = $('#repositories a.slug').attr('href')
|
href = $('#repositories a.current').attr('href')
|
||||||
expect(href).toEqual '#/travis-ci/travis-core'
|
expect(href).toEqual '#!/travis-ci/travis-core'
|
||||||
|
|
||||||
it "links to the repository's last build action", ->
|
it "links to the repository's last build action", ->
|
||||||
href = $('#repositories a.last_build').attr('href')
|
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'
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
describe 'The repository view', ->
|
describe 'The repository view', ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
app '/'
|
app ''
|
||||||
waitFor repositoriesRendered
|
waitFor repositoriesRendered
|
||||||
|
|
||||||
it 'displays the repository header', ->
|
it 'displays the repository header', ->
|
||||||
|
|
|
@ -2,17 +2,13 @@ minispade.require 'app'
|
||||||
|
|
||||||
@reset = ->
|
@reset = ->
|
||||||
Travis.app.destroy() if Travis.app
|
Travis.app.destroy() if Travis.app
|
||||||
$('body #content').empty()
|
$('body #content').remove()
|
||||||
|
|
||||||
@app = (url) ->
|
@app = (url) ->
|
||||||
router = Travis.Router.create
|
$('body').append('<div id="content"></div>')
|
||||||
location: Em.NoneLocation.create()
|
Travis.app = Travis.App.create(rootElement: '#content')
|
||||||
|
Travis.app.initialize()
|
||||||
Travis.app = Travis.App.create()
|
Em.routes.set('location', url)
|
||||||
Travis.app.set('rootElement', '#content')
|
|
||||||
Travis.app.initialize(router)
|
|
||||||
|
|
||||||
router.route(url)
|
|
||||||
|
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
reset()
|
reset()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
@repositoriesRendered = ->
|
@repositoriesRendered = ->
|
||||||
$('#repositories li').length > 0
|
$('#repositories li a.current').text() != ''
|
||||||
|
|
||||||
@buildRendered = ->
|
@buildRendered = ->
|
||||||
$('#build .summary .number').text() != ''
|
$('#build .summary .number').text() != ''
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
@displaysBuildSummary = (data) ->
|
@displaysBuildSummary = (data) ->
|
||||||
element = $('#build .summary .number a')
|
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')
|
element = $('#build .summary .finished_at')
|
||||||
expect(element.text()).toMatch /\d+ (\w+) ago/
|
expect(element.text()).toMatch /\d+ (\w+) ago/
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
expect(element.text()).toEqual job.number
|
expect(element.text()).toEqual job.number
|
||||||
|
|
||||||
element = $("#jobs tr:nth-child(#{ix}) td.number a")
|
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")
|
element = $("#jobs tr:nth-child(#{ix}) td.duration")
|
||||||
expect(element.text()).toEqual job.duration
|
expect(element.text()).toEqual job.duration
|
||||||
|
|
|
@ -1,23 +1,25 @@
|
||||||
# describe 'The tabs view', ->
|
describe 'The tabs view', ->
|
||||||
# describe 'on the "index" state', ->
|
describe 'on the "index" state', ->
|
||||||
# beforeEach ->
|
beforeEach ->
|
||||||
# app '/'
|
app ''
|
||||||
# waitFor repositoriesRendered
|
waitFor repositoriesRendered
|
||||||
#
|
|
||||||
# it 'has a "current" tab linking to the current build', ->
|
it 'has a "current" tab linking to the current build', ->
|
||||||
# href = $('#main .tabs a.current').attr('href')
|
href = $('#main .tabs a.current').attr('href')
|
||||||
# expect(href).toEqual '/travis-ci/travis-core'
|
expect(href).toEqual '#!/travis-ci/travis-core'
|
||||||
#
|
|
||||||
# it 'has a "history" tab linking to the builds list', ->
|
it 'has a "history" tab linking to the builds list', ->
|
||||||
# href = $('#main .tabs a.history').attr('href')
|
href = $('#main .tabs a.history').attr('href')
|
||||||
# expect(href).toEqual '/travis-ci/travis-core/builds'
|
expect(href).toEqual '#!/travis-ci/travis-core/builds'
|
||||||
#
|
|
||||||
# describe 'on the "current" state', ->
|
describe 'on the "current" state', ->
|
||||||
# app '/travis-ci/travis-core'
|
beforeEach ->
|
||||||
# waitFor repositoriesRendered
|
app '!/travis-ci/travis-core'
|
||||||
#
|
waitFor repositoriesRendered
|
||||||
# it 'has a "current" tab linking to the current build', ->
|
waitFor buildRendered
|
||||||
# href = $('#main .tabs a.current').attr('href')
|
|
||||||
# expect(href).toEqual '/travis-ci/travis-core'
|
it 'has a "current" tab linking to the current build', ->
|
||||||
#
|
href = $('#main .tabs a.current').attr('href')
|
||||||
#
|
expect(href).toEqual '#!/travis-ci/travis-core'
|
||||||
|
|
||||||
|
|
||||||
|
|
546
assets/javascripts/vendor/sc-routes.js
vendored
Normal file
546
assets/javascripts/vendor/sc-routes.js
vendored
Normal 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
|
@ -1,5 +1,7 @@
|
||||||
(function() {
|
(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 = [
|
repositories = [
|
||||||
{
|
{
|
||||||
|
@ -33,7 +35,7 @@
|
||||||
builds = [
|
builds = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
repository_id: 'travis-ci/travis-core',
|
repository_id: '1',
|
||||||
commit_id: 1,
|
commit_id: 1,
|
||||||
job_ids: [1, 2],
|
job_ids: [1, 2],
|
||||||
number: 1,
|
number: 1,
|
||||||
|
@ -46,7 +48,7 @@
|
||||||
result: 0
|
result: 0
|
||||||
}, {
|
}, {
|
||||||
id: 2,
|
id: 2,
|
||||||
repository_id: 'travis-ci/travis-core',
|
repository_id: '1',
|
||||||
commit_id: 2,
|
commit_id: 2,
|
||||||
job_ids: [3],
|
job_ids: [3],
|
||||||
number: 2,
|
number: 2,
|
||||||
|
@ -56,7 +58,7 @@
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
id: 3,
|
id: 3,
|
||||||
repository_id: 'travis-ci/travis-assets',
|
repository_id: '2',
|
||||||
commit_id: 3,
|
commit_id: 3,
|
||||||
job_ids: [4],
|
job_ids: [4],
|
||||||
number: 3,
|
number: 3,
|
||||||
|
@ -69,7 +71,7 @@
|
||||||
result: 0
|
result: 0
|
||||||
}, {
|
}, {
|
||||||
id: 4,
|
id: 4,
|
||||||
repository_id: 'travis-ci/travis-hub',
|
repository_id: '3',
|
||||||
commit_id: 4,
|
commit_id: 4,
|
||||||
job_ids: [5],
|
job_ids: [5],
|
||||||
number: 4,
|
number: 4,
|
||||||
|
@ -197,7 +199,7 @@
|
||||||
|
|
||||||
$.mockjax({
|
$.mockjax({
|
||||||
url: '/repositories',
|
url: '/repositories',
|
||||||
responseTime: 0,
|
responseTime: responseTime,
|
||||||
responseText: {
|
responseText: {
|
||||||
repositories: repositories
|
repositories: repositories
|
||||||
}
|
}
|
||||||
|
@ -207,7 +209,7 @@
|
||||||
repository = repositories[_i];
|
repository = repositories[_i];
|
||||||
$.mockjax({
|
$.mockjax({
|
||||||
url: '/' + repository.slug,
|
url: '/' + repository.slug,
|
||||||
responseTime: 0,
|
responseTime: responseTime,
|
||||||
responseText: {
|
responseText: {
|
||||||
repository: repository
|
repository: repository
|
||||||
}
|
}
|
||||||
|
@ -218,7 +220,7 @@
|
||||||
build = builds[_j];
|
build = builds[_j];
|
||||||
$.mockjax({
|
$.mockjax({
|
||||||
url: '/builds/' + build.id,
|
url: '/builds/' + build.id,
|
||||||
responseTime: 0,
|
responseTime: responseTime,
|
||||||
responseText: {
|
responseText: {
|
||||||
build: build,
|
build: build,
|
||||||
commit: commits[build.commit_id - 1],
|
commit: commits[build.commit_id - 1],
|
||||||
|
@ -241,11 +243,11 @@
|
||||||
$.mockjax({
|
$.mockjax({
|
||||||
url: '/builds',
|
url: '/builds',
|
||||||
data: {
|
data: {
|
||||||
repository_id: 1,
|
repository_id: repository.id,
|
||||||
event_type: 'push',
|
event_type: 'push',
|
||||||
orderBy: 'number DESC'
|
orderBy: 'number DESC'
|
||||||
},
|
},
|
||||||
responseTime: 0,
|
responseTime: responseTime,
|
||||||
responseText: {
|
responseText: {
|
||||||
builds: (function() {
|
builds: (function() {
|
||||||
var _l, _len3, _ref, _results;
|
var _l, _len3, _ref, _results;
|
||||||
|
@ -275,7 +277,7 @@
|
||||||
job = jobs[_l];
|
job = jobs[_l];
|
||||||
$.mockjax({
|
$.mockjax({
|
||||||
url: '/jobs/' + job.id,
|
url: '/jobs/' + job.id,
|
||||||
responseTime: 0,
|
responseTime: responseTime,
|
||||||
responseText: {
|
responseText: {
|
||||||
job: job,
|
job: job,
|
||||||
commit: commits[job.commit_id - 1]
|
commit: commits[job.commit_id - 1]
|
||||||
|
@ -287,7 +289,7 @@
|
||||||
artifact = artifacts[_m];
|
artifact = artifacts[_m];
|
||||||
$.mockjax({
|
$.mockjax({
|
||||||
url: '/artifacts/' + artifact.id,
|
url: '/artifacts/' + artifact.id,
|
||||||
responseTime: 0,
|
responseTime: responseTime,
|
||||||
responseText: {
|
responseText: {
|
||||||
artifact: artifact
|
artifact: artifact
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
describe('The current build tab', function() {
|
describe('The current build tab', function() {
|
||||||
describe('on the "index" state', function() {
|
describe('on the "index" state', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
app('/');
|
app('');
|
||||||
return waitFor(buildRendered);
|
return waitFor(buildRendered);
|
||||||
});
|
});
|
||||||
it('displays the build summary', function() {
|
it('displays the build summary', function() {
|
||||||
|
@ -23,12 +23,14 @@
|
||||||
headers: ['Job', 'Duration', 'Finished', 'Rvm'],
|
headers: ['Job', 'Duration', 'Finished', 'Rvm'],
|
||||||
jobs: [
|
jobs: [
|
||||||
{
|
{
|
||||||
|
id: 1,
|
||||||
number: '#1.1',
|
number: '#1.1',
|
||||||
repo: 'travis-ci/travis-core',
|
repo: 'travis-ci/travis-core',
|
||||||
finishedAt: /\d+ (\w+) ago/,
|
finishedAt: /\d+ (\w+) ago/,
|
||||||
duration: '35 sec',
|
duration: '35 sec',
|
||||||
rvm: 'rbx'
|
rvm: 'rbx'
|
||||||
}, {
|
}, {
|
||||||
|
id: 2,
|
||||||
number: '#1.2',
|
number: '#1.2',
|
||||||
repo: 'travis-ci/travis-core',
|
repo: 'travis-ci/travis-core',
|
||||||
finishedAt: '-',
|
finishedAt: '-',
|
||||||
|
@ -42,7 +44,7 @@
|
||||||
});
|
});
|
||||||
return describe('on the "current" state', function() {
|
return describe('on the "current" state', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
app('/travis-ci/travis-core');
|
app('!/travis-ci/travis-core');
|
||||||
waitFor(repositoriesRendered);
|
waitFor(repositoriesRendered);
|
||||||
return waitFor(buildRendered);
|
return waitFor(buildRendered);
|
||||||
});
|
});
|
||||||
|
@ -63,12 +65,14 @@
|
||||||
headers: ['Job', 'Duration', 'Finished', 'Rvm'],
|
headers: ['Job', 'Duration', 'Finished', 'Rvm'],
|
||||||
jobs: [
|
jobs: [
|
||||||
{
|
{
|
||||||
|
id: 1,
|
||||||
number: '#1.1',
|
number: '#1.1',
|
||||||
repo: 'travis-ci/travis-core',
|
repo: 'travis-ci/travis-core',
|
||||||
finishedAt: /\d+ (\w+) ago/,
|
finishedAt: /\d+ (\w+) ago/,
|
||||||
duration: '35 sec',
|
duration: '35 sec',
|
||||||
rvm: 'rbx'
|
rvm: 'rbx'
|
||||||
}, {
|
}, {
|
||||||
|
id: 2,
|
||||||
number: '#1.2',
|
number: '#1.2',
|
||||||
repo: 'travis-ci/travis-core',
|
repo: 'travis-ci/travis-core',
|
||||||
finishedAt: '-',
|
finishedAt: '-',
|
||||||
|
@ -87,18 +91,18 @@
|
||||||
|
|
||||||
describe('The repositories list', function() {
|
describe('The repositories list', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
app('/');
|
app('');
|
||||||
return waitFor(repositoriesRendered);
|
return waitFor(repositoriesRendered);
|
||||||
});
|
});
|
||||||
it('lists repositories', function() {
|
it('lists repositories', function() {
|
||||||
var href;
|
var href;
|
||||||
href = $('#repositories a.slug').attr('href');
|
href = $('#repositories a.current').attr('href');
|
||||||
return expect(href).toEqual('#/travis-ci/travis-core');
|
return expect(href).toEqual('#!/travis-ci/travis-core');
|
||||||
});
|
});
|
||||||
return it("links to the repository's last build action", function() {
|
return it("links to the repository's last build action", function() {
|
||||||
var href;
|
var href;
|
||||||
href = $('#repositories a.last_build').attr('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() {
|
describe('The repository view', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
app('/');
|
app('');
|
||||||
return waitFor(repositoriesRendered);
|
return waitFor(repositoriesRendered);
|
||||||
});
|
});
|
||||||
return it('displays the repository header', function() {
|
return it('displays the repository header', function() {
|
||||||
|
@ -126,18 +130,16 @@
|
||||||
if (Travis.app) {
|
if (Travis.app) {
|
||||||
Travis.app.destroy();
|
Travis.app.destroy();
|
||||||
}
|
}
|
||||||
return $('body #content').empty();
|
return $('body #content').remove();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.app = function(url) {
|
this.app = function(url) {
|
||||||
var router;
|
$('body').append('<div id="content"></div>');
|
||||||
router = Travis.Router.create({
|
Travis.app = Travis.App.create({
|
||||||
location: Em.NoneLocation.create()
|
rootElement: '#content'
|
||||||
});
|
});
|
||||||
Travis.app = Travis.App.create();
|
Travis.app.initialize();
|
||||||
Travis.app.set('rootElement', '#content');
|
return Em.routes.set('location', url);
|
||||||
Travis.app.initialize(router);
|
|
||||||
return router.route(url);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
|
@ -148,7 +150,7 @@
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
this.repositoriesRendered = function() {
|
this.repositoriesRendered = function() {
|
||||||
return $('#repositories li').length > 0;
|
return $('#repositories li a.current').text() !== '';
|
||||||
};
|
};
|
||||||
|
|
||||||
this.buildRendered = function() {
|
this.buildRendered = function() {
|
||||||
|
@ -165,7 +167,7 @@
|
||||||
this.displaysBuildSummary = function(data) {
|
this.displaysBuildSummary = function(data) {
|
||||||
var element;
|
var element;
|
||||||
element = $('#build .summary .number a');
|
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');
|
element = $('#build .summary .finished_at');
|
||||||
expect(element.text()).toMatch(/\d+ (\w+) ago/);
|
expect(element.text()).toMatch(/\d+ (\w+) ago/);
|
||||||
element = $('#build .summary .duration');
|
element = $('#build .summary .duration');
|
||||||
|
@ -200,7 +202,7 @@
|
||||||
element = $("#jobs tr:nth-child(" + ix + ") td.number");
|
element = $("#jobs tr:nth-child(" + ix + ") td.number");
|
||||||
expect(element.text()).toEqual(job.number);
|
expect(element.text()).toEqual(job.number);
|
||||||
element = $("#jobs tr:nth-child(" + ix + ") td.number a");
|
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");
|
element = $("#jobs tr:nth-child(" + ix + ") td.duration");
|
||||||
expect(element.text()).toEqual(job.duration);
|
expect(element.text()).toEqual(job.duration);
|
||||||
element = $("#jobs tr:nth-child(" + ix + ") td.finished_at");
|
element = $("#jobs tr:nth-child(" + ix + ") td.finished_at");
|
||||||
|
@ -232,6 +234,35 @@
|
||||||
}).call(this);
|
}).call(this);
|
||||||
(function() {
|
(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);
|
}).call(this);
|
||||||
|
|
|
@ -26970,6 +26970,552 @@ I18n.p = I18n.pluralize;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ==========================================================================
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* MockJax - jQuery Plugin to Mock Ajax requests
|
* MockJax - jQuery Plugin to Mock Ajax requests
|
||||||
*
|
*
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
<script src="javascripts/specs/specs.js"></script>
|
<script src="javascripts/specs/specs.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="content"></div>
|
|
||||||
<script>
|
<script>
|
||||||
for(key in minispade.modules)
|
for(key in minispade.modules)
|
||||||
if(key.match(/_spec$/))
|
if(key.match(/_spec$/))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user