diff --git a/app/controllers/build.coffee b/app/controllers/build.coffee
index 91c14983..1f86f62c 100644
--- a/app/controllers/build.coffee
+++ b/app/controllers/build.coffee
@@ -8,6 +8,7 @@ Controller = Ember.Controller.extend GithubUrlPropertievs,
commitBinding: 'build.commit'
currentUserBinding: 'controllers.repo.currentUser'
tabBinding: 'controllers.repo.tab'
+ sendFaviconStateChanges: true
currentItemBinding: 'build'
@@ -23,4 +24,9 @@ Controller = Ember.Controller.extend GithubUrlPropertievs,
gravatarImage(@get('commit.authorEmail'), 40)
).property('commit.authorEmail')
+ buildStateDidChange: (->
+ if @get('sendFaviconStateChanges')
+ @send('faviconStateDidChange', @get('build.state'))
+ ).observes('build.state')
+
`export default Controller`
diff --git a/app/controllers/job.coffee b/app/controllers/job.coffee
index f05e44ad..f262a0e4 100644
--- a/app/controllers/job.coffee
+++ b/app/controllers/job.coffee
@@ -16,4 +16,8 @@ Controller = Ember.Controller.extend
githubCommit(@get('repo.slug'), @get('commit.sha'))
).property('repo.slug', 'commit.sha')
+ jobStateDidChange: (->
+ @send('faviconStateDidChange', @get('job.state'))
+ ).observes('job.state')
+
`export default Controller`
diff --git a/app/index.html b/app/index.html
index 7d63a71d..c6a62dfc 100644
--- a/app/index.html
+++ b/app/index.html
@@ -6,6 +6,7 @@
{{title}}
+
{{content-for 'head'}}
diff --git a/app/mixins/build-favicon.coffee b/app/mixins/build-favicon.coffee
new file mode 100644
index 00000000..1d7bbed2
--- /dev/null
+++ b/app/mixins/build-favicon.coffee
@@ -0,0 +1,30 @@
+`import Ember from 'ember'`
+`import { colorForState } from 'travis/utils/helpers'`
+`import FaviconManager from 'travis/utils/favicon-manager'`
+`import getFaviconUri from 'travis/utils/favicon-data-uris'`
+
+Mixin = Ember.Mixin.create
+ actions:
+ faviconStateDidChange: (state) ->
+ if state
+ @setFaviconForState(state)
+ else
+ @setDefault()
+
+ init: ->
+ @faviconManager = new FaviconManager()
+
+ @_super.apply this, arguments
+
+ setFaviconForState: (state) ->
+ color = colorForState(state)
+
+ @setFavicon(getFaviconUri(color))
+
+ setDefault: ->
+ @setFavicon(getFaviconUri('default'))
+
+ setFavicon: (href) ->
+ @faviconManager.setFavicon(href)
+
+`export default Mixin`
diff --git a/app/routes/abstract-builds.coffee b/app/routes/abstract-builds.coffee
index 1fc355ba..1a79b7ec 100644
--- a/app/routes/abstract-builds.coffee
+++ b/app/routes/abstract-builds.coffee
@@ -15,6 +15,8 @@ Route = TravisRoute.extend
deactivate: ->
@controllerFor('repo').removeObserver(@get('path'), this, 'contentDidChange')
+ @_super.apply(this, arguments)
+
contentDidChange: ->
path = @get('path')
@controllerFor('builds').set('model', @controllerFor('repo').get(path))
diff --git a/app/routes/application.coffee b/app/routes/application.coffee
index 2e17f470..c46573fe 100644
--- a/app/routes/application.coffee
+++ b/app/routes/application.coffee
@@ -1,7 +1,8 @@
`import TravisRoute from 'travis/routes/basic'`
`import config from 'travis/config/environment'`
+`import BuildFaviconMixin from 'travis/mixins/build-favicon'`
-Route = TravisRoute.extend
+Route = TravisRoute.extend BuildFaviconMixin,
needsAuth: false
renderTemplate: ->
diff --git a/app/routes/build.coffee b/app/routes/build.coffee
index a1227588..2703be70 100644
--- a/app/routes/build.coffee
+++ b/app/routes/build.coffee
@@ -22,6 +22,7 @@ Route = TravisRoute.extend
@store.find('build', params.build_id)
deactivate: ->
+ @_super.apply(this, arguments)
@controllerFor('job').set('job', null)
@controllerFor('build').set('build', null)
diff --git a/app/routes/job.coffee b/app/routes/job.coffee
index f5b39c25..e207df5f 100644
--- a/app/routes/job.coffee
+++ b/app/routes/job.coffee
@@ -20,13 +20,24 @@ Route = TravisRoute.extend
if build = model.get('build')
build = @store.recordForId('build', build.get('id'))
- @controllerFor('build').set('build', build)
+ buildController = @controllerFor('build')
+
+ # this is a hack to not set favicon changes from build
+ # controller while we're viewing job, this should go away
+ # after refactoring of controllers
+ buildController.set('sendFaviconStateChanges', false)
+
+ buildController.set('build', build)
model: (params) ->
@store.find('job', params.job_id)
deactivate: ->
+ buildController = @controllerFor('build')
+ buildController.set('sendFaviconStateChanges', true)
@controllerFor('build').set('build', null)
@controllerFor('job').set('job', null)
+ @_super.apply(this, arguments)
+
`export default Route`
diff --git a/app/routes/main-tab.coffee b/app/routes/main-tab.coffee
index d35a328a..bbc1d4da 100644
--- a/app/routes/main-tab.coffee
+++ b/app/routes/main-tab.coffee
@@ -17,6 +17,8 @@ Route = TravisRoute.extend
deactivate: ->
@controllerFor('repos').removeObserver('firstObject', this, 'currentRepoDidChange')
+ @_super.apply(this, arguments)
+
currentRepoDidChange: ->
if repo = @controllerFor('repos').get('firstObject')
@controllerFor('repo').set('repo', repo)
diff --git a/app/routes/pull-requests.coffee b/app/routes/pull-requests.coffee
index 121aa633..4253f7c0 100644
--- a/app/routes/pull-requests.coffee
+++ b/app/routes/pull-requests.coffee
@@ -10,6 +10,7 @@ Route = AbstractBuildsRoute.extend(
this.controllerFor('builds').set('isPullRequestsList', true)
deactivate: ->
+ @_super.apply(this, arguments)
this.controllerFor('builds').set('isPullRequestsList', false)
)
diff --git a/app/routes/repo/index.coffee b/app/routes/repo/index.coffee
index 348425cf..0715b315 100644
--- a/app/routes/repo/index.coffee
+++ b/app/routes/repo/index.coffee
@@ -16,4 +16,6 @@ Route = TravisRoute.extend
@controllerFor('build').set('build', null)
@controllerFor('job').set('job', null)
+ @_super.apply(this, arguments)
+
`export default Route`
diff --git a/app/utils/favicon-data-uris.js b/app/utils/favicon-data-uris.js
new file mode 100644
index 00000000..088a1937
--- /dev/null
+++ b/app/utils/favicon-data-uris.js
@@ -0,0 +1,13 @@
+var __inlineImageDataUri__ = function() {}; // in case image inliner doesn't run
+
+var uris = {
+ default: __inlineImageDataUri__('favicon.png'),
+ red: __inlineImageDataUri__('favicon-red.png'),
+ gray: __inlineImageDataUri__('favicon-gray.png'),
+ green: __inlineImageDataUri__('favicon-green.png'),
+ yellow: __inlineImageDataUri__('favicon-yellow.png')
+};
+
+export default function(type) {
+ return uris[type] || uris.default;
+}
diff --git a/app/utils/favicon-manager.coffee b/app/utils/favicon-manager.coffee
new file mode 100644
index 00000000..4be9c524
--- /dev/null
+++ b/app/utils/favicon-manager.coffee
@@ -0,0 +1,38 @@
+`import Ember from 'ember'`
+
+manager = (headTag) ->
+ @headTag = headTag if headTag
+
+ return this
+
+manager.prototype.getHeadTag = ->
+ @headTag || document.getElementsByTagName('head')[0]
+
+manager.prototype.setFavicon = (href) ->
+ head = @getHeadTag()
+
+ if oldLink = @getLinkTag()
+ head.removeChild(oldLink)
+
+ link = @createLinkTag()
+ head.appendChild(link)
+
+ link.setAttribute('href', href)
+ setTimeout ->
+ link.setAttribute('href', href)
+ , 1
+
+manager.prototype.getLinkTag = ->
+ links = @getHeadTag().getElementsByTagName('link')
+ if links.length
+ for link in links
+ if (link.getAttribute('rel') || '').trim() == 'icon'
+ return link
+
+manager.prototype.createLinkTag = ->
+ link = document.createElement('link')
+ link.setAttribute('rel', 'icon')
+ link.setAttribute('type', 'image/png')
+ @getHeadTag().appendChild(link)
+
+`export default manager`
diff --git a/package.json b/package.json
index 498c2e91..e75e0df1 100644
--- a/package.json
+++ b/package.json
@@ -31,7 +31,7 @@
"ember-cli-htmlbars": "^0.6.0",
"ember-cli-ic-ajax": "0.1.1",
"ember-cli-inject-live-reload": "^1.3.0",
- "ember-cli-inline-images": "^0.0.3",
+ "ember-cli-inline-images": "^0.0.4",
"ember-cli-pretender": "0.3.1",
"ember-cli-qunit": "0.3.0",
"ember-cli-sauce": "0.0.7",
diff --git a/public/cancelled-favicon.ico b/public/cancelled-favicon.ico
deleted file mode 100644
index e82838fa..00000000
Binary files a/public/cancelled-favicon.ico and /dev/null differ
diff --git a/public/failed-favicon.ico b/public/failed-favicon.ico
deleted file mode 100644
index 81497d53..00000000
Binary files a/public/failed-favicon.ico and /dev/null differ
diff --git a/public/favicon.ico b/public/favicon.ico
deleted file mode 100644
index acb338dc..00000000
Binary files a/public/favicon.ico and /dev/null differ
diff --git a/public/images/favicon-gray.png b/public/images/favicon-gray.png
new file mode 100644
index 00000000..e33f5dce
Binary files /dev/null and b/public/images/favicon-gray.png differ
diff --git a/public/images/favicon-green.png b/public/images/favicon-green.png
new file mode 100644
index 00000000..3a243de1
Binary files /dev/null and b/public/images/favicon-green.png differ
diff --git a/public/images/favicon-red.png b/public/images/favicon-red.png
new file mode 100644
index 00000000..ec9e8f49
Binary files /dev/null and b/public/images/favicon-red.png differ
diff --git a/public/images/favicon-yellow.png b/public/images/favicon-yellow.png
new file mode 100644
index 00000000..b005d9f4
Binary files /dev/null and b/public/images/favicon-yellow.png differ
diff --git a/public/images/favicon.png b/public/images/favicon.png
new file mode 100644
index 00000000..4cd61479
Binary files /dev/null and b/public/images/favicon.png differ
diff --git a/public/passed-favicon.ico b/public/passed-favicon.ico
deleted file mode 100644
index b43783e2..00000000
Binary files a/public/passed-favicon.ico and /dev/null differ
diff --git a/public/queued-favicon.ico b/public/queued-favicon.ico
deleted file mode 100644
index 558bf72e..00000000
Binary files a/public/queued-favicon.ico and /dev/null differ
diff --git a/tests/unit/utils/favicon-manager-test.coffee b/tests/unit/utils/favicon-manager-test.coffee
new file mode 100644
index 00000000..d5013381
--- /dev/null
+++ b/tests/unit/utils/favicon-manager-test.coffee
@@ -0,0 +1,51 @@
+`import Ember from 'ember'`
+`import FaviconManager from 'travis/utils/favicon-manager'`
+
+manager = null
+fakeHead = null
+
+module("Favicon manager",
+ beforeEach: ->
+ fakeHead = $('').appendTo($('#qunit-fixture'))
+ manager = new FaviconManager(fakeHead[0])
+ afterEach: ->
+ fakeHead.remove()
+ manager = null
+)
+
+test 'use tag by default', ->
+ manager = new FaviconManager()
+ equal manager.getHeadTag(), $('head')[0]
+
+test 'set favicon if there is no link tag in head', ->
+ equal fakeHead.find('link').length, 0, 'there should be no link tags initially'
+
+ manager.setFavicon('foobar')
+
+ link = fakeHead.find('link')[0]
+
+ ok link, 'link tag should be added by favicon manager'
+ equal link.getAttribute('href'), 'foobar', 'href attribute for the link should be properly set'
+ equal link.getAttribute('rel'), 'icon', 'rel attribute for the link should be properly set'
+ equal link.getAttribute('type'), 'image/png', 'type attribute for the link should be properly set'
+
+test 'replace exisiting link tag', ->
+ fakeHead.append($(''))
+
+ ok 'foo', fakeHead.find('link').attr('id'), 'initially link should exist'
+
+ manager.setFavicon('foobar')
+
+ links = fakeHead.find('link')
+ equal links.length, 1, 'there should be only one link in head'
+
+ link = links[0]
+
+ ok !link.getAttribute('id'), 'existing link should be replaced with a new one'
+ equal link.getAttribute('href'), 'foobar', 'href attribute for the link should be properly set'
+ equal link.getAttribute('rel'), 'icon', 'rel attribute for the link should be properly set'
+ equal link.getAttribute('type'), 'image/png', 'type attribute for the link should be properly set'
+
+test 'find link with rel=icon only', ->
+ fakeHead.append($(''))
+ ok !manager.getLinkTag()