Compare commits
38 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1cd460e020 | ||
![]() |
59c9fce76c | ||
![]() |
4a73c17d4c | ||
![]() |
fbe384d5ae | ||
![]() |
e60d16209d | ||
![]() |
bd3b62c264 | ||
![]() |
9a9708df31 | ||
![]() |
042e058f2a | ||
![]() |
76c604a39c | ||
![]() |
df0f883ad0 | ||
![]() |
4e1976d30c | ||
![]() |
9222276f87 | ||
![]() |
2267babac0 | ||
![]() |
706ea836e4 | ||
![]() |
b4ad8a54b7 | ||
![]() |
d1b9bddfe0 | ||
![]() |
8956dedeaf | ||
![]() |
c072738d58 | ||
![]() |
0ceb677aed | ||
![]() |
cf1e5d6ad3 | ||
![]() |
3f6ad9cf1a | ||
![]() |
c6bac57047 | ||
![]() |
4a62d4ac9c | ||
![]() |
5a2c86697b | ||
![]() |
f09742abe1 | ||
![]() |
b83e70c324 | ||
![]() |
5c5c433363 | ||
![]() |
264d780931 | ||
![]() |
1efdcf1290 | ||
![]() |
3d7833d64b | ||
![]() |
18838acd27 | ||
![]() |
bc4b666d9e | ||
![]() |
310627cc55 | ||
![]() |
2a2b3f018b | ||
![]() |
6992a79a93 | ||
![]() |
0976672f9e | ||
![]() |
22d4f59ce9 | ||
![]() |
b9de261388 |
12
.jshintrc
12
.jshintrc
|
@ -1,19 +1,9 @@
|
||||||
{
|
{
|
||||||
"predef": [
|
"predef": [
|
||||||
"server",
|
|
||||||
"document",
|
"document",
|
||||||
"window",
|
"window",
|
||||||
"-Promise",
|
"-Promise",
|
||||||
"jQuery",
|
"jQuery"
|
||||||
"Visibility",
|
|
||||||
"$",
|
|
||||||
"Travis",
|
|
||||||
"_cio",
|
|
||||||
"_gaq",
|
|
||||||
"Log",
|
|
||||||
"moment",
|
|
||||||
"Pusher",
|
|
||||||
"md5"
|
|
||||||
],
|
],
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"boss": true,
|
"boss": true,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js: stable
|
node_js: "0.10.36"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
- EMBER_VERSION=default
|
- EMBER_VERSION=default
|
||||||
|
@ -16,8 +16,8 @@ matrix:
|
||||||
|
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
|
|
||||||
addons:
|
#addons:
|
||||||
sauce_connect: true
|
# sauce_connect: true
|
||||||
|
|
||||||
sudo: false
|
sudo: false
|
||||||
|
|
||||||
|
@ -39,6 +39,9 @@ install:
|
||||||
- npm install
|
- npm install
|
||||||
- bower install
|
- bower install
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- ruby ci/prepare_testem.rb
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ember try $EMBER_VERSION
|
- ember try $EMBER_VERSION
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,3 @@ DEPENDENCIES
|
||||||
sinatra
|
sinatra
|
||||||
sinatra-contrib
|
sinatra-contrib
|
||||||
travis-web!
|
travis-web!
|
||||||
|
|
||||||
BUNDLED WITH
|
|
||||||
1.10.1
|
|
||||||
|
|
51
README.md
51
README.md
|
@ -16,39 +16,6 @@ Now you can run the server:
|
||||||
|
|
||||||
And open http://localhost:4200 in the browser.
|
And open http://localhost:4200 in the browser.
|
||||||
|
|
||||||
Alternatively you can run `ember build --watch` and start the server with `waiter/script/server`
|
|
||||||
|
|
||||||
### Running the app in private repos mode
|
|
||||||
|
|
||||||
At the moment Travis CI is available as two separate sites - https://travis-ci.org for Open Source
|
|
||||||
projects and https://travis-ci.com for private projects. travis-web will connect
|
|
||||||
to the Open Source version by default. In order to connect it to the API for private projects
|
|
||||||
you need to run:
|
|
||||||
|
|
||||||
```
|
|
||||||
TRAVIS_PRO=true ember serve --ssl --ssl-key=ssl/server.key --ssl-cert=ssl/server.crt
|
|
||||||
```
|
|
||||||
|
|
||||||
One caveat here is that the command will start server with SSL, so the page will
|
|
||||||
be accessible at https://localhost:4200 (note `https` part).
|
|
||||||
|
|
||||||
### Running on SSL in general
|
|
||||||
|
|
||||||
Sometimes there is a need to test the app with an SSL connection. This is required
|
|
||||||
to make Pusher work when running Travis CI Pro, but it may also be needed in other
|
|
||||||
situations.
|
|
||||||
|
|
||||||
There's already an SSL certificate in the `ssl` directory, which is set for `localhost`
|
|
||||||
host. If you want to use it, you can start the server with:
|
|
||||||
|
|
||||||
```
|
|
||||||
ember serve --ssl --ssl-key=ssl/server.key --ssl-cert=ssl/server.crt
|
|
||||||
```
|
|
||||||
|
|
||||||
In case you want your own certificate, you can follow the instructions posted
|
|
||||||
here: https://gist.github.com/trcarden/3295935 and then point the server to your
|
|
||||||
certificate with `--ssl-key` and `--ssl-cert`.
|
|
||||||
|
|
||||||
### Running tests
|
### Running tests
|
||||||
|
|
||||||
To run a test suite execute:
|
To run a test suite execute:
|
||||||
|
@ -58,21 +25,3 @@ To run a test suite execute:
|
||||||
You can also start an interactive test runner for easier development:
|
You can also start an interactive test runner for easier development:
|
||||||
|
|
||||||
ember test --serve
|
ember test --serve
|
||||||
|
|
||||||
|
|
||||||
### Updating the team page
|
|
||||||
|
|
||||||
The team information can be found in `app/routes/team.js`.
|
|
||||||
To add another member just add the info in the same style as the previous ones. Like so
|
|
||||||
|
|
||||||
{
|
|
||||||
name: 'Mr T'
|
|
||||||
title: 'Mascot'
|
|
||||||
handle: 'travisci'
|
|
||||||
nationality: 'internet'
|
|
||||||
country: 'internet'
|
|
||||||
image: 'mrt'
|
|
||||||
}
|
|
||||||
|
|
||||||
The order of value pairs does not matter, the quotationmarks do. Name and title will be displayed as they are. The handle will be used to generate a link to Twitter and displayed with a '@' in front of it. Nationality and country determine the flags. Please use the name of the country and not the adjective (like 'germany' and NOT 'german'). Image is the identifier to find the right image and animated gif. 'mrt' in the example will result in `team-mrt.png` and `mrt-animated.gif`.
|
|
||||||
Add the images themselves to `public/images/team/` and additional flags to `public/images/pro-landing/`. Mind the naming conventions already in place.
|
|
||||||
|
|
14
SSL_LOCALLY.md
Normal file
14
SSL_LOCALLY.md
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
Sometimes there is a need to test the app with an SSL connection, for example for
|
||||||
|
testing pusher on Travis CI Pro.
|
||||||
|
|
||||||
|
In order to run the app with SSL enabled you need to:
|
||||||
|
|
||||||
|
* generate self signed certificate as described here: https://gist.github.com/trcarden/3295935
|
||||||
|
* one difference is that you need to use localhost.ssl, because travis-api
|
||||||
|
doesn't whitelist localhost.ssl at the moment
|
||||||
|
* run ruby server with `SSL=true TRAVIS_PRO=true waiter/script/server`
|
||||||
|
* run Ember app in build mode only `TRAVIS_PRO=true ember build --watch`
|
||||||
|
|
||||||
|
Now open the app on whatever port ruby server is running (3001 by default). The
|
||||||
|
ruby app will serve files generated by `ember build` and it will allow SSL
|
||||||
|
connections
|
25
app/adapters/application.coffee
Normal file
25
app/adapters/application.coffee
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
`import DS from 'ember-data'`
|
||||||
|
`import config from 'travis/config/environment'`
|
||||||
|
|
||||||
|
Adapter = DS.ActiveModelAdapter.extend
|
||||||
|
auth: Ember.inject.service()
|
||||||
|
|
||||||
|
host: config.apiEndpoint
|
||||||
|
coalesceFindRequests: true
|
||||||
|
|
||||||
|
ajaxOptions: (url, type, options) ->
|
||||||
|
hash = @_super(url, type, options)
|
||||||
|
|
||||||
|
hash.headers ||= {}
|
||||||
|
|
||||||
|
hash.headers['accept'] = 'application/json; version=2'
|
||||||
|
|
||||||
|
if token = @get('auth').token()
|
||||||
|
hash.headers['Authorization'] ||= "token #{token}"
|
||||||
|
|
||||||
|
hash
|
||||||
|
|
||||||
|
findMany: (store, type, ids) ->
|
||||||
|
@ajax(@buildURL(type.modelName), 'GET', data: { ids: ids })
|
||||||
|
|
||||||
|
`export default Adapter`
|
|
@ -1,57 +0,0 @@
|
||||||
import config from 'travis/config/environment';
|
|
||||||
import Ember from 'ember';
|
|
||||||
import ActiveModelAdapter from 'active-model-adapter';
|
|
||||||
|
|
||||||
export default ActiveModelAdapter.extend({
|
|
||||||
auth: Ember.inject.service(),
|
|
||||||
host: config.apiEndpoint,
|
|
||||||
coalesceFindRequests: true,
|
|
||||||
|
|
||||||
// Before Ember Data 2.0 the default behaviour of running `findAll` was to get
|
|
||||||
// new records only when there're no records in the store. This will change
|
|
||||||
// to a different strategy in 2.0: when you run `findAll` it will not get any
|
|
||||||
// new data initially, but it will try loading new data in the background.
|
|
||||||
//
|
|
||||||
// I'm disabling the new behaviour for now.
|
|
||||||
shouldBackgroundReloadRecord() {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
ajaxOptions(url, type, options) {
|
|
||||||
var base, hash, token;
|
|
||||||
|
|
||||||
hash = this._super(...arguments);
|
|
||||||
hash.headers = hash.headers || {};
|
|
||||||
hash.headers['accept'] = 'application/json; version=2';
|
|
||||||
|
|
||||||
if (token = this.get('auth').token()) {
|
|
||||||
if(!hash.headers['Authorization']) {
|
|
||||||
hash.headers['Authorization'] = "token " + token;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
},
|
|
||||||
|
|
||||||
findMany(store, type, ids) {
|
|
||||||
return this.ajax(this.buildURL(type.modelName), 'GET', {
|
|
||||||
data: {
|
|
||||||
ids: ids
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
handleResponse(status, headers, payload) {
|
|
||||||
if (status > 299) {
|
|
||||||
console.log("[ERROR] API responded with an error (" + status + "): " + (JSON.stringify(payload)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._super(...arguments);
|
|
||||||
},
|
|
||||||
|
|
||||||
// this can be removed once this PR is merged and live:
|
|
||||||
// https://github.com/emberjs/data/pull/4204
|
|
||||||
findRecord(store, type, id, snapshot) {
|
|
||||||
return this.ajax(this.buildURL(type.modelName, id, snapshot, 'findRecord'), 'GET');
|
|
||||||
}
|
|
||||||
});
|
|
26
app/adapters/env-var.coffee
Normal file
26
app/adapters/env-var.coffee
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
`import ApplicationAdapter from 'travis/adapters/application'`
|
||||||
|
|
||||||
|
Adapter = ApplicationAdapter.extend
|
||||||
|
namespace: 'settings'
|
||||||
|
|
||||||
|
buildURL: (type, id, record) ->
|
||||||
|
url = @_super.apply this, arguments
|
||||||
|
|
||||||
|
if record && (repoId = Ember.get(record, 'repo.id'))
|
||||||
|
delimiter = if url.indexOf('?') != -1 then '&' else '?'
|
||||||
|
url = "#{url}#{delimiter}repository_id=#{repoId}"
|
||||||
|
|
||||||
|
url
|
||||||
|
|
||||||
|
updateRecord: (store, type, record) ->
|
||||||
|
data = {};
|
||||||
|
serializer = store.serializerFor(type.typeKey);
|
||||||
|
|
||||||
|
serializer.serializeIntoHash(data, type, record);
|
||||||
|
|
||||||
|
id = Ember.get(record, 'id');
|
||||||
|
|
||||||
|
this.ajax(this.buildURL(type.typeKey, id, record), "PATCH", { data: data })
|
||||||
|
|
||||||
|
`export default Adapter`
|
|
@ -1,27 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import ApplicationAdapter from 'travis/adapters/application';
|
|
||||||
|
|
||||||
export default ApplicationAdapter.extend({
|
|
||||||
namespace: 'settings',
|
|
||||||
|
|
||||||
buildURL(type, id, record) {
|
|
||||||
var delimiter, repoId, url;
|
|
||||||
url = this._super.apply(this, arguments);
|
|
||||||
if (record && record.belongsTo('repo') && (repoId = record.belongsTo('repo').id)) {
|
|
||||||
delimiter = url.indexOf('?') !== -1 ? '&' : '?';
|
|
||||||
url = "" + url + delimiter + "repository_id=" + repoId;
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
},
|
|
||||||
|
|
||||||
updateRecord(store, type, record) {
|
|
||||||
var data, serializer;
|
|
||||||
data = {};
|
|
||||||
serializer = store.serializerFor(type.modelName);
|
|
||||||
serializer.serializeIntoHash(data, type, record);
|
|
||||||
var id = record.id;
|
|
||||||
return this.ajax(this.buildURL(type.modelName, id, record), "PATCH", {
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,9 +0,0 @@
|
||||||
import ApplicationAdapter from 'travis/adapters/application';
|
|
||||||
|
|
||||||
export default ApplicationAdapter.extend({
|
|
||||||
updateRecord(store, type, snapshot) {
|
|
||||||
return this._super(...arguments).then( (data) => {
|
|
||||||
return { hook: { id: snapshot.id } };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,11 +1,11 @@
|
||||||
import V3Adapter from 'travis/adapters/v3';
|
import V3Adapter from 'travis/adapters/v3';
|
||||||
import ApplicationAdapter from 'travis/adapters/application';
|
|
||||||
import Config from 'travis/config/environment';
|
|
||||||
|
|
||||||
let Adapter = Config.useV3API ? V3Adapter : ApplicationAdapter;
|
export default V3Adapter.extend({
|
||||||
|
buildUrl(modelName, id, snapshot, requestType, query) {
|
||||||
|
var url = this._super(...arguments);
|
||||||
|
|
||||||
export default Adapter.extend({
|
return url;
|
||||||
defaultSerializer: '-repo',
|
},
|
||||||
|
|
||||||
ajaxOptions(url, type, options) {
|
ajaxOptions(url, type, options) {
|
||||||
var hash = options || {};
|
var hash = options || {};
|
||||||
|
@ -13,12 +13,10 @@ export default Adapter.extend({
|
||||||
hash.data = {};
|
hash.data = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Config.useV3API) {
|
if(hash.data.include) {
|
||||||
if(hash.data.include) {
|
hash.data.include += ',repository.default_branch,branch.last_build,build.commit';
|
||||||
hash.data.include += ',repository.default_branch,branch.last_build,build.commit';
|
} else {
|
||||||
} else {
|
hash.data.include = 'repository.default_branch,branch.last_build,build.commit';
|
||||||
hash.data.include = 'repository.default_branch,branch.last_build,build.commit';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._super(url, type, hash);
|
return this._super(url, type, hash);
|
||||||
|
|
24
app/adapters/ssh-key.coffee
Normal file
24
app/adapters/ssh-key.coffee
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
`import ApplicationAdapter from 'travis/adapters/application'`
|
||||||
|
|
||||||
|
Adapter = ApplicationAdapter.extend
|
||||||
|
namespace: 'settings'
|
||||||
|
|
||||||
|
find: (store, type, id, record) ->
|
||||||
|
@ajax(this.urlPrefix() + '/ssh_key/' + id, 'GET')
|
||||||
|
|
||||||
|
deleteRecord: (store, type, record) ->
|
||||||
|
id = Ember.get(record, 'id')
|
||||||
|
|
||||||
|
@ajax(this.urlPrefix() + '/ssh_key/' + id, "DELETE");
|
||||||
|
|
||||||
|
createRecord: (store, type, record) ->
|
||||||
|
data = {};
|
||||||
|
serializer = store.serializerFor(type.typeKey);
|
||||||
|
serializer.serializeIntoHash(data, type, record, { includeId: true });
|
||||||
|
|
||||||
|
id = Ember.get(record, 'id')
|
||||||
|
|
||||||
|
this.ajax(this.urlPrefix() + '/ssh_key/' + id, "PATCH", { data: data })
|
||||||
|
|
||||||
|
`export default Adapter`
|
|
@ -1,29 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import ApplicationAdapter from 'travis/adapters/application';
|
|
||||||
|
|
||||||
export default ApplicationAdapter.extend({
|
|
||||||
namespace: 'settings',
|
|
||||||
|
|
||||||
findRecord(store, type, id, record) {
|
|
||||||
return this.ajax(this.urlPrefix() + '/ssh_key/' + id, 'GET');
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteRecord(store, type, record) {
|
|
||||||
var id = record.id;
|
|
||||||
return this.ajax(this.urlPrefix() + '/ssh_key/' + id, "DELETE");
|
|
||||||
},
|
|
||||||
|
|
||||||
createRecord(store, type, record) {
|
|
||||||
var data, serializer;
|
|
||||||
data = {};
|
|
||||||
serializer = store.serializerFor(type.modelName);
|
|
||||||
serializer.serializeIntoHash(data, type, record, {
|
|
||||||
includeId: true
|
|
||||||
});
|
|
||||||
|
|
||||||
var id = record.id;
|
|
||||||
return this.ajax(this.urlPrefix() + '/ssh_key/' + id, "PATCH", {
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,11 +1,13 @@
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
|
import DS from 'ember-data';
|
||||||
import config from 'travis/config/environment';
|
import config from 'travis/config/environment';
|
||||||
import RESTAdapter from 'ember-data/adapters/rest';
|
|
||||||
|
|
||||||
export default RESTAdapter.extend({
|
export default DS.RESTAdapter.extend({
|
||||||
auth: Ember.inject.service(),
|
auth: Ember.inject.service(),
|
||||||
host: config.apiEndpoint,
|
host: config.apiEndpoint,
|
||||||
|
|
||||||
|
defaultSerializer: '-repo',
|
||||||
|
|
||||||
sortQueryParams: false,
|
sortQueryParams: false,
|
||||||
coalesceFindRequests: false,
|
coalesceFindRequests: false,
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -54,11 +56,5 @@ export default RESTAdapter.extend({
|
||||||
pathForType: function(modelName, id) {
|
pathForType: function(modelName, id) {
|
||||||
var underscored = Ember.String.underscore(modelName);
|
var underscored = Ember.String.underscore(modelName);
|
||||||
return id ? underscored : Ember.String.pluralize(underscored);
|
return id ? underscored : Ember.String.pluralize(underscored);
|
||||||
},
|
|
||||||
|
|
||||||
// this can be removed once this PR is merged and live:
|
|
||||||
// https://github.com/emberjs/data/pull/4204
|
|
||||||
findRecord(store, type, id, snapshot) {
|
|
||||||
return this.ajax(this.buildURL(type.modelName, id, snapshot, 'findRecord'), 'GET');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
107
app/app.coffee
Normal file
107
app/app.coffee
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
`import Resolver from 'ember/resolver'`
|
||||||
|
`import loadInitializers from 'ember/load-initializers'`
|
||||||
|
`import config from './config/environment'`
|
||||||
|
|
||||||
|
Ember.MODEL_FACTORY_INJECTIONS = true
|
||||||
|
|
||||||
|
App = Ember.Application.extend(Ember.Evented,
|
||||||
|
LOG_TRANSITIONS: true
|
||||||
|
LOG_TRANSITIONS_INTERNAL: true
|
||||||
|
LOG_ACTIVE_GENERATION: true
|
||||||
|
LOG_MODULE_RESOLVER: true
|
||||||
|
LOG_VIEW_LOOKUPS: true
|
||||||
|
#LOG_RESOLVER: true
|
||||||
|
|
||||||
|
modulePrefix: config.modulePrefix
|
||||||
|
podModulePrefix: config.podModulePrefix
|
||||||
|
Resolver: Resolver
|
||||||
|
|
||||||
|
lookup: ->
|
||||||
|
@__container__.lookup.apply @__container__, arguments
|
||||||
|
|
||||||
|
flash: (options) ->
|
||||||
|
Travis.lookup('controller:flash').loadFlashes([options])
|
||||||
|
|
||||||
|
toggleSidebar: ->
|
||||||
|
$('body').toggleClass('maximized')
|
||||||
|
# TODO gotta force redraws here :/
|
||||||
|
element = $('<span></span>')
|
||||||
|
$('#top .profile').append(element)
|
||||||
|
Em.run.later (-> element.remove()), 10
|
||||||
|
element = $('<span></span>')
|
||||||
|
$('#repo').append(element)
|
||||||
|
Em.run.later (-> element.remove()), 10
|
||||||
|
|
||||||
|
ready: ->
|
||||||
|
location.href = location.href.replace('#!/', '') if location.hash.slice(0, 2) == '#!'
|
||||||
|
|
||||||
|
@on 'user:signed_in', (user) ->
|
||||||
|
Travis.onUserUpdate(user)
|
||||||
|
|
||||||
|
@on 'user:refreshed', (user) ->
|
||||||
|
Travis.onUserUpdate(user)
|
||||||
|
|
||||||
|
@on 'user:synced', (user) ->
|
||||||
|
Travis.onUserUpdate(user)
|
||||||
|
|
||||||
|
@on 'user:signed_out', () ->
|
||||||
|
if config.userlike
|
||||||
|
Travis.removeUserlike()
|
||||||
|
|
||||||
|
currentDate: ->
|
||||||
|
new Date()
|
||||||
|
|
||||||
|
onUserUpdate: (user) ->
|
||||||
|
if config.pro
|
||||||
|
@identifyCustomer(user)
|
||||||
|
if config.userlike
|
||||||
|
@setupUserlike(user)
|
||||||
|
|
||||||
|
@subscribePusher(user)
|
||||||
|
|
||||||
|
subscribePusher: (user) ->
|
||||||
|
return unless user.channels
|
||||||
|
channels = user.channels
|
||||||
|
if config.pro
|
||||||
|
channels = channels.map (channel) ->
|
||||||
|
if channel.match /^private-/
|
||||||
|
channel
|
||||||
|
else
|
||||||
|
"private-#{channel}"
|
||||||
|
|
||||||
|
Travis.pusher.subscribeAll(channels)
|
||||||
|
|
||||||
|
setupUserlike: (user) ->
|
||||||
|
|
||||||
|
btn = document.getElementById('userlikeCustomTab')
|
||||||
|
btn.classList.add("logged-in")
|
||||||
|
|
||||||
|
userlikeData = window.userlikeData = {}
|
||||||
|
userlikeData.user = {}
|
||||||
|
|
||||||
|
userlikeData.user.name= user.login;
|
||||||
|
userlikeData.user.email = user.email;
|
||||||
|
|
||||||
|
unless document.getElementById('userlike-script')
|
||||||
|
s = document.createElement('script')
|
||||||
|
s.id = 'userlike-script'
|
||||||
|
s.src = '//userlike-cdn-widgets.s3-eu-west-1.amazonaws.com/0327dbb23382ccbbb91b445b76e8a91d4b37d90ef9f2faf84e11177847ff7bb9.js'
|
||||||
|
document.body.appendChild(s)
|
||||||
|
|
||||||
|
removeUserlike: () ->
|
||||||
|
btn = document.getElementById('userlikeCustomTab')
|
||||||
|
btn.classList.remove("logged-in")
|
||||||
|
|
||||||
|
identifyCustomer: (user) ->
|
||||||
|
if _cio && _cio.identify
|
||||||
|
_cio.identify
|
||||||
|
id: user.id
|
||||||
|
email: user.email
|
||||||
|
name: user.name
|
||||||
|
created_at: (Date.parse(user.created_at) / 1000) || null
|
||||||
|
login: user.login
|
||||||
|
)
|
||||||
|
loadInitializers(App, config.modulePrefix)
|
||||||
|
|
||||||
|
`export default App`
|
130
app/app.js
130
app/app.js
|
@ -1,130 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import Resolver from './resolver';
|
|
||||||
import loadInitializers from 'ember-load-initializers';
|
|
||||||
import config from './config/environment';
|
|
||||||
|
|
||||||
Ember.MODEL_FACTORY_INJECTIONS = true;
|
|
||||||
|
|
||||||
Ember.LinkComponent.reopen({
|
|
||||||
attributeBindings: ['alt']
|
|
||||||
});
|
|
||||||
|
|
||||||
var App = Ember.Application.extend(Ember.Evented, {
|
|
||||||
LOG_TRANSITIONS: true,
|
|
||||||
LOG_TRANSITIONS_INTERNAL: true,
|
|
||||||
LOG_ACTIVE_GENERATION: true,
|
|
||||||
LOG_MODULE_RESOLVER: true,
|
|
||||||
LOG_VIEW_LOOKUPS: true,
|
|
||||||
modulePrefix: config.modulePrefix,
|
|
||||||
podModulePrefix: config.podModulePrefix,
|
|
||||||
Resolver: Resolver,
|
|
||||||
|
|
||||||
flash(options) {
|
|
||||||
return Ember.getOwner(Travis).lookup('controller:flash').loadFlashes([options]);
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleSidebar() {
|
|
||||||
var element;
|
|
||||||
$('body').toggleClass('maximized');
|
|
||||||
element = $('<span></span>');
|
|
||||||
$('#top .profile').append(element);
|
|
||||||
Ember.run.later((function() {
|
|
||||||
return element.remove();
|
|
||||||
}), 10);
|
|
||||||
element = $('<span></span>');
|
|
||||||
$('#repo').append(element);
|
|
||||||
return Ember.run.later((function() {
|
|
||||||
return element.remove();
|
|
||||||
}), 10);
|
|
||||||
},
|
|
||||||
|
|
||||||
ready() {
|
|
||||||
if (location.hash.slice(0, 2) === '#!') {
|
|
||||||
location.href = location.href.replace('#!/', '');
|
|
||||||
}
|
|
||||||
this.on('user:signed_in', function(user) {
|
|
||||||
return Travis.onUserUpdate(user);
|
|
||||||
});
|
|
||||||
this.on('user:refreshed', function(user) {
|
|
||||||
return Travis.onUserUpdate(user);
|
|
||||||
});
|
|
||||||
this.on('user:synced', function(user) {
|
|
||||||
return Travis.onUserUpdate(user);
|
|
||||||
});
|
|
||||||
return this.on('user:signed_out', function() {
|
|
||||||
if (config.userlike) {
|
|
||||||
return Travis.removeUserlike();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
currentDate() {
|
|
||||||
return new Date();
|
|
||||||
},
|
|
||||||
|
|
||||||
onUserUpdate(user) {
|
|
||||||
if (config.pro) {
|
|
||||||
this.identifyCustomer(user);
|
|
||||||
}
|
|
||||||
if (config.userlike) {
|
|
||||||
this.setupUserlike(user);
|
|
||||||
}
|
|
||||||
return this.subscribePusher(user);
|
|
||||||
},
|
|
||||||
|
|
||||||
subscribePusher(user) {
|
|
||||||
var channels;
|
|
||||||
if (!user.channels) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
channels = user.channels;
|
|
||||||
if (config.pro) {
|
|
||||||
channels = channels.map(function(channel) {
|
|
||||||
if (channel.match(/^private-/)) {
|
|
||||||
return channel;
|
|
||||||
} else {
|
|
||||||
return "private-" + channel;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Travis.pusher.subscribeAll(channels);
|
|
||||||
},
|
|
||||||
|
|
||||||
setupUserlike(user) {
|
|
||||||
var btn, s, userlikeData;
|
|
||||||
btn = document.getElementById('userlikeCustomTab');
|
|
||||||
btn.classList.add("logged-in");
|
|
||||||
userlikeData = window.userlikeData = {};
|
|
||||||
userlikeData.user = {};
|
|
||||||
userlikeData.user.name = user.login;
|
|
||||||
userlikeData.user.email = user.email;
|
|
||||||
if (!document.getElementById('userlike-script')) {
|
|
||||||
s = document.createElement('script');
|
|
||||||
s.id = 'userlike-script';
|
|
||||||
s.src = '//userlike-cdn-widgets.s3-eu-west-1.amazonaws.com/0327dbb23382ccbbb91b445b76e8a91d4b37d90ef9f2faf84e11177847ff7bb9.js';
|
|
||||||
return document.body.appendChild(s);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
removeUserlike() {
|
|
||||||
var btn;
|
|
||||||
btn = document.getElementById('userlikeCustomTab');
|
|
||||||
return btn.classList.remove("logged-in");
|
|
||||||
},
|
|
||||||
|
|
||||||
identifyCustomer(user) {
|
|
||||||
if (_cio && _cio.identify) {
|
|
||||||
return _cio.identify({
|
|
||||||
id: user.id,
|
|
||||||
email: user.email,
|
|
||||||
name: user.name,
|
|
||||||
created_at: (Date.parse(user.created_at) / 1000) || null,
|
|
||||||
login: user.login
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
loadInitializers(App, config.modulePrefix);
|
|
||||||
|
|
||||||
export default App;
|
|
46
app/components/add-env-var.coffee
Normal file
46
app/components/add-env-var.coffee
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
AddEnvVarComponent = Ember.Component.extend
|
||||||
|
|
||||||
|
classNames: ['form--envvar']
|
||||||
|
classNameBindings: ['nameIsBlank:form-error']
|
||||||
|
|
||||||
|
store: Ember.inject.service()
|
||||||
|
|
||||||
|
isValid: () ->
|
||||||
|
if Ember.isBlank(@get('name'))
|
||||||
|
this.set('nameIsBlank', true)
|
||||||
|
false
|
||||||
|
else
|
||||||
|
true
|
||||||
|
|
||||||
|
reset: ->
|
||||||
|
@setProperties(name: null, value: null, public: null)
|
||||||
|
|
||||||
|
actions:
|
||||||
|
save: ->
|
||||||
|
return if @get('isSaving')
|
||||||
|
@set('isSaving', true)
|
||||||
|
|
||||||
|
if @isValid()
|
||||||
|
env_var = @get('store').createRecord('env_var',
|
||||||
|
name: @get('name')
|
||||||
|
value: @get('value')
|
||||||
|
public: @get('public')
|
||||||
|
repo: @get('repo')
|
||||||
|
)
|
||||||
|
|
||||||
|
self = this
|
||||||
|
env_var.save().then =>
|
||||||
|
@set('isSaving', false)
|
||||||
|
@reset()
|
||||||
|
, =>
|
||||||
|
@set('isSaving', false)
|
||||||
|
else
|
||||||
|
@set('isSaving', false)
|
||||||
|
|
||||||
|
nameChanged: ->
|
||||||
|
this.set('nameIsBlank', false)
|
||||||
|
|
||||||
|
|
||||||
|
`export default AddEnvVarComponent`
|
|
@ -1,55 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
classNames: ['form--envvar'],
|
|
||||||
classNameBindings: ['nameIsBlank:form-error'],
|
|
||||||
store: Ember.inject.service(),
|
|
||||||
|
|
||||||
isValid() {
|
|
||||||
if (Ember.isBlank(this.get('name'))) {
|
|
||||||
this.set('nameIsBlank', true);
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
reset() {
|
|
||||||
return this.setProperties({
|
|
||||||
name: null,
|
|
||||||
value: null,
|
|
||||||
"public": null
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
save() {
|
|
||||||
var env_var, self;
|
|
||||||
if (this.get('isSaving')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.set('isSaving', true);
|
|
||||||
if (this.isValid()) {
|
|
||||||
env_var = this.get('store').createRecord('env_var', {
|
|
||||||
name: this.get('name'),
|
|
||||||
value: this.get('value'),
|
|
||||||
"public": this.get('public'),
|
|
||||||
repo: this.get('repo')
|
|
||||||
});
|
|
||||||
self = this;
|
|
||||||
return env_var.save().then(() => {
|
|
||||||
this.set('isSaving', false);
|
|
||||||
return this.reset();
|
|
||||||
}, () => {
|
|
||||||
return this.set('isSaving', false);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return this.set('isSaving', false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
nameChanged() {
|
|
||||||
return this.set('nameIsBlank', false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
73
app/components/add-ssh-key.coffee
Normal file
73
app/components/add-ssh-key.coffee
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
# `import Ember from 'ember'`
|
||||||
|
|
||||||
|
AddSshKeyComponent = Ember.Component.extend
|
||||||
|
|
||||||
|
classNames: ['form--sshkey']
|
||||||
|
classNameBindings: ['valueError:form-error']
|
||||||
|
|
||||||
|
store: Ember.inject.service()
|
||||||
|
isSaving: false
|
||||||
|
|
||||||
|
didInsertElement: () ->
|
||||||
|
id = @get('repo.id')
|
||||||
|
model = @get('store').recordForId('ssh_key', id)
|
||||||
|
# TODO: this can be removed in favor of simply unloading record
|
||||||
|
# once https://github.com/emberjs/data/pull/2867
|
||||||
|
# and https://github.com/emberjs/data/pull/2870 are merged
|
||||||
|
if model
|
||||||
|
@get('store').dematerializeRecord(model._internalModel)
|
||||||
|
typeMap = @get('store').typeMapFor(model.constructor)
|
||||||
|
idToRecord = typeMap.idToRecord
|
||||||
|
delete idToRecord[id]
|
||||||
|
|
||||||
|
model = @get('store').createRecord('ssh_key', id: id)
|
||||||
|
@set('model', model)
|
||||||
|
|
||||||
|
isValid: () ->
|
||||||
|
if Ember.isBlank(@get('value'))
|
||||||
|
this.set('valueError', 'Value can\'t be blank.')
|
||||||
|
false
|
||||||
|
else
|
||||||
|
true
|
||||||
|
|
||||||
|
reset: ->
|
||||||
|
@setProperties(description: null, value: null)
|
||||||
|
|
||||||
|
valueChanged: (->
|
||||||
|
this.set('valueError', false)
|
||||||
|
).observes('value')
|
||||||
|
|
||||||
|
addErrorsFromResponse: (errArr) ->
|
||||||
|
error = errArr[0].detail
|
||||||
|
if error.code == 'not_a_private_key'
|
||||||
|
this.set('valueError', 'This key is not a private key.')
|
||||||
|
else if error.code == 'key_with_a_passphrase'
|
||||||
|
this.set('valueError', 'The key can\'t have a passphrase.')
|
||||||
|
|
||||||
|
actions:
|
||||||
|
|
||||||
|
save: ->
|
||||||
|
this.set('valueError', false)
|
||||||
|
return if @get('isSaving')
|
||||||
|
@set('isSaving', true)
|
||||||
|
if @isValid()
|
||||||
|
|
||||||
|
ssh_key = @get('model').setProperties(
|
||||||
|
description: @get('description')
|
||||||
|
value: @get('value')
|
||||||
|
)
|
||||||
|
|
||||||
|
ssh_key.save().then =>
|
||||||
|
@set('isSaving', false)
|
||||||
|
@reset()
|
||||||
|
|
||||||
|
@sendAction('sshKeyAdded', ssh_key)
|
||||||
|
, (error) =>
|
||||||
|
@set('isSaving', false)
|
||||||
|
if error.errors
|
||||||
|
@addErrorsFromResponse(error.errors)
|
||||||
|
|
||||||
|
else
|
||||||
|
@set('isSaving', false)
|
||||||
|
|
||||||
|
`export default AddSshKeyComponent`
|
|
@ -1,85 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
classNames: ['form--sshkey'],
|
|
||||||
classNameBindings: ['valueError:form-error'],
|
|
||||||
store: Ember.inject.service(),
|
|
||||||
isSaving: false,
|
|
||||||
|
|
||||||
didInsertElement() {
|
|
||||||
var id = this.get('repo.id');
|
|
||||||
var model = this.get('store').recordForId('ssh_key', id);
|
|
||||||
|
|
||||||
if (model) {
|
|
||||||
this.get('store').unloadRecord(model);
|
|
||||||
var typeMap = this.get('store').typeMapFor(model.constructor);
|
|
||||||
var idToRecord = typeMap.idToRecord;
|
|
||||||
delete idToRecord[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
model = this.get('store').createRecord('ssh_key', { id: id });
|
|
||||||
|
|
||||||
return this.set('model', model);
|
|
||||||
},
|
|
||||||
|
|
||||||
isValid() {
|
|
||||||
if (Ember.isBlank(this.get('value'))) {
|
|
||||||
this.set('valueError', 'Value can\'t be blank.');
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
reset() {
|
|
||||||
return this.setProperties({
|
|
||||||
description: null,
|
|
||||||
value: null
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
valueChanged: function() {
|
|
||||||
return this.set('valueError', false);
|
|
||||||
}.observes('value'),
|
|
||||||
|
|
||||||
addErrorsFromResponse(errArr) {
|
|
||||||
var error = errArr[0].detail;
|
|
||||||
|
|
||||||
if (error.code === 'not_a_private_key') {
|
|
||||||
return this.set('valueError', 'This key is not a private key.');
|
|
||||||
} else if (error.code === 'key_with_a_passphrase') {
|
|
||||||
return this.set('valueError', 'The key can\'t have a passphrase.');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
save() {
|
|
||||||
var ssh_key;
|
|
||||||
|
|
||||||
this.set('valueError', false);
|
|
||||||
if (this.get('isSaving')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.set('isSaving', true);
|
|
||||||
if (this.isValid()) {
|
|
||||||
ssh_key = this.get('model');
|
|
||||||
ssh_key.setProperties({
|
|
||||||
description: this.get('description'),
|
|
||||||
value: this.get('value')
|
|
||||||
});
|
|
||||||
return ssh_key.save().then(() => {
|
|
||||||
this.set('isSaving', false);
|
|
||||||
this.reset();
|
|
||||||
return this.sendAction('sshKeyAdded', ssh_key);
|
|
||||||
}, (error) => {
|
|
||||||
this.set('isSaving', false);
|
|
||||||
if (error.errors) {
|
|
||||||
return this.addErrorsFromResponse(error.errors);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return this.set('isSaving', false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
91
app/components/branch-row.coffee
Normal file
91
app/components/branch-row.coffee
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
`import { gravatarImage } from 'travis/utils/urls'`
|
||||||
|
`import { githubCommit as githubCommitUrl } from 'travis/utils/urls'`
|
||||||
|
`import TravisRoute from 'travis/routes/basic'`
|
||||||
|
`import config from 'travis/config/environment'`
|
||||||
|
|
||||||
|
BranchRowComponent = Ember.Component.extend
|
||||||
|
routing: Ember.inject.service('-routing')
|
||||||
|
tagName: 'li'
|
||||||
|
classNameBindings: ['build.last_build.state']
|
||||||
|
classNames: ['branch-row', 'row-li']
|
||||||
|
isLoading: false
|
||||||
|
isTriggering: false
|
||||||
|
hasTriggered: false
|
||||||
|
|
||||||
|
urlGithubCommit: (->
|
||||||
|
githubCommitUrl(@get('build.repository.slug'), @get('build.last_build.commit.sha'))
|
||||||
|
).property('build.last_build')
|
||||||
|
|
||||||
|
getLast5Builds: (->
|
||||||
|
|
||||||
|
lastBuilds = Ember.ArrayProxy.create(
|
||||||
|
content: [{}, {}, {}, {}, {}]
|
||||||
|
isLoading: true,
|
||||||
|
count: 0
|
||||||
|
)
|
||||||
|
|
||||||
|
if !@get('build.last_build')
|
||||||
|
lastBuilds.set('isLoading', false)
|
||||||
|
else
|
||||||
|
apiEndpoint = config.apiEndpoint
|
||||||
|
repoId = @get('build.repository.id')
|
||||||
|
branchName = @get('build.name')
|
||||||
|
|
||||||
|
options = {}
|
||||||
|
if @get('auth.signedIn')
|
||||||
|
options.headers = { Authorization: "token #{@auth.token()}" }
|
||||||
|
|
||||||
|
$.ajax("#{apiEndpoint}/v3/repo/#{repoId}/builds?branch.name=#{branchName}&limit=5&build.event_type=push,api", options).then (response) ->
|
||||||
|
array = response.builds.map( (build) ->
|
||||||
|
Ember.Object.create(build)
|
||||||
|
)
|
||||||
|
if array.length < 5
|
||||||
|
for i in [1..5 - array.length] by 1
|
||||||
|
array.push({})
|
||||||
|
|
||||||
|
lastBuilds.set('count', response['@pagination'].count)
|
||||||
|
lastBuilds.set('content', array)
|
||||||
|
lastBuilds.set('isLoading', false)
|
||||||
|
|
||||||
|
lastBuilds
|
||||||
|
).property()
|
||||||
|
|
||||||
|
canTrigger: (->
|
||||||
|
if !@get('auth.signedIn')
|
||||||
|
false
|
||||||
|
else
|
||||||
|
permissions = @get('auth.currentUser.permissions')
|
||||||
|
if permissions.contains parseInt(@get('build.repository.id'))
|
||||||
|
true
|
||||||
|
else
|
||||||
|
false
|
||||||
|
).property()
|
||||||
|
|
||||||
|
triggerBuild: (->
|
||||||
|
apiEndpoint = config.apiEndpoint
|
||||||
|
repoId = @get('build.repository.id')
|
||||||
|
options = {
|
||||||
|
type: 'POST',
|
||||||
|
body: {
|
||||||
|
request: {
|
||||||
|
branch: @get('build.name')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if @get('auth.signedIn')
|
||||||
|
options.headers = { Authorization: "token #{@auth.token()}" }
|
||||||
|
$.ajax("#{apiEndpoint}/v3/repo/#{repoId}/requests", options).then (response) =>
|
||||||
|
@set('isTriggering', false)
|
||||||
|
@set('hasTriggered', true)
|
||||||
|
)
|
||||||
|
|
||||||
|
actions:
|
||||||
|
tiggerBuild: (branch) ->
|
||||||
|
@set('isTriggering', true)
|
||||||
|
@triggerBuild()
|
||||||
|
|
||||||
|
viewAllBuilds: (branch) ->
|
||||||
|
@get('routing').transitionTo('builds')
|
||||||
|
|
||||||
|
`export default BranchRowComponent`
|
|
@ -1,103 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import { githubCommit as githubCommitUrl } from 'travis/utils/urls';
|
|
||||||
import TravisRoute from 'travis/routes/basic';
|
|
||||||
import config from 'travis/config/environment';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
routing: Ember.inject.service('-routing'),
|
|
||||||
tagName: 'li',
|
|
||||||
classNameBindings: ['build.last_build.state'],
|
|
||||||
classNames: ['branch-row', 'row-li'],
|
|
||||||
isLoading: false,
|
|
||||||
isTriggering: false,
|
|
||||||
hasTriggered: false,
|
|
||||||
|
|
||||||
urlGithubCommit: function() {
|
|
||||||
return githubCommitUrl(this.get('build.repository.slug'), this.get('build.last_build.commit.sha'));
|
|
||||||
}.property('build.last_build'),
|
|
||||||
|
|
||||||
getLast5Builds: function() {
|
|
||||||
var apiEndpoint, branchName, lastBuilds, options, repoId;
|
|
||||||
lastBuilds = Ember.ArrayProxy.create({
|
|
||||||
content: [{}, {}, {}, {}, {}],
|
|
||||||
isLoading: true,
|
|
||||||
count: 0
|
|
||||||
});
|
|
||||||
if (!this.get('build.last_build')) {
|
|
||||||
lastBuilds.set('isLoading', false);
|
|
||||||
} else {
|
|
||||||
apiEndpoint = config.apiEndpoint;
|
|
||||||
repoId = this.get('build.repository.id');
|
|
||||||
branchName = this.get('build.name');
|
|
||||||
options = {};
|
|
||||||
if (this.get('auth.signedIn')) {
|
|
||||||
options.headers = {
|
|
||||||
Authorization: "token " + (this.auth.token())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
$.ajax(apiEndpoint + "/v3/repo/" + repoId + "/builds?branch.name=" + branchName + "&limit=5&build.event_type=push,api", options).then(function(response) {
|
|
||||||
var array, i, j, ref;
|
|
||||||
array = response.builds.map(function(build) {
|
|
||||||
return Ember.Object.create(build);
|
|
||||||
});
|
|
||||||
if (array.length < 5) {
|
|
||||||
for (i = j = 1, ref = 5 - array.length; j <= ref; i = j += 1) {
|
|
||||||
array.push({});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastBuilds.set('count', response['@pagination'].count);
|
|
||||||
lastBuilds.set('content', array);
|
|
||||||
return lastBuilds.set('isLoading', false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return lastBuilds;
|
|
||||||
}.property(),
|
|
||||||
|
|
||||||
canTrigger: function() {
|
|
||||||
var permissions;
|
|
||||||
if (!this.get('auth.signedIn')) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
permissions = this.get('auth.currentUser.permissions');
|
|
||||||
if (permissions.contains(parseInt(this.get('build.repository.id')))) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.property(),
|
|
||||||
|
|
||||||
triggerBuild: function() {
|
|
||||||
var apiEndpoint, options, repoId;
|
|
||||||
apiEndpoint = config.apiEndpoint;
|
|
||||||
repoId = this.get('build.repository.id');
|
|
||||||
options = {
|
|
||||||
type: 'POST',
|
|
||||||
body: {
|
|
||||||
request: {
|
|
||||||
branch: this.get('build.name')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (this.get('auth.signedIn')) {
|
|
||||||
options.headers = {
|
|
||||||
Authorization: "token " + (this.auth.token())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return $.ajax(apiEndpoint + "/v3/repo/" + repoId + "/requests", options).then(() => {
|
|
||||||
this.set('isTriggering', false);
|
|
||||||
return this.set('hasTriggered', true);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
tiggerBuild(branch) {
|
|
||||||
this.set('isTriggering', true);
|
|
||||||
return this.triggerBuild();
|
|
||||||
},
|
|
||||||
|
|
||||||
viewAllBuilds(branch) {
|
|
||||||
return this.get('routing').transitionTo('builds');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
24
app/components/broadcast-tower.coffee
Normal file
24
app/components/broadcast-tower.coffee
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
BroadcastTowerComponent = Ember.Component.extend
|
||||||
|
|
||||||
|
classNames: ['broadcast']
|
||||||
|
|
||||||
|
isOpen: false
|
||||||
|
timeoutId: ''
|
||||||
|
|
||||||
|
actions:
|
||||||
|
toggleBroadcasts:() ->
|
||||||
|
@toggleProperty('isOpen')
|
||||||
|
@sendAction('toggleBroadcasts')
|
||||||
|
|
||||||
|
if @get('isOpen') == true
|
||||||
|
@set('timeoutId', setTimeout =>
|
||||||
|
@toggleProperty('isOpen')
|
||||||
|
@sendAction('toggleBroadcasts')
|
||||||
|
, 10000
|
||||||
|
)
|
||||||
|
else
|
||||||
|
clearTimeout(@get('timeoutId'))
|
||||||
|
|
||||||
|
`export default BroadcastTowerComponent`
|
|
@ -1,21 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
classNames: ['broadcast'],
|
|
||||||
isOpen: false,
|
|
||||||
timeoutId: '',
|
|
||||||
actions: {
|
|
||||||
toggleBroadcasts() {
|
|
||||||
this.toggleProperty('isOpen');
|
|
||||||
this.sendAction('toggleBroadcasts');
|
|
||||||
if (this.get('isOpen') === true) {
|
|
||||||
return this.set('timeoutId', setTimeout(() => {
|
|
||||||
this.toggleProperty('isOpen');
|
|
||||||
return this.sendAction('toggleBroadcasts');
|
|
||||||
}, 10000));
|
|
||||||
} else {
|
|
||||||
return clearTimeout(this.get('timeoutId'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,27 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import { gravatarImage } from 'travis/utils/urls';
|
|
||||||
import GithubUrlProperties from 'travis/mixins/github-url-properties';
|
|
||||||
import { durationFrom, safe } from 'travis/utils/helpers';
|
|
||||||
import { githubCommit } from 'travis/utils/urls';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
tagName: 'section',
|
|
||||||
classNames: ['build-header'],
|
|
||||||
classNameBindings: ['item.state'],
|
|
||||||
|
|
||||||
isJob: function() {
|
|
||||||
if (this.get('item.build')) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}.property('item'),
|
|
||||||
|
|
||||||
urlGithubCommit: function() {
|
|
||||||
return githubCommit(this.get('repo.slug'), this.get('commit.sha'));
|
|
||||||
}.property('item'),
|
|
||||||
|
|
||||||
elapsedTime: function() {
|
|
||||||
return durationFrom(this.get('item.startedAt'), this.get('item.finishedAt'));
|
|
||||||
}.property('item.startedAt', 'item.finishedAt', 'item.duration')
|
|
||||||
});
|
|
9
app/components/build-repo-actions.coffee
Normal file
9
app/components/build-repo-actions.coffee
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
`import RepoActionsItemComponentMixin from 'travis/utils/repo-actions-item-component-mixin'`
|
||||||
|
|
||||||
|
BuildRepoActionsComponent = Ember.Component.extend(RepoActionsItemComponentMixin,
|
||||||
|
item: Ember.computed.alias('build')
|
||||||
|
type: 'build'
|
||||||
|
)
|
||||||
|
|
||||||
|
`export default BuildRepoActionsComponent`
|
|
@ -1,7 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import RepoActionsItemComponentMixin from 'travis/utils/repo-actions-item-component-mixin';
|
|
||||||
|
|
||||||
export default Ember.Component.extend(RepoActionsItemComponentMixin, {
|
|
||||||
item: Ember.computed.alias('build'),
|
|
||||||
type: 'build'
|
|
||||||
});
|
|
14
app/components/build-tile.coffee
Normal file
14
app/components/build-tile.coffee
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
BuildTileComponent = Ember.Component.extend
|
||||||
|
|
||||||
|
tagName: 'li'
|
||||||
|
classNameBindings: ['build.state']
|
||||||
|
attributeBindings: ['title'],
|
||||||
|
|
||||||
|
title: (->
|
||||||
|
num = @get('build.number')
|
||||||
|
"##{num}"
|
||||||
|
).property('build')
|
||||||
|
|
||||||
|
`export default BuildTileComponent`
|
|
@ -1,14 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
tagName: 'li',
|
|
||||||
classNameBindings: ['build.state'],
|
|
||||||
attributeBindings: ['title'],
|
|
||||||
|
|
||||||
title: function() {
|
|
||||||
var num, state;
|
|
||||||
num = this.get('build.number');
|
|
||||||
state = this.get('build.state');
|
|
||||||
return "Build #" + num + " " + state;
|
|
||||||
}.property('build')
|
|
||||||
});
|
|
|
@ -1,12 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import { colorForState } from 'travis/utils/helpers';
|
|
||||||
import Polling from 'travis/mixins/polling';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
classNameBindings: ['color'],
|
|
||||||
pollModels: 'build',
|
|
||||||
|
|
||||||
color: function() {
|
|
||||||
return colorForState(this.get('build.state'));
|
|
||||||
}.property('build.state')
|
|
||||||
});
|
|
17
app/components/builds-item.coffee
Normal file
17
app/components/builds-item.coffee
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
`import { gravatarImage } from 'travis/utils/urls'`
|
||||||
|
`import { githubCommit as githubCommitUrl } from 'travis/utils/urls'`
|
||||||
|
|
||||||
|
BuildsItemComponent = Ember.Component.extend
|
||||||
|
classNameBindings: ['build.state']
|
||||||
|
classNames: ['tile', 'tile--small', 'tile--build', 'row']
|
||||||
|
|
||||||
|
urlAuthorGravatarImage: (->
|
||||||
|
gravatarImage(@get('build.commit.authorEmail'), 40)
|
||||||
|
).property('build.commit.authorEmail')
|
||||||
|
|
||||||
|
urlGithubCommit: (->
|
||||||
|
githubCommitUrl(@get('build.repo.slug'), @get('build.commit.sha'))
|
||||||
|
).property('build.commit.sha')
|
||||||
|
|
||||||
|
`export default BuildsItemComponent`
|
|
@ -1,12 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import { githubCommit as githubCommitUrl } from 'travis/utils/urls';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
tagName: 'li',
|
|
||||||
classNameBindings: ['build.state'],
|
|
||||||
classNames: ['row-li', 'pr-row'],
|
|
||||||
|
|
||||||
urlGithubCommit: function() {
|
|
||||||
return githubCommitUrl(this.get('build.repo.slug'), this.get('build.commit.sha'));
|
|
||||||
}.property('build.commit.sha')
|
|
||||||
});
|
|
|
@ -1,30 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import Polling from 'travis/mixins/polling';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
store: Ember.inject.service('store'),
|
|
||||||
|
|
||||||
pollHook: function(store) {
|
|
||||||
var contentType, repositoryId;
|
|
||||||
contentType = this.get('contentType');
|
|
||||||
repositoryId = this.get('repo.id');
|
|
||||||
store = this.get('store');
|
|
||||||
|
|
||||||
if (contentType === 'builds') {
|
|
||||||
return store.query('build', {
|
|
||||||
event_type: 'push',
|
|
||||||
repository_id: repositoryId
|
|
||||||
});
|
|
||||||
} else if (contentType === 'pull_requests') {
|
|
||||||
return store.filter('build', {
|
|
||||||
event_type: 'pull_request',
|
|
||||||
repository_id: repositoryId
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return store.query('build', {
|
|
||||||
repository_id: repositoryId,
|
|
||||||
branches: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
27
app/components/caches-item.coffee
Normal file
27
app/components/caches-item.coffee
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
CachesItemComponent = Ember.Component.extend
|
||||||
|
ajax: Ember.inject.service()
|
||||||
|
|
||||||
|
tagName: 'li'
|
||||||
|
classNames: ['tile', 'tile--xs', 'row']
|
||||||
|
classNameBindings: ['cache.type']
|
||||||
|
isDeleting: false
|
||||||
|
|
||||||
|
actions:
|
||||||
|
delete: ->
|
||||||
|
return if @get('isDeleting')
|
||||||
|
|
||||||
|
if confirm('Are you sure?')
|
||||||
|
@set('isDeleting', true)
|
||||||
|
|
||||||
|
data = { branch: @get('cache.branch') }
|
||||||
|
|
||||||
|
deletingDone = => @set('isDeleting', false)
|
||||||
|
|
||||||
|
repo = @get('repo')
|
||||||
|
@get('ajax').ajax("/repos/#{repo.get('id')}/caches", "DELETE", data: data).then(deletingDone, deletingDone).then =>
|
||||||
|
@get('caches').removeObject(@get('cache'))
|
||||||
|
|
||||||
|
|
||||||
|
`export default CachesItemComponent`
|
|
@ -1,33 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
ajax: Ember.inject.service(),
|
|
||||||
tagName: 'li',
|
|
||||||
classNames: ['cache-item'],
|
|
||||||
classNameBindings: ['cache.type'],
|
|
||||||
isDeleting: false,
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
"delete": function() {
|
|
||||||
var data, deletingDone, repo;
|
|
||||||
if (this.get('isDeleting')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (confirm('Are you sure?')) {
|
|
||||||
this.set('isDeleting', true);
|
|
||||||
data = {
|
|
||||||
branch: this.get('cache.branch')
|
|
||||||
};
|
|
||||||
deletingDone = () => {
|
|
||||||
return this.set('isDeleting', false);
|
|
||||||
};
|
|
||||||
repo = this.get('repo');
|
|
||||||
return this.get('ajax').ajax("/repos/" + (repo.get('id')) + "/caches", "DELETE", {
|
|
||||||
data: data
|
|
||||||
}).then(deletingDone, deletingDone).then(() => {
|
|
||||||
return this.get('caches').removeObject(this.get('cache'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
10
app/components/code-climate-popup.coffee
Normal file
10
app/components/code-climate-popup.coffee
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
Component = Ember.Component.extend(
|
||||||
|
actions:
|
||||||
|
close: ->
|
||||||
|
$('.popup').removeClass('display')
|
||||||
|
return false
|
||||||
|
)
|
||||||
|
|
||||||
|
`export default Component`
|
55
app/components/dashboard-row.coffee
Normal file
55
app/components/dashboard-row.coffee
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
`import { githubCommit as githubCommitUrl } from 'travis/utils/urls'`
|
||||||
|
`import config from 'travis/config/environment'`
|
||||||
|
|
||||||
|
DashboardRowComponent = Ember.Component.extend
|
||||||
|
|
||||||
|
tagName: 'li'
|
||||||
|
classNameBindings: ['repo.default_branch.last_build.state']
|
||||||
|
classNames: ['dashboard-row', 'row-li']
|
||||||
|
isLoading: false
|
||||||
|
isTriggering: false
|
||||||
|
hasTriggered: false
|
||||||
|
|
||||||
|
urlGithubCommit: (->
|
||||||
|
githubCommitUrl(@get('repo.slug'), @get('repo.default_branch.last_build.commit.sha'))
|
||||||
|
).property('repo')
|
||||||
|
|
||||||
|
# canTrigger: (->
|
||||||
|
# if !@get('auth.signedIn')
|
||||||
|
# false
|
||||||
|
# else
|
||||||
|
# permissions = @get('auth.currentUser.permissions')
|
||||||
|
# if permissions.contains parseInt(@get('build.repository.id'))
|
||||||
|
# true
|
||||||
|
# else
|
||||||
|
# false
|
||||||
|
# ).property()
|
||||||
|
|
||||||
|
# triggerBuild: (->
|
||||||
|
# apiEndpoint = config.apiEndpoint
|
||||||
|
# repoId = @get('build.repository.id')
|
||||||
|
# options = {
|
||||||
|
# type: 'POST',
|
||||||
|
# body: {
|
||||||
|
# request: {
|
||||||
|
# branch: @get('build.name')
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# if @get('auth.signedIn')
|
||||||
|
# options.headers = { Authorization: "token #{@auth.token()}" }
|
||||||
|
# $.ajax("#{apiEndpoint}/v3/repo/#{repoId}/requests", options).then (response) =>
|
||||||
|
# @set('isTriggering', false)
|
||||||
|
# @set('hasTriggered', true)
|
||||||
|
# )
|
||||||
|
|
||||||
|
actions:
|
||||||
|
tiggerBuild: (branch) ->
|
||||||
|
@set('isTriggering', true)
|
||||||
|
@triggerBuild()
|
||||||
|
|
||||||
|
# viewAllBuilds: (branch) ->
|
||||||
|
# @get('routing').transitionTo('builds')
|
||||||
|
|
||||||
|
`export default DashboardRowComponent`
|
|
@ -1,36 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import { githubCommit as githubCommitUrl } from 'travis/utils/urls';
|
|
||||||
import config from 'travis/config/environment';
|
|
||||||
import { hasAdminPermission, hasPushPermission } from 'travis/utils/permission';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
tagName: 'li',
|
|
||||||
classNameBindings: ['repo.default_branch.last_build.state'],
|
|
||||||
classNames: ['rows', 'rows--dashboard'],
|
|
||||||
isLoading: false,
|
|
||||||
isTriggering: false,
|
|
||||||
hasTriggered: false,
|
|
||||||
dropupIsOpen: false,
|
|
||||||
|
|
||||||
urlGithubCommit: function() {
|
|
||||||
return githubCommitUrl(this.get('repo.slug'), this.get('repo.default_branch.last_build.commit.sha'));
|
|
||||||
}.property('repo'),
|
|
||||||
|
|
||||||
displayMenuTofu: function() {
|
|
||||||
return hasPushPermission(this.get('currentUser'), this.get('repo.id'));
|
|
||||||
},
|
|
||||||
|
|
||||||
displayActivateLink: function() {
|
|
||||||
return hasAdminPermission(this.get('currentUser'), this.get('repo.id'));
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
tiggerBuild(branch) {
|
|
||||||
this.set('isTriggering', true);
|
|
||||||
return this.triggerBuild();
|
|
||||||
},
|
|
||||||
openDropup() {
|
|
||||||
this.toggleProperty('dropupIsOpen');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
29
app/components/env-var.coffee
Normal file
29
app/components/env-var.coffee
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
EnvVarComponent = Ember.Component.extend
|
||||||
|
|
||||||
|
classNames: ['settings-envvar']
|
||||||
|
classNameBindings: ['envVar.public:is-public']
|
||||||
|
|
||||||
|
isDeleting: false
|
||||||
|
|
||||||
|
validates:
|
||||||
|
name: ['presence']
|
||||||
|
|
||||||
|
actionType: 'Save'
|
||||||
|
showValueField: Ember.computed.alias('public')
|
||||||
|
|
||||||
|
value: ( (key, value) ->
|
||||||
|
if @get('envVar.public')
|
||||||
|
@get('envVar.value')
|
||||||
|
else
|
||||||
|
'••••••••••••••••'
|
||||||
|
).property('envVar.value', 'envVar.public')
|
||||||
|
|
||||||
|
actions:
|
||||||
|
delete: ->
|
||||||
|
return if @get('isDeleting')
|
||||||
|
@set('isDeleting', true)
|
||||||
|
@get('envVar').destroyRecord()
|
||||||
|
|
||||||
|
`export default EnvVarComponent`
|
|
@ -1,28 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
classNames: ['settings-envvar'],
|
|
||||||
classNameBindings: ['envVar.public:is-public'],
|
|
||||||
isDeleting: false,
|
|
||||||
validates: { name: ['presence'] },
|
|
||||||
actionType: 'Save',
|
|
||||||
showValueField: Ember.computed.alias('public'),
|
|
||||||
|
|
||||||
value: function(key) {
|
|
||||||
if (this.get('envVar.public')) {
|
|
||||||
return this.get('envVar.value');
|
|
||||||
} else {
|
|
||||||
return '••••••••••••••••';
|
|
||||||
}
|
|
||||||
}.property('envVar.value', 'envVar.public'),
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
"delete": function() {
|
|
||||||
if (this.get('isDeleting')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.set('isDeleting', true);
|
|
||||||
return this.get('envVar').destroyRecord();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
8
app/components/eye-icon.coffee
Normal file
8
app/components/eye-icon.coffee
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
EyeIconComponent = Ember.Component.extend
|
||||||
|
|
||||||
|
tagName: 'span'
|
||||||
|
classNames: ['icon-eye']
|
||||||
|
|
||||||
|
`export default EyeIconComponent`
|
|
@ -1,6 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
tagName: 'span',
|
|
||||||
classNames: ['icon-eye']
|
|
||||||
});
|
|
15
app/components/flash-display.coffee
Normal file
15
app/components/flash-display.coffee
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
FlashDisplayComponent = Ember.Component.extend
|
||||||
|
flashes: Ember.inject.service()
|
||||||
|
|
||||||
|
classNames: ['flash']
|
||||||
|
tagName: 'ul'
|
||||||
|
|
||||||
|
messagesBinding: 'flashes.messages'
|
||||||
|
|
||||||
|
actions:
|
||||||
|
closeMessage: (msg) ->
|
||||||
|
@get('flashes').close(msg)
|
||||||
|
|
||||||
|
`export default FlashDisplayComponent`
|
|
@ -1,14 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
flashes: Ember.inject.service(),
|
|
||||||
classNames: ['flash'],
|
|
||||||
tagName: 'ul',
|
|
||||||
messagesBinding: 'flashes.messages',
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
closeMessage(msg) {
|
|
||||||
return this.get('flashes').close(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
15
app/components/flash-item.coffee
Normal file
15
app/components/flash-item.coffee
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
FlashItemComponent = Ember.Component.extend
|
||||||
|
tagName: 'li'
|
||||||
|
classNameBindings: ['type']
|
||||||
|
|
||||||
|
type: (->
|
||||||
|
@get('flash.type') || 'broadcast'
|
||||||
|
).property('flash.type')
|
||||||
|
|
||||||
|
actions:
|
||||||
|
close: ->
|
||||||
|
this.attrs.close(@get('flash'))
|
||||||
|
|
||||||
|
`export default FlashItemComponent`
|
|
@ -1,16 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
tagName: 'li',
|
|
||||||
classNameBindings: ['type'],
|
|
||||||
|
|
||||||
type: function() {
|
|
||||||
return this.get('flash.type') || 'broadcast';
|
|
||||||
}.property('flash.type'),
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
close() {
|
|
||||||
return this.attrs.close(this.get('flash'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
18
app/components/hook-switch.coffee
Normal file
18
app/components/hook-switch.coffee
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
HookSwitchComponent = Ember.Component.extend
|
||||||
|
tagName: 'a'
|
||||||
|
classNames: ['switch--icon']
|
||||||
|
classNameBindings: ['active']
|
||||||
|
activeBinding: "hook.active"
|
||||||
|
|
||||||
|
click: ->
|
||||||
|
@sendAction('onToggle')
|
||||||
|
hook = @get('hook')
|
||||||
|
hook.toggle().then( (->), =>
|
||||||
|
@toggleProperty('hook.active')
|
||||||
|
@sendAction('onToggleError', hook)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
`export default HookSwitchComponent`
|
|
@ -1,17 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
tagName: 'a',
|
|
||||||
classNames: ['switch--icon'],
|
|
||||||
classNameBindings: ['active'],
|
|
||||||
activeBinding: "hook.active",
|
|
||||||
click() {
|
|
||||||
var hook;
|
|
||||||
this.sendAction('onToggle');
|
|
||||||
hook = this.get('hook');
|
|
||||||
return hook.toggle().then((function() {}), () => {
|
|
||||||
this.toggleProperty('hook.active');
|
|
||||||
return this.sendAction('onToggleError', hook);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
21
app/components/hooks-list-item.coffee
Normal file
21
app/components/hooks-list-item.coffee
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
`import config from 'travis/config/environment'`
|
||||||
|
|
||||||
|
HooksListItemComponent = Ember.Component.extend
|
||||||
|
tagName: 'li'
|
||||||
|
classNames: ['row']
|
||||||
|
classNameBindings: ['hook.active:active']
|
||||||
|
|
||||||
|
githubOrgsOauthAccessSettingsUrl: config.githubOrgsOauthAccessSettingsUrl
|
||||||
|
|
||||||
|
actions:
|
||||||
|
handleToggleError: ->
|
||||||
|
@set("showError", true)
|
||||||
|
|
||||||
|
close: ->
|
||||||
|
@send('resetErrors')
|
||||||
|
|
||||||
|
resetErrors: ->
|
||||||
|
@set("showError", false)
|
||||||
|
|
||||||
|
`export default HooksListItemComponent`
|
|
@ -1,23 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import config from 'travis/config/environment';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
tagName: 'li',
|
|
||||||
classNames: ['row'],
|
|
||||||
classNameBindings: ['hook.active:active'],
|
|
||||||
githubOrgsOauthAccessSettingsUrl: config.githubOrgsOauthAccessSettingsUrl,
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
handleToggleError() {
|
|
||||||
return this.set("showError", true);
|
|
||||||
},
|
|
||||||
|
|
||||||
close() {
|
|
||||||
return this.send('resetErrors');
|
|
||||||
},
|
|
||||||
|
|
||||||
resetErrors() {
|
|
||||||
return this.set("showError", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
30
app/components/job-log.coffee
Normal file
30
app/components/job-log.coffee
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
JobLogComponent = Ember.Component.extend
|
||||||
|
logBinding: 'job.log'
|
||||||
|
|
||||||
|
didInsertElement: ->
|
||||||
|
@setupLog()
|
||||||
|
|
||||||
|
logDidChange: (->
|
||||||
|
@setupLog()
|
||||||
|
).observes('log')
|
||||||
|
|
||||||
|
logWillChange: (->
|
||||||
|
@teardownLog()
|
||||||
|
).observesBefore('log')
|
||||||
|
|
||||||
|
willDestroyElement: ->
|
||||||
|
@teardownLog()
|
||||||
|
|
||||||
|
teardownLog: ->
|
||||||
|
job = @get('job')
|
||||||
|
job.unsubscribe() if job
|
||||||
|
|
||||||
|
setupLog: ->
|
||||||
|
job = @get('job')
|
||||||
|
if job
|
||||||
|
job.get('log').fetch()
|
||||||
|
job.subscribe()
|
||||||
|
|
||||||
|
`export default JobLogComponent`
|
|
@ -1,35 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
logBinding: 'job.log',
|
|
||||||
classNames: ['job-log'],
|
|
||||||
|
|
||||||
didReceiveAttrs: function(options) {
|
|
||||||
this._super(...arguments);
|
|
||||||
|
|
||||||
let oldJob = options.oldAttrs && options.oldAttrs.job && options.oldAttrs.job.value,
|
|
||||||
newJob = options.newAttrs && options.newAttrs.job && options.newAttrs.job.value;
|
|
||||||
|
|
||||||
if(newJob !== oldJob) {
|
|
||||||
if(newJob) {
|
|
||||||
this.setupLog(newJob);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(oldJob) {
|
|
||||||
this.teardownLog(oldJob);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
teardownLog(job) {
|
|
||||||
job.unsubscribe();
|
|
||||||
},
|
|
||||||
|
|
||||||
setupLog(job) {
|
|
||||||
this.set('error', false);
|
|
||||||
job.get('log').fetch().then(function() { }, () => {
|
|
||||||
this.set('error', true);
|
|
||||||
});
|
|
||||||
job.subscribe();
|
|
||||||
}
|
|
||||||
});
|
|
9
app/components/job-repo-actions.coffee
Normal file
9
app/components/job-repo-actions.coffee
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
`import RepoActionsItemComponentMixin from 'travis/utils/repo-actions-item-component-mixin'`
|
||||||
|
|
||||||
|
JobRepoActionsComponent = Ember.Component.extend(RepoActionsItemComponentMixin,
|
||||||
|
item: Ember.computed.alias('job')
|
||||||
|
type: 'job'
|
||||||
|
)
|
||||||
|
|
||||||
|
`export default JobRepoActionsComponent`
|
|
@ -1,7 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import RepoActionsItemComponentMixin from 'travis/utils/repo-actions-item-component-mixin';
|
|
||||||
|
|
||||||
export default Ember.Component.extend(RepoActionsItemComponentMixin, {
|
|
||||||
item: Ember.computed.alias('job'),
|
|
||||||
type: 'job'
|
|
||||||
});
|
|
|
@ -1,18 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import { colorForState } from 'travis/utils/helpers';
|
|
||||||
import { githubCommit } from 'travis/utils/urls';
|
|
||||||
import Polling from 'travis/mixins/polling';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
pollModels: 'job.build',
|
|
||||||
commitBinding: 'job.commit',
|
|
||||||
currentItemBinding: 'job',
|
|
||||||
|
|
||||||
color: function() {
|
|
||||||
return colorForState(this.get('job.state'));
|
|
||||||
}.property('job.state'),
|
|
||||||
|
|
||||||
urlGithubCommit: function() {
|
|
||||||
return githubCommit(this.get('repo.slug'), this.get('commit.sha'));
|
|
||||||
}.property('repo.slug', 'commit.sha')
|
|
||||||
});
|
|
41
app/components/jobs-item.coffee
Normal file
41
app/components/jobs-item.coffee
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
`import { colorForState } from 'travis/utils/helpers'`
|
||||||
|
`import { languageConfigKeys } from 'travis/utils/keys-map';`
|
||||||
|
|
||||||
|
JobsItemComponent = Ember.Component.extend
|
||||||
|
tagName: 'li'
|
||||||
|
classNameBindings: ['job.state']
|
||||||
|
classNames: ['tile', 'tile--jobs', 'row']
|
||||||
|
|
||||||
|
isAnimating: (->
|
||||||
|
state = @get('job.state')
|
||||||
|
animationStates = ['received', 'queued', 'started', 'booting']
|
||||||
|
|
||||||
|
unless animationStates.indexOf(state) == -1
|
||||||
|
true
|
||||||
|
|
||||||
|
).property('job.state')
|
||||||
|
|
||||||
|
languages: (->
|
||||||
|
output = []
|
||||||
|
|
||||||
|
if config = @get('job.config')
|
||||||
|
for key, languageName of languageConfigKeys
|
||||||
|
if version = config[key]
|
||||||
|
output.push(languageName + ': ' + version)
|
||||||
|
|
||||||
|
gemfile = @get('job.config.gemfile')
|
||||||
|
if gemfile && @get('job.config.env')
|
||||||
|
output.push "Gemfile: #{gemfile}"
|
||||||
|
|
||||||
|
output.join(' ')
|
||||||
|
).property('job.config')
|
||||||
|
|
||||||
|
environment: (->
|
||||||
|
if env = @get('job.config.env')
|
||||||
|
env
|
||||||
|
else if gemfile = @get('job.config.gemfile')
|
||||||
|
"Gemfile: #{gemfile}"
|
||||||
|
).property('job.config.env', 'job.config.gemfile')
|
||||||
|
|
||||||
|
`export default JobsItemComponent`
|
|
@ -1,36 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import { colorForState } from 'travis/utils/helpers';
|
|
||||||
import { languageConfigKeys } from 'travis/utils/keys-map';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
tagName: 'li',
|
|
||||||
classNameBindings: ['job.state'],
|
|
||||||
classNames: ['jobs-item'],
|
|
||||||
|
|
||||||
languages: function() {
|
|
||||||
var config, gemfile, key, languageName, output, version;
|
|
||||||
output = [];
|
|
||||||
if (config = this.get('job.config')) {
|
|
||||||
for (key in languageConfigKeys) {
|
|
||||||
languageName = languageConfigKeys[key];
|
|
||||||
if (version = config[key]) {
|
|
||||||
output.push(languageName + ': ' + version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gemfile = this.get('job.config.gemfile');
|
|
||||||
if (gemfile && this.get('job.config.env')) {
|
|
||||||
output.push("Gemfile: " + gemfile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output.join(' ');
|
|
||||||
}.property('job.config'),
|
|
||||||
|
|
||||||
environment: function() {
|
|
||||||
var env, gemfile;
|
|
||||||
if (env = this.get('job.config.env')) {
|
|
||||||
return env;
|
|
||||||
} else if (gemfile = this.get('job.config.gemfile')) {
|
|
||||||
return "Gemfile: " + gemfile;
|
|
||||||
}
|
|
||||||
}.property('job.config.env', 'job.config.gemfile')
|
|
||||||
});
|
|
11
app/components/jobs-list.coffee
Normal file
11
app/components/jobs-list.coffee
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
JobsListComponent = Ember.Component.extend
|
||||||
|
jobTableId: Ember.computed(->
|
||||||
|
if @get('required')
|
||||||
|
'jobs'
|
||||||
|
else
|
||||||
|
'allowed_failure_jobs'
|
||||||
|
)
|
||||||
|
|
||||||
|
`export default JobsListComponent`
|
|
@ -1,13 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
tagName: 'section',
|
|
||||||
classNames: ['jobs'],
|
|
||||||
jobTableId: Ember.computed(function() {
|
|
||||||
if (this.get('required')) {
|
|
||||||
return 'jobs';
|
|
||||||
} else {
|
|
||||||
return 'allowed_failure_jobs';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
12
app/components/landing-row.coffee
Normal file
12
app/components/landing-row.coffee
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
`import { githubCommit as githubCommitUrl } from 'travis/utils/urls'`
|
||||||
|
`import TravisRoute from 'travis/routes/basic'`
|
||||||
|
`import config from 'travis/config/environment'`
|
||||||
|
|
||||||
|
LandingRowComponent = Ember.Component.extend
|
||||||
|
|
||||||
|
tagName: 'li'
|
||||||
|
classNameBindings: ['repo.lastBuildState']
|
||||||
|
classNames: ['landing-row', 'row-li']
|
||||||
|
|
||||||
|
`export default LandingRowComponent`
|
|
@ -1,8 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
|
|
||||||
tagName: 'li',
|
|
||||||
classNameBindings: ['build.state']
|
|
||||||
|
|
||||||
});
|
|
40
app/components/limit-concurrent-builds.coffee
Normal file
40
app/components/limit-concurrent-builds.coffee
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
LimitConcurrentBuildsComponent = Ember.Component.extend
|
||||||
|
|
||||||
|
classNames: ['limit-concurrent-builds']
|
||||||
|
|
||||||
|
description: (->
|
||||||
|
description = "Limit concurrent jobs"
|
||||||
|
if @get('enabled')
|
||||||
|
description += " "
|
||||||
|
description
|
||||||
|
).property('enabled')
|
||||||
|
|
||||||
|
limitChanged: ->
|
||||||
|
repo = @get('repo')
|
||||||
|
limit = parseInt(@get('value'))
|
||||||
|
if limit
|
||||||
|
@set('isSaving', true)
|
||||||
|
savingFinished = =>
|
||||||
|
@set('isSaving', false)
|
||||||
|
|
||||||
|
repo.saveSettings(maximum_number_of_builds: limit).
|
||||||
|
then(savingFinished, savingFinished)
|
||||||
|
actions:
|
||||||
|
toggle: ->
|
||||||
|
unless @get('enabled')
|
||||||
|
return if @get('value') == 0
|
||||||
|
return if @get('isSaving')
|
||||||
|
@set('isSaving', true)
|
||||||
|
|
||||||
|
savingFinished = =>
|
||||||
|
@set('isSaving', false)
|
||||||
|
|
||||||
|
@get('repo').saveSettings(maximum_number_of_builds: 0).then(savingFinished, savingFinished)
|
||||||
|
@set('value', 0)
|
||||||
|
|
||||||
|
limitChanged: ->
|
||||||
|
Ember.run.debounce(this, 'limitChanged', 1000)
|
||||||
|
|
||||||
|
`export default LimitConcurrentBuildsComponent`
|
|
@ -1,55 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
classNames: ['limit-concurrent-builds'],
|
|
||||||
|
|
||||||
description: function() {
|
|
||||||
var description;
|
|
||||||
description = "Limit concurrent jobs";
|
|
||||||
if (this.get('enabled')) {
|
|
||||||
description += " ";
|
|
||||||
}
|
|
||||||
return description;
|
|
||||||
}.property('enabled'),
|
|
||||||
|
|
||||||
limitChanged() {
|
|
||||||
var limit, repo, savingFinished;
|
|
||||||
repo = this.get('repo');
|
|
||||||
limit = parseInt(this.get('value'));
|
|
||||||
if (limit) {
|
|
||||||
this.set('isSaving', true);
|
|
||||||
savingFinished = () => {
|
|
||||||
return this.set('isSaving', false);
|
|
||||||
};
|
|
||||||
return repo.saveSettings({
|
|
||||||
maximum_number_of_builds: limit
|
|
||||||
}).then(savingFinished, savingFinished);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
toggle() {
|
|
||||||
var savingFinished;
|
|
||||||
if (!this.get('enabled')) {
|
|
||||||
if (this.get('value') === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.get('isSaving')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.set('isSaving', true);
|
|
||||||
savingFinished = () => {
|
|
||||||
return this.set('isSaving', false);
|
|
||||||
};
|
|
||||||
this.get('repo').saveSettings({
|
|
||||||
maximum_number_of_builds: 0
|
|
||||||
}).then(savingFinished, savingFinished);
|
|
||||||
return this.set('value', 0);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
limitChanged() {
|
|
||||||
return Ember.run.debounce(this, 'limitChanged', 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
8
app/components/loading-indicator.coffee
Normal file
8
app/components/loading-indicator.coffee
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
LoadingIndicatorComponent = Ember.Component.extend
|
||||||
|
tagName: 'div'
|
||||||
|
classNameBindings: ['center:loading-container', 'inline:inline-block']
|
||||||
|
center: false
|
||||||
|
|
||||||
|
`export default LoadingIndicatorComponent`
|
|
@ -1,7 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
tagName: 'div',
|
|
||||||
classNameBindings: ['center:loading-container', 'inline:inline-block', 'height:icon-height'],
|
|
||||||
center: false
|
|
||||||
});
|
|
157
app/components/log-content.coffee
Normal file
157
app/components/log-content.coffee
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
`import LinesSelector from 'travis/utils/lines-selector'`
|
||||||
|
`import LogFolder from 'travis/utils/log-folder'`
|
||||||
|
`import config from 'travis/config/environment'`
|
||||||
|
`import { plainTextLog as plainTextLogUrl } from 'travis/utils/urls'`
|
||||||
|
|
||||||
|
Log.DEBUG = false
|
||||||
|
Log.LIMIT = 10000
|
||||||
|
|
||||||
|
Log.Scroll = (options) ->
|
||||||
|
options ||= {}
|
||||||
|
@beforeScroll = options.beforeScroll
|
||||||
|
this
|
||||||
|
Log.Scroll.prototype = $.extend new Log.Listener,
|
||||||
|
insert: (log, data, pos) ->
|
||||||
|
@tryScroll() if @numbers
|
||||||
|
true
|
||||||
|
|
||||||
|
tryScroll: ->
|
||||||
|
if element = $("#log p:visible.highlight:first")
|
||||||
|
if @beforeScroll
|
||||||
|
@beforeScroll()
|
||||||
|
$('#main').scrollTop(0)
|
||||||
|
$('html, body').scrollTop(element.offset()?.top - (window.innerHeight / 3)) # weird, html works in chrome, body in firefox
|
||||||
|
|
||||||
|
Log.Limit = (max_lines, limitedLogCallback) ->
|
||||||
|
@max_lines = max_lines || 1000
|
||||||
|
@limitedLogCallback = limitedLogCallback || (->)
|
||||||
|
this
|
||||||
|
|
||||||
|
Log.Limit.prototype = Log.extend new Log.Listener,
|
||||||
|
count: 0,
|
||||||
|
insert: (log, node, pos) ->
|
||||||
|
if node.type == 'paragraph' && !node.hidden
|
||||||
|
@count += 1
|
||||||
|
if @limited
|
||||||
|
@limitedLogCallback()
|
||||||
|
return @count
|
||||||
|
|
||||||
|
Object.defineProperty Log.Limit.prototype, 'limited',
|
||||||
|
get: ->
|
||||||
|
@count >= @max_lines
|
||||||
|
|
||||||
|
LogContentComponent = Ember.Component.extend
|
||||||
|
popup: Ember.inject.service()
|
||||||
|
|
||||||
|
currentUserBinding: 'auth.currentUser'
|
||||||
|
|
||||||
|
didInsertElement: ->
|
||||||
|
console.log 'log view: did insert' if Log.DEBUG
|
||||||
|
@_super.apply this, arguments
|
||||||
|
@createEngine()
|
||||||
|
|
||||||
|
willDestroyElement: ->
|
||||||
|
console.log 'log view: will destroy' if Log.DEBUG
|
||||||
|
@teardownLog()
|
||||||
|
|
||||||
|
teardownLog: (log) ->
|
||||||
|
if log || log = @get('log')
|
||||||
|
parts = log.get('parts')
|
||||||
|
parts.removeArrayObserver(@, didChange: 'partsDidChange', willChange: 'noop')
|
||||||
|
parts.destroy()
|
||||||
|
log.notifyPropertyChange('parts')
|
||||||
|
@lineSelector?.willDestroy()
|
||||||
|
if logElement = this.$('#log')
|
||||||
|
logElement.empty()
|
||||||
|
|
||||||
|
createEngine: (log) ->
|
||||||
|
if log || log = @get('log')
|
||||||
|
if logElement = this.$('#log')
|
||||||
|
logElement.empty()
|
||||||
|
|
||||||
|
log.onClear =>
|
||||||
|
@teardownLog()
|
||||||
|
@createEngine()
|
||||||
|
|
||||||
|
@scroll = new Log.Scroll beforeScroll: =>
|
||||||
|
@unfoldHighlight()
|
||||||
|
@limit = new Log.Limit Log.LIMIT, =>
|
||||||
|
@set('limited', true)
|
||||||
|
@engine = Log.create(listeners: [@scroll, @limit])
|
||||||
|
@engine.limit = @limit
|
||||||
|
@logFolder = new LogFolder(@$('#log'))
|
||||||
|
@lineSelector = new LinesSelector(@$('#log'), @scroll, @logFolder)
|
||||||
|
@observeParts(log)
|
||||||
|
|
||||||
|
didUpdateAttrs: (changes) ->
|
||||||
|
@_super.apply(this, arguments)
|
||||||
|
|
||||||
|
return unless changes.oldAttrs
|
||||||
|
|
||||||
|
if changes.newAttrs.job.value && changes.oldAttrs.job.value &&
|
||||||
|
changes.newAttrs.job.value != changes.oldAttrs.job.value
|
||||||
|
|
||||||
|
@teardownLog(changes.oldAttrs.job.value.get('log'))
|
||||||
|
@createEngine(changes.newAttrs.job.value.get('log'))
|
||||||
|
|
||||||
|
unfoldHighlight: ->
|
||||||
|
@lineSelector.unfoldLines()
|
||||||
|
|
||||||
|
observeParts: (log) ->
|
||||||
|
if log || log = @get('log')
|
||||||
|
parts = log.get('parts')
|
||||||
|
parts.addArrayObserver(@, didChange: 'partsDidChange', willChange: 'noop')
|
||||||
|
parts = parts.slice(0)
|
||||||
|
@partsDidChange(parts, 0, null, parts.length)
|
||||||
|
|
||||||
|
partsDidChange: (parts, start, _, added) ->
|
||||||
|
console.log 'log view: parts did change' if Log.DEBUG
|
||||||
|
return unless @get('state') == 'inDOM'
|
||||||
|
|
||||||
|
for part, i in parts.slice(start, start + added)
|
||||||
|
# console.log "limit in log view: #{@get('limited')}"
|
||||||
|
break if @engine?.limit?.limited
|
||||||
|
@engine.set(part.number, part.content)
|
||||||
|
|
||||||
|
plainTextLogUrl: (->
|
||||||
|
if id = @get('log.job.id')
|
||||||
|
url = plainTextLogUrl(id)
|
||||||
|
if config.pro
|
||||||
|
url += "&access_token=#{@get('job.log.token')}"
|
||||||
|
url
|
||||||
|
).property('job.log.id', 'job.log.token')
|
||||||
|
|
||||||
|
hasPermission: (->
|
||||||
|
if permissions = @get('currentUser.permissions')
|
||||||
|
permissions.contains parseInt(@get('job.repo.id'))
|
||||||
|
).property('currentUser.permissions.length', 'job.repo.id')
|
||||||
|
|
||||||
|
canRemoveLog: (->
|
||||||
|
if job = @get('job')
|
||||||
|
job.get('canRemoveLog') && @get('hasPermission')
|
||||||
|
).property('job.canRemoveLog', 'hasPermission')
|
||||||
|
|
||||||
|
showToTop: (->
|
||||||
|
@get('log.hasContent') && @get('job.canRemoveLog')
|
||||||
|
).property('log.hasContent', 'job.canRemoveLog')
|
||||||
|
showTailing: Ember.computed.alias('showToTop')
|
||||||
|
|
||||||
|
actions:
|
||||||
|
toTop: () ->
|
||||||
|
Travis.tailing.stop()
|
||||||
|
$(window).scrollTop(0)
|
||||||
|
|
||||||
|
toggleTailing: ->
|
||||||
|
Travis.tailing.toggle()
|
||||||
|
@engine.autoCloseFold = !Travis.tailing.isActive()
|
||||||
|
event.preventDefault()
|
||||||
|
|
||||||
|
removeLogPopup: ->
|
||||||
|
if @get('canRemoveLog')
|
||||||
|
@get('popup').open('remove-log-popup')
|
||||||
|
return false
|
||||||
|
|
||||||
|
noop: -> # TODO required?
|
||||||
|
|
||||||
|
`export default LogContentComponent`
|
|
@ -1,238 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import LinesSelector from 'travis/utils/lines-selector';
|
|
||||||
import LogFolder from 'travis/utils/log-folder';
|
|
||||||
import config from 'travis/config/environment';
|
|
||||||
import { plainTextLog as plainTextLogUrl } from 'travis/utils/urls';
|
|
||||||
|
|
||||||
Log.DEBUG = false;
|
|
||||||
|
|
||||||
Log.LIMIT = 10000;
|
|
||||||
|
|
||||||
Log.Scroll = function(options) {
|
|
||||||
options = options || {};
|
|
||||||
this.beforeScroll = options.beforeScroll;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Log.Scroll.prototype = $.extend(new Log.Listener(), {
|
|
||||||
insert: function(log, data, pos) {
|
|
||||||
if (this.numbers) {
|
|
||||||
this.tryScroll();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
tryScroll: function() {
|
|
||||||
var element, ref;
|
|
||||||
if (element = $("#log p:visible.highlight:first")) {
|
|
||||||
if (this.beforeScroll) {
|
|
||||||
this.beforeScroll();
|
|
||||||
}
|
|
||||||
$('#main').scrollTop(0);
|
|
||||||
return $('html, body').scrollTop(((ref = element.offset()) != null ? ref.top : void 0) - (window.innerHeight / 3));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Log.Limit = function(max_lines, limitedLogCallback) {
|
|
||||||
this.max_lines = max_lines || 1000;
|
|
||||||
this.limitedLogCallback = limitedLogCallback || (function() {});
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Log.Limit.prototype = Log.extend(new Log.Listener(), {
|
|
||||||
count: 0,
|
|
||||||
insert: function(log, node, pos) {
|
|
||||||
if (node.type === 'paragraph' && !node.hidden) {
|
|
||||||
this.count += 1;
|
|
||||||
if (this.limited) {
|
|
||||||
this.limitedLogCallback();
|
|
||||||
}
|
|
||||||
return this.count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.defineProperty(Log.Limit.prototype, 'limited', {
|
|
||||||
get: function() {
|
|
||||||
return this.count >= this.max_lines;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
popup: Ember.inject.service(),
|
|
||||||
classNameBindings: ['logIsVisible:is-open'],
|
|
||||||
logIsVisible: false,
|
|
||||||
currentUserBinding: 'auth.currentUser',
|
|
||||||
|
|
||||||
didInsertElement() {
|
|
||||||
if (Log.DEBUG) {
|
|
||||||
console.log('log view: did insert');
|
|
||||||
}
|
|
||||||
this._super.apply(this, arguments);
|
|
||||||
Ember.run.scheduleOnce('afterRender', this, 'createEngine');
|
|
||||||
},
|
|
||||||
|
|
||||||
willDestroyElement() {
|
|
||||||
if (Log.DEBUG) {
|
|
||||||
console.log('log view: will destroy');
|
|
||||||
}
|
|
||||||
Ember.run.scheduleOnce('afterRender', this, 'teardownLog');
|
|
||||||
},
|
|
||||||
|
|
||||||
teardownLog(log) {
|
|
||||||
var parts, ref;
|
|
||||||
if (log || (log = this.get('log'))) {
|
|
||||||
parts = log.get('parts');
|
|
||||||
parts.removeArrayObserver(this, {
|
|
||||||
didChange: 'partsDidChange',
|
|
||||||
willChange: 'noop'
|
|
||||||
});
|
|
||||||
parts.destroy();
|
|
||||||
log.notifyPropertyChange('parts');
|
|
||||||
if ((ref = this.lineSelector) != null) {
|
|
||||||
ref.willDestroy();
|
|
||||||
}
|
|
||||||
this.clearLogElement();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
clearLogElement() {
|
|
||||||
var logElement = this.$('#log');
|
|
||||||
if (logElement && logElement[0]) {
|
|
||||||
logElement[0].innerHTML = '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
createEngine(log) {
|
|
||||||
if (log || (log = this.get('log'))) {
|
|
||||||
this.clearLogElement();
|
|
||||||
log.onClear(() => {
|
|
||||||
this.teardownLog();
|
|
||||||
return this.createEngine();
|
|
||||||
});
|
|
||||||
this.scroll = new Log.Scroll({
|
|
||||||
beforeScroll: () => {
|
|
||||||
return this.unfoldHighlight();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.limit = new Log.Limit(Log.LIMIT, () => {
|
|
||||||
return this.set('limited', true);
|
|
||||||
});
|
|
||||||
this.engine = Log.create({
|
|
||||||
listeners: [this.scroll, this.limit]
|
|
||||||
});
|
|
||||||
this.engine.limit = this.limit;
|
|
||||||
this.logFolder = new LogFolder(this.$('#log'));
|
|
||||||
this.lineSelector = new LinesSelector(this.$('#log'), this.scroll, this.logFolder);
|
|
||||||
this.observeParts(log);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
didUpdateAttrs(changes) {
|
|
||||||
this._super.apply(this, arguments);
|
|
||||||
if (!changes.oldAttrs) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (changes.newAttrs.job.value && changes.oldAttrs.job.value && changes.newAttrs.job.value !== changes.oldAttrs.job.value) {
|
|
||||||
this.teardownLog(changes.oldAttrs.job.value.get('log'));
|
|
||||||
return this.createEngine(changes.newAttrs.job.value.get('log'));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
unfoldHighlight() {
|
|
||||||
return this.lineSelector.unfoldLines();
|
|
||||||
},
|
|
||||||
|
|
||||||
observeParts(log) {
|
|
||||||
var parts;
|
|
||||||
if (log || (log = this.get('log'))) {
|
|
||||||
parts = log.get('parts');
|
|
||||||
parts.addArrayObserver(this, {
|
|
||||||
didChange: 'partsDidChange',
|
|
||||||
willChange: 'noop'
|
|
||||||
});
|
|
||||||
parts = parts.slice(0);
|
|
||||||
this.partsDidChange(parts, 0, null, parts.length);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
partsDidChange(parts, start, _, added) {
|
|
||||||
Ember.run.schedule('afterRender', this, function() {
|
|
||||||
var i, j, len, part, ref, ref1, ref2, results;
|
|
||||||
if (Log.DEBUG) {
|
|
||||||
console.log('log view: parts did change');
|
|
||||||
}
|
|
||||||
if (this.get('_state') !== 'inDOM') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ref = parts.slice(start, start + added);
|
|
||||||
results = [];
|
|
||||||
for (i = j = 0, len = ref.length; j < len; i = ++j) {
|
|
||||||
part = ref[i];
|
|
||||||
if ((ref1 = this.engine) != null ? (ref2 = ref1.limit) != null ? ref2.limited : void 0 : void 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
results.push(this.engine.set(part.number, part.content));
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
plainTextLogUrl: function() {
|
|
||||||
var id, url;
|
|
||||||
if (id = this.get('log.job.id')) {
|
|
||||||
url = plainTextLogUrl(id);
|
|
||||||
if (config.pro) {
|
|
||||||
url += "&access_token=" + (this.get('job.log.token'));
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
}.property('job.log.id', 'job.log.token'),
|
|
||||||
|
|
||||||
hasPermission: function() {
|
|
||||||
var permissions;
|
|
||||||
if (permissions = this.get('currentUser.permissions')) {
|
|
||||||
return permissions.contains(parseInt(this.get('job.repo.id')));
|
|
||||||
}
|
|
||||||
}.property('currentUser.permissions.length', 'job.repo.id'),
|
|
||||||
|
|
||||||
canRemoveLog: function() {
|
|
||||||
var job;
|
|
||||||
if (job = this.get('job')) {
|
|
||||||
return job.get('canRemoveLog') && this.get('hasPermission');
|
|
||||||
}
|
|
||||||
}.property('job.canRemoveLog', 'hasPermission'),
|
|
||||||
|
|
||||||
showToTop: function() {
|
|
||||||
return this.get('log.hasContent') && this.get('job.canRemoveLog');
|
|
||||||
}.property('log.hasContent', 'job.canRemoveLog'),
|
|
||||||
|
|
||||||
showTailing: Ember.computed.alias('showToTop'),
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
toTop() {
|
|
||||||
Travis.tailing.stop();
|
|
||||||
return $(window).scrollTop(0);
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleTailing() {
|
|
||||||
Travis.tailing.toggle();
|
|
||||||
this.engine.autoCloseFold = !Travis.tailing.isActive();
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
removeLogPopup() {
|
|
||||||
if (this.get('canRemoveLog')) {
|
|
||||||
this.get('popup').open('remove-log-popup');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleLog() {
|
|
||||||
this.toggleProperty('logIsVisible');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// don't remove this, it's needed as an empty willChange callback
|
|
||||||
noop: function() {}
|
|
||||||
});
|
|
19
app/components/no-builds.coffee
Normal file
19
app/components/no-builds.coffee
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
`import config from 'travis/config/environment'`
|
||||||
|
|
||||||
|
NoBuildsComponent = Ember.Component.extend
|
||||||
|
actions:
|
||||||
|
triggerBuild: () ->
|
||||||
|
@set('isLoading', true)
|
||||||
|
apiEndpoint = config.apiEndpoint
|
||||||
|
$.ajax(apiEndpoint + "/v3/repo/#{@get('repo.repo.id')}/requests", {
|
||||||
|
headers: {
|
||||||
|
Authorization: 'token ' + @get('repo.auth')
|
||||||
|
},
|
||||||
|
type: "POST"
|
||||||
|
}).then( =>
|
||||||
|
@set('isLoading', false)
|
||||||
|
# @transitionToRoute('repo')
|
||||||
|
);
|
||||||
|
|
||||||
|
`export default NoBuildsComponent`
|
|
@ -1,20 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import config from 'travis/config/environment';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
actions: {
|
|
||||||
triggerBuild() {
|
|
||||||
var apiEndpoint;
|
|
||||||
this.set('isLoading', true);
|
|
||||||
apiEndpoint = config.apiEndpoint;
|
|
||||||
return $.ajax(apiEndpoint + ("/v3/repo/" + (this.get('repo.repo.id')) + "/requests"), {
|
|
||||||
headers: {
|
|
||||||
Authorization: 'token ' + this.get('repo.auth')
|
|
||||||
},
|
|
||||||
type: "POST"
|
|
||||||
}).then(() => {
|
|
||||||
return this.set('isLoading', false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
5
app/components/no-repos.coffee
Normal file
5
app/components/no-repos.coffee
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
NoReposComponent = Ember.Component.extend()
|
||||||
|
|
||||||
|
`export default NoReposComponent`
|
|
@ -1,3 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend();
|
|
5
app/components/not-active.coffee
Normal file
5
app/components/not-active.coffee
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
NotActiveComponent = Ember.Component.extend()
|
||||||
|
|
||||||
|
`export default NotActiveComponent`
|
|
@ -1,3 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend();
|
|
|
@ -1,28 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
classNames: ['media', 'account'],
|
|
||||||
tagName: 'li',
|
|
||||||
classNameBindings: ['type', 'selected'],
|
|
||||||
typeBinding: 'account.type',
|
|
||||||
selectedBinding: 'account.selected',
|
|
||||||
tokenIsVisible: false,
|
|
||||||
|
|
||||||
name: function() {
|
|
||||||
return this.get('account.name') || this.get('account.login');
|
|
||||||
}.property('account'),
|
|
||||||
|
|
||||||
avatarUrl: function() {
|
|
||||||
return this.get('account.avatarUrl') || false;
|
|
||||||
}.property('account'),
|
|
||||||
|
|
||||||
isUser: function() {
|
|
||||||
return this.get('account.type') === 'user';
|
|
||||||
}.property('account'),
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
tokenVisibility() {
|
|
||||||
return this.toggleProperty('tokenIsVisible');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
13
app/components/orgs-filter.coffee
Normal file
13
app/components/orgs-filter.coffee
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
Component = Ember.Component.extend
|
||||||
|
|
||||||
|
actions:
|
||||||
|
toggleOrgFilter: () ->
|
||||||
|
@toggleProperty('showFilter')
|
||||||
|
false
|
||||||
|
select: (org) ->
|
||||||
|
@toggleProperty('showFilter')
|
||||||
|
@sendAction('action', org)
|
||||||
|
|
||||||
|
`export default Component`
|
|
@ -1,17 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
|
|
||||||
classNames: ['organisation-filter'],
|
|
||||||
actions: {
|
|
||||||
toggleOrgFilter() {
|
|
||||||
this.toggleProperty('showFilter');
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
select(org) {
|
|
||||||
this.toggleProperty('showFilter');
|
|
||||||
return this.sendAction('action', org);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
29
app/components/owner-repo-tile.coffee
Normal file
29
app/components/owner-repo-tile.coffee
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
OwnerRepoTileComponent = Ember.Component.extend
|
||||||
|
|
||||||
|
tagName: 'li'
|
||||||
|
classNames: ['owner-tile', 'row-li']
|
||||||
|
classNameBindings: ['repo.default_branch.last_build.state']
|
||||||
|
|
||||||
|
ownerName: (->
|
||||||
|
@get('repo.slug').split(/\//)[0]
|
||||||
|
).property('repo.slug')
|
||||||
|
|
||||||
|
repoName: (->
|
||||||
|
@get('repo.slug').split(/\//)[1]
|
||||||
|
).property('repo.slug')
|
||||||
|
|
||||||
|
isAnimating: (->
|
||||||
|
state = @get('repo.default_branch.last_build.state')
|
||||||
|
animationStates = ['received', 'queued', 'started', 'booting']
|
||||||
|
|
||||||
|
unless animationStates.indexOf(state) == -1
|
||||||
|
true
|
||||||
|
|
||||||
|
).property('repo.default_branch.last_build.state')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`export default OwnerRepoTileComponent`
|
|
@ -1,24 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
tagName: 'li',
|
|
||||||
classNames: ['owner-tile', 'row-li'],
|
|
||||||
classNameBindings: ['repo.default_branch.last_build.state'],
|
|
||||||
|
|
||||||
ownerName: function() {
|
|
||||||
return this.get('repo.slug').split(/\//)[0];
|
|
||||||
}.property('repo.slug'),
|
|
||||||
|
|
||||||
repoName: function() {
|
|
||||||
return this.get('repo.slug').split(/\//)[1];
|
|
||||||
}.property('repo.slug'),
|
|
||||||
|
|
||||||
isAnimating: function() {
|
|
||||||
var animationStates, state;
|
|
||||||
state = this.get('repo.default_branch.last_build.state');
|
|
||||||
animationStates = ['received', 'queued', 'started', 'booting'];
|
|
||||||
if (animationStates.indexOf(state) !== -1) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}.property('repo.default_branch.last_build.state')
|
|
||||||
});
|
|
6
app/components/owner-sync-button.coffee
Normal file
6
app/components/owner-sync-button.coffee
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
`import SyncButton from 'travis/components/sync-button'`
|
||||||
|
|
||||||
|
Component = SyncButton.extend()
|
||||||
|
|
||||||
|
`export default Component`
|
|
@ -1,4 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import SyncButton from 'travis/components/sync-button';
|
|
||||||
|
|
||||||
export default SyncButton.extend();
|
|
|
@ -1,17 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
popup: Ember.inject.service(),
|
|
||||||
classNames: ['application'],
|
|
||||||
|
|
||||||
click(event) {
|
|
||||||
var targetAndParents = $(event.target).parents().andSelf();
|
|
||||||
|
|
||||||
if (!(targetAndParents.hasClass('open-popup') || targetAndParents.hasClass('popup'))) {
|
|
||||||
this.get('popup').close();
|
|
||||||
}
|
|
||||||
if (!targetAndParents.hasClass('menu') && !targetAndParents.is('#tools > a')) {
|
|
||||||
$('.menu').removeClass('display');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,6 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
classNames: ['profile-orglist', 'columns', 'medium-4'],
|
|
||||||
tagName: 'aside',
|
|
||||||
});
|
|
16
app/components/queued-jobs.coffee
Normal file
16
app/components/queued-jobs.coffee
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
`import config from 'travis/config/environment'`
|
||||||
|
|
||||||
|
QueuedJobsComponent = Ember.Component.extend
|
||||||
|
store: Ember.inject.service()
|
||||||
|
|
||||||
|
init: ->
|
||||||
|
@_super.apply this, arguments
|
||||||
|
if !Ember.testing
|
||||||
|
Visibility.every config.intervals.updateTimes, @updateTimes.bind(this)
|
||||||
|
|
||||||
|
updateTimes: ->
|
||||||
|
if jobs = @get('jobs')
|
||||||
|
jobs.forEach (job) -> job.updateTimes()
|
||||||
|
|
||||||
|
`export default QueuedJobsComponent`
|
|
@ -1,18 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import config from 'travis/config/environment';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
store: Ember.inject.service(),
|
|
||||||
updateTimesService: Ember.inject.service('updateTimes'),
|
|
||||||
|
|
||||||
init() {
|
|
||||||
this._super.apply(this, arguments);
|
|
||||||
if (!Ember.testing) {
|
|
||||||
return Visibility.every(config.intervals.updateTimes, this.updateTimes.bind(this));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateTimes() {
|
|
||||||
this.get('updateTimesService').push(this.get('jobs'));
|
|
||||||
}
|
|
||||||
});
|
|
23
app/components/remove-log-popup.coffee
Normal file
23
app/components/remove-log-popup.coffee
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
Component = Ember.Component.extend(
|
||||||
|
actions:
|
||||||
|
close: ->
|
||||||
|
$('.popup').removeClass('display')
|
||||||
|
return false
|
||||||
|
|
||||||
|
removeLog: ->
|
||||||
|
$('.popup').removeClass('display')
|
||||||
|
job = @get('job')
|
||||||
|
job.removeLog().then ->
|
||||||
|
Travis.flash(success: 'Log has been successfully removed.')
|
||||||
|
, (xhr) ->
|
||||||
|
if xhr.status == 409
|
||||||
|
Travis.flash(error: 'Log can\'t be removed')
|
||||||
|
else if xhr.status == 401
|
||||||
|
Travis.flash(error: 'You don\'t have sufficient access to remove the log')
|
||||||
|
else
|
||||||
|
Travis.flash(error: 'An error occured when removing the log')
|
||||||
|
)
|
||||||
|
|
||||||
|
`export default Component`
|
|
@ -1,27 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
actions: {
|
|
||||||
close() {
|
|
||||||
$('.popup').removeClass('display');
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
removeLog() {
|
|
||||||
var job = this.get('job');
|
|
||||||
$('.popup').removeClass('display');
|
|
||||||
|
|
||||||
return job.removeLog().then(function() {
|
|
||||||
return Travis.flash({ success: 'Log has been successfully removed.' });
|
|
||||||
}, function(xhr) {
|
|
||||||
if (xhr.status === 409) {
|
|
||||||
return Travis.flash({ error: 'Log can\'t be removed' });
|
|
||||||
} else if (xhr.status === 401) {
|
|
||||||
return Travis.flash({ error: 'You don\'t have sufficient access to remove the log' });
|
|
||||||
} else {
|
|
||||||
return Travis.flash({ error: 'An error occured when removing the log' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
15
app/components/repo-actions.coffee
Normal file
15
app/components/repo-actions.coffee
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
RepoActionsComponent = Ember.Component.extend(
|
||||||
|
displayCodeClimate: (->
|
||||||
|
@get('repo.githubLanguage') == 'Ruby'
|
||||||
|
).property('repo.githubLanguage')
|
||||||
|
|
||||||
|
actions:
|
||||||
|
codeClimatePopup: ->
|
||||||
|
$('.popup').removeClass('display')
|
||||||
|
$('#code-climate').addClass('display')
|
||||||
|
return false
|
||||||
|
)
|
||||||
|
|
||||||
|
`export default RepoActionsComponent`
|
|
@ -1,7 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
|
|
||||||
classNames: ['repo-main-tools']
|
|
||||||
|
|
||||||
});
|
|
54
app/components/repo-show-tabs.coffee
Normal file
54
app/components/repo-show-tabs.coffee
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
RepoShowTabsComponent = Ember.Component.extend
|
||||||
|
# hrm. how to parametrize bind-attr?
|
||||||
|
classCurrent: (->
|
||||||
|
'active' if @get('tab') == 'current'
|
||||||
|
).property('tab')
|
||||||
|
|
||||||
|
classBuilds: (->
|
||||||
|
'active' if @get('tab') == 'builds'
|
||||||
|
).property('tab')
|
||||||
|
|
||||||
|
classPullRequests: (->
|
||||||
|
'active' if @get('tab') == 'pull_requests'
|
||||||
|
).property('tab')
|
||||||
|
|
||||||
|
classBranches: (->
|
||||||
|
'active' if @get('tab') == 'branches'
|
||||||
|
).property('tab')
|
||||||
|
|
||||||
|
classEvents: (->
|
||||||
|
'active' if @get('tab') == 'events'
|
||||||
|
).property('tab')
|
||||||
|
|
||||||
|
classBuild: (->
|
||||||
|
tab = @get('tab')
|
||||||
|
classes = []
|
||||||
|
classes.push('active') if tab == 'build'
|
||||||
|
classes.push('display-inline') if tab == 'build' || tab == 'job'
|
||||||
|
classes.join(' ')
|
||||||
|
).property('tab')
|
||||||
|
|
||||||
|
# TODO: refactor tabs, most of the things here are not really DRY
|
||||||
|
classJob: (->
|
||||||
|
'active' if @get('tab') == 'job'
|
||||||
|
).property('tab')
|
||||||
|
|
||||||
|
classRequests: (->
|
||||||
|
'active' if @get('tab') == 'requests'
|
||||||
|
).property('tab')
|
||||||
|
|
||||||
|
classCaches: (->
|
||||||
|
'active' if @get('tab') == 'caches'
|
||||||
|
).property('tab')
|
||||||
|
|
||||||
|
classSettings: (->
|
||||||
|
'active' if @get('tab') == 'settings'
|
||||||
|
).property('tab')
|
||||||
|
|
||||||
|
classRequest: (->
|
||||||
|
'active' if @get('tab') == 'request'
|
||||||
|
).property('tab')
|
||||||
|
|
||||||
|
`export default RepoShowTabsComponent`
|
|
@ -1,74 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
tagName: 'nav',
|
|
||||||
classNames: ['tabnav'],
|
|
||||||
ariaRole: 'tablist',
|
|
||||||
|
|
||||||
classCurrent: function() {
|
|
||||||
if (this.get('tab') === 'current') {
|
|
||||||
return 'active';
|
|
||||||
}
|
|
||||||
}.property('tab'),
|
|
||||||
|
|
||||||
classBuilds: function() {
|
|
||||||
if (this.get('tab') === 'builds') {
|
|
||||||
return 'active';
|
|
||||||
}
|
|
||||||
}.property('tab'),
|
|
||||||
|
|
||||||
classPullRequests: function() {
|
|
||||||
if (this.get('tab') === 'pull_requests') {
|
|
||||||
return 'active';
|
|
||||||
}
|
|
||||||
}.property('tab'),
|
|
||||||
|
|
||||||
classBranches: function() {
|
|
||||||
if (this.get('tab') === 'branches') {
|
|
||||||
return 'active';
|
|
||||||
}
|
|
||||||
}.property('tab'),
|
|
||||||
|
|
||||||
classBuild: function() {
|
|
||||||
var classes, tab;
|
|
||||||
tab = this.get('tab');
|
|
||||||
classes = [];
|
|
||||||
if (tab === 'build') {
|
|
||||||
classes.push('active');
|
|
||||||
}
|
|
||||||
if (tab === 'build' || tab === 'job') {
|
|
||||||
classes.push('display-inline');
|
|
||||||
}
|
|
||||||
return classes.join(' ');
|
|
||||||
}.property('tab'),
|
|
||||||
|
|
||||||
classJob: function() {
|
|
||||||
if (this.get('tab') === 'job') {
|
|
||||||
return 'active';
|
|
||||||
}
|
|
||||||
}.property('tab'),
|
|
||||||
|
|
||||||
classRequests: function() {
|
|
||||||
if (this.get('tab') === 'requests') {
|
|
||||||
return 'active';
|
|
||||||
}
|
|
||||||
}.property('tab'),
|
|
||||||
|
|
||||||
classCaches: function() {
|
|
||||||
if (this.get('tab') === 'caches') {
|
|
||||||
return 'active';
|
|
||||||
}
|
|
||||||
}.property('tab'),
|
|
||||||
|
|
||||||
classSettings: function() {
|
|
||||||
if (this.get('tab') === 'settings') {
|
|
||||||
return 'active';
|
|
||||||
}
|
|
||||||
}.property('tab'),
|
|
||||||
|
|
||||||
classRequest: function() {
|
|
||||||
if (this.get('tab') === 'request') {
|
|
||||||
return 'active';
|
|
||||||
}
|
|
||||||
}.property('tab')
|
|
||||||
});
|
|
47
app/components/repo-show-tools.coffee
Normal file
47
app/components/repo-show-tools.coffee
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
`import config from 'travis/config/environment'`
|
||||||
|
|
||||||
|
RepoShowToolsComponent = Ember.Component.extend
|
||||||
|
popup: Ember.inject.service()
|
||||||
|
|
||||||
|
click: (event) ->
|
||||||
|
if $(event.target).is('a') && $(event.target).parents('.dropdown-menu').length
|
||||||
|
@closeMenu()
|
||||||
|
|
||||||
|
closeMenu: ->
|
||||||
|
$('.menu').removeClass('display')
|
||||||
|
|
||||||
|
actions:
|
||||||
|
menu: ->
|
||||||
|
@get('popup').close()
|
||||||
|
$('#tools .menu').toggleClass('display')
|
||||||
|
return false
|
||||||
|
|
||||||
|
hasPermission: (->
|
||||||
|
if permissions = @get('currentUser.permissions')
|
||||||
|
permissions.contains parseInt(@get('repo.id'))
|
||||||
|
).property('currentUser.permissions.length', 'repo.id')
|
||||||
|
|
||||||
|
hasPushPermission: (->
|
||||||
|
if permissions = @get('currentUser.pushPermissions')
|
||||||
|
permissions.contains parseInt(@get('repo.id'))
|
||||||
|
).property('currentUser.pushPermissions.length', 'repo.id')
|
||||||
|
|
||||||
|
hasAdminPermission: (->
|
||||||
|
if permissions = @get('currentUser.adminPermissions')
|
||||||
|
permissions.contains parseInt(@get('repo.id'))
|
||||||
|
).property('currentUser.adminPermissions.length', 'repo.id')
|
||||||
|
|
||||||
|
displaySettingsLink: (->
|
||||||
|
@get('hasPushPermission')
|
||||||
|
).property('hasPushPermission')
|
||||||
|
|
||||||
|
displayCachesLink: (->
|
||||||
|
@get('hasPushPermission') && config.endpoints.caches
|
||||||
|
).property('hasPushPermission')
|
||||||
|
|
||||||
|
displayStatusImages: (->
|
||||||
|
@get('hasPermission')
|
||||||
|
).property('hasPermission')
|
||||||
|
|
||||||
|
`export default RepoShowToolsComponent`
|
|
@ -1,38 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import config from 'travis/config/environment';
|
|
||||||
import { hasPermission, hasPushPermission } from 'travis/utils/permission';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
popup: Ember.inject.service(),
|
|
||||||
classNames: ['option-button'],
|
|
||||||
classNameBindings: ['isOpen:display'],
|
|
||||||
isOpen: false,
|
|
||||||
|
|
||||||
click(event) {
|
|
||||||
if ($(event.target).is('a') && $(event.target).parents('.settings-dropdown').length) {
|
|
||||||
return this.closeMenu();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
closeMenu() {
|
|
||||||
return this.toggleProperty('isOpen');
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
menu() {
|
|
||||||
return this.toggleProperty('isOpen');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
displaySettingsLink: function() {
|
|
||||||
return hasPushPermission(this.get('currentUser'), this.get('repo.id'));
|
|
||||||
}.property('currentUser.pushPermissions.length', 'repo'),
|
|
||||||
|
|
||||||
displayCachesLink: function() {
|
|
||||||
return hasPushPermission(this.get('currentUser'), this.get('repo.id')) && config.endpoints.caches;
|
|
||||||
}.property('currentUser.pushPermissions.length', 'repo'),
|
|
||||||
|
|
||||||
displayStatusImages: function() {
|
|
||||||
return hasPermission(this.get('currentUser'), this.get('repo.id'));
|
|
||||||
}.property('currentUser.permissions.length', 'repo.id')
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,7 +0,0 @@
|
||||||
import Polling from 'travis/mixins/polling';
|
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend(Polling, {
|
|
||||||
pollModels: 'repo',
|
|
||||||
classNameBindings: ['isLoading:loading']
|
|
||||||
});
|
|
5
app/components/repos-empty.coffee
Normal file
5
app/components/repos-empty.coffee
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
ReposEmptyComponent = Ember.Component.extend()
|
||||||
|
|
||||||
|
`export default ReposEmptyComponent`
|
|
@ -1,3 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend();
|
|
31
app/components/repos-list-item.coffee
Normal file
31
app/components/repos-list-item.coffee
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
`import Polling from 'travis/mixins/polling'`
|
||||||
|
`import { colorForState } from 'travis/utils/helpers'`
|
||||||
|
|
||||||
|
ReposListItemComponent = Ember.Component.extend Polling,
|
||||||
|
routing: Ember.inject.service('-routing')
|
||||||
|
|
||||||
|
tagName: 'li'
|
||||||
|
|
||||||
|
pollModels: 'repo'
|
||||||
|
|
||||||
|
classNames: ['repo']
|
||||||
|
classNameBindings: ['color', 'selected']
|
||||||
|
selected: (->
|
||||||
|
@get('repo') == @get('selectedRepo')
|
||||||
|
).property('selectedRepo')
|
||||||
|
|
||||||
|
color: (->
|
||||||
|
colorForState(@get('repo.defaultBranch.lastBuild.state'))
|
||||||
|
).property('repo.defaultBranch.lastBuild.state')
|
||||||
|
|
||||||
|
scrollTop: (->
|
||||||
|
if (window.scrollY > 0)
|
||||||
|
$('html, body').animate({scrollTop: 0}, 200)
|
||||||
|
)
|
||||||
|
|
||||||
|
click: ->
|
||||||
|
@scrollTop()
|
||||||
|
@get('routing').transitionTo('repo', @get('repo.slug').split('/'))
|
||||||
|
|
||||||
|
`export default ReposListItemComponent`
|
|
@ -1,27 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import Polling from 'travis/mixins/polling';
|
|
||||||
import { colorForState } from 'travis/utils/helpers';
|
|
||||||
|
|
||||||
export default Ember.Component.extend(Polling, {
|
|
||||||
routing: Ember.inject.service('-routing'),
|
|
||||||
tagName: 'li',
|
|
||||||
pollModels: 'repo',
|
|
||||||
classNames: ['repo'],
|
|
||||||
classNameBindings: ['selected'],
|
|
||||||
|
|
||||||
selected: function() {
|
|
||||||
return this.get('repo') === this.get('selectedRepo');
|
|
||||||
}.property('selectedRepo'),
|
|
||||||
|
|
||||||
color: function() {
|
|
||||||
return colorForState(this.get('repo.lastBuildState'));
|
|
||||||
}.property('repo.lastBuildState'),
|
|
||||||
|
|
||||||
scrollTop: function() {
|
|
||||||
if (window.scrollY > 0) {
|
|
||||||
return $('html, body').animate({
|
|
||||||
scrollTop: 0
|
|
||||||
}, 200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,48 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
|
||||||
auth: Ember.inject.service(),
|
|
||||||
|
|
||||||
currentUserBinding: 'auth.currentUser',
|
|
||||||
|
|
||||||
classRecent: function() {
|
|
||||||
if (this.get('tab') === 'recent') {
|
|
||||||
return 'active';
|
|
||||||
} else if (this.get('tab') === 'search' && this.get('auth.signedIn')) {
|
|
||||||
return 'hidden';
|
|
||||||
}
|
|
||||||
}.property('tab'),
|
|
||||||
|
|
||||||
classRunning: function() {
|
|
||||||
var classes;
|
|
||||||
classes = [];
|
|
||||||
if (this.get('tab') === 'running') {
|
|
||||||
classes.push('active');
|
|
||||||
}
|
|
||||||
return classes.join(' ');
|
|
||||||
}.property('tab'),
|
|
||||||
|
|
||||||
classOwned: function() {
|
|
||||||
var classes;
|
|
||||||
classes = [];
|
|
||||||
if (this.get('tab') === 'owned') {
|
|
||||||
classes.push('active');
|
|
||||||
}
|
|
||||||
if (this.get('currentUser')) {
|
|
||||||
classes.push('display-inline');
|
|
||||||
}
|
|
||||||
return classes.join(' ');
|
|
||||||
}.property('tab', 'currentUser'),
|
|
||||||
|
|
||||||
classSearch: function() {
|
|
||||||
if (this.get('tab') === 'search') {
|
|
||||||
return 'active';
|
|
||||||
}
|
|
||||||
}.property('tab'),
|
|
||||||
|
|
||||||
classNew: function() {
|
|
||||||
if (this.get('currentUser')) {
|
|
||||||
return 'display-inline';
|
|
||||||
}
|
|
||||||
}.property('currentUser')
|
|
||||||
});
|
|
|
@ -1,7 +1,66 @@
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
var sortCallback = function(repo1, repo2) {
|
||||||
|
// this function could be made simpler, but I think it's clearer this way
|
||||||
|
// what're we really trying to achieve
|
||||||
|
|
||||||
|
var lastBuild1 = repo1.get('defaultBranch.lastBuild');
|
||||||
|
var lastBuild2 = repo2.get('defaultBranch.lastBuild');
|
||||||
|
|
||||||
|
if(!lastBuild1 && !lastBuild2) {
|
||||||
|
// if both repos lack builds, put newer repo first
|
||||||
|
return repo1.get('id') > repo2.get('id') ? -1 : 1;
|
||||||
|
} else if(lastBuild1 && !lastBuild2) {
|
||||||
|
// if only repo1 has a build, it goes first
|
||||||
|
return -1;
|
||||||
|
} else if(lastBuild2 && !lastBuild1) {
|
||||||
|
// if only repo2 has a build, it goes first
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var finishedAt1 = lastBuild1.get('finishedAt');
|
||||||
|
var finishedAt2 = lastBuild2.get('finishedAt');
|
||||||
|
|
||||||
|
if(finishedAt1) {
|
||||||
|
finishedAt1 = new Date(finishedAt1);
|
||||||
|
}
|
||||||
|
if(finishedAt2) {
|
||||||
|
finishedAt2 = new Date(finishedAt2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(finishedAt1 && finishedAt2) {
|
||||||
|
// if both builds finished, put newer first
|
||||||
|
return finishedAt1.getTime() > finishedAt2.getTime() ? -1 : 1;
|
||||||
|
} else if(finishedAt1 && !finishedAt2) {
|
||||||
|
// if repo1 finished, but repo2 didn't, put repo2 first
|
||||||
|
return 1;
|
||||||
|
} else if(finishedAt2 && !finishedAt1) {
|
||||||
|
// if repo2 finisher, but repo1 didn't, put repo1 first
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
// none of the builds finished, put newer build first
|
||||||
|
return lastBuild1.get('id') > lastBuild2.get('id') ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw "should not happen";
|
||||||
|
};
|
||||||
|
|
||||||
var ReposListComponent = Ember.Component.extend({
|
var ReposListComponent = Ember.Component.extend({
|
||||||
tagName: 'ul'
|
tagName: 'ul',
|
||||||
|
|
||||||
|
sortedRepos: function() {
|
||||||
|
var repos = this.get('repos');
|
||||||
|
|
||||||
|
if(repos && repos.toArray) {
|
||||||
|
repos = repos.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(repos && repos.sort) {
|
||||||
|
return repos.sort(sortCallback);
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}.property('repos.[]', 'repos')
|
||||||
});
|
});
|
||||||
|
|
||||||
export default ReposListComponent;
|
export default ReposListComponent;
|
||||||
|
|
30
app/components/request-icon.coffee
Normal file
30
app/components/request-icon.coffee
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
`import Ember from 'ember'`
|
||||||
|
|
||||||
|
RequestIconComponent = Ember.Component.extend
|
||||||
|
|
||||||
|
tagName: 'span'
|
||||||
|
classNames: ['icon-request', 'icon']
|
||||||
|
classNameBindings: ['build.last_build.state', 'build.state']
|
||||||
|
|
||||||
|
isPush: (->
|
||||||
|
@get('build.last_build.event_type') == 'push' ||
|
||||||
|
@get('build.event_type') == 'push'
|
||||||
|
).property('build.last_build')
|
||||||
|
|
||||||
|
isPR: (->
|
||||||
|
@get('build.last_build.event_type') == 'pull_request' ||
|
||||||
|
@get('build.event_type') == 'pull_request'
|
||||||
|
).property('build.last_build')
|
||||||
|
|
||||||
|
isAPI: (->
|
||||||
|
@get('build.last_build.event_type') == 'api' ||
|
||||||
|
@get('build.event_type') == 'api'
|
||||||
|
).property('build.last_build')
|
||||||
|
|
||||||
|
isEmpty: (->
|
||||||
|
true if @get('build.last_build') == null || @get('build') == null
|
||||||
|
).property('build.last_build')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
`export default RequestIconComponent`
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user