From 3c6fe791238af11aac9138f3afa6ddbf47b04487 Mon Sep 17 00:00:00 2001 From: Radek Stepan Date: Fri, 26 Sep 2014 21:01:58 -0700 Subject: [PATCH] system notification --- README.md | 2 + public/css/app.bundle.css | 7 +- public/css/app.css | 7 +- public/js/app.bundle.js | 103 ++++++++++++++---------- public/js/app.js | 103 ++++++++++++++---------- src/app.coffee | 4 +- src/modules/router.coffee | 13 ++- src/styles/app.styl | 13 ++- src/templates/milestones.mustache | 31 ++++++++ src/templates/notify.mustache | 15 +++- src/templates/pages/project.mustache | 4 +- src/templates/projects.mustache | 114 +++++++++++++-------------- src/views/milestones.coffee | 16 ++++ src/views/notify.coffee | 31 ++++++-- src/views/pages/project.coffee | 9 +-- 15 files changed, 297 insertions(+), 175 deletions(-) create mode 100644 src/templates/milestones.mustache create mode 100644 src/views/milestones.coffee diff --git a/README.md b/README.md index 8f32bcc..dcc44a1 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ GitHub Burndown Chart as a service. Public repos are free, for private access au ### The 20% +- [ ] verify that project exists on project page when fetching it remotely (add behind the scenes) - [ ] need to show status (receiving information etc.) per repo - [ ] provide a documentation site - [ ] visiting a chart page saves the project if it isn't saved already @@ -34,6 +35,7 @@ GitHub Burndown Chart as a service. Public repos are free, for private access au - [ ] highlight for a moment recently changed milestone - [ ] smooth animation when transitioning between icons and notifications - [ ] show logged-in state only after it is known +- [x] how does a message look like when we have an error and nothing to render? - [x] format milestone titles prepending "Milestone" word if appropriate - [x] Variable document.title on different pages - [x] be able to go back to homepage diff --git a/public/css/app.bundle.css b/public/css/app.bundle.css index 47f58af..5764ebc 100644 --- a/public/css/app.bundle.css +++ b/public/css/app.bundle.css @@ -449,12 +449,13 @@ ul{list-style-type:none;margin:0;padding:0;} ul li{display:inline-block} .wrap{width:800px;margin:0 auto} #notify{position:fixed;top:-68px;z-index:1;width:100%;background:#fcfcfc;color:#aaafbf;border-top:3px solid #aaafbf;border-bottom:1px solid #f3f4f8;} +#notify.system{top:0%;left:50%;-webkit-transform:translateX(-50%) translateY(-50%);-moz-transform:translateX(-50%) translateY(-50%);-o-transform:translateX(-50%) translateY(-50%);-ms-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%);width:500px} #notify.success{border-top-color:#00b361;color:#00b361} #notify.warn{border-top-color:#ea9712;color:#ea9712} #notify.alert{border-top-color:#c1041c;color:#c1041c} -#notify .icon,#notify p{display:inline-block} -#notify .icon{font-size:26px;padding:18px;width:38px} -#notify p{margin-right:10px} +#notify .icon,#notify p{display:block} +#notify .icon{font-size:26px;padding:18px;width:38px;float:left} +#notify p{padding:20px 20px 20px 74px;text-align:justify} #head{background:#c1041c;height:64px;} #head #icon{font-size:26px;margin:0;padding:10px 0;line-height:44px;height:44px;width:74px;background:#77000e;display:inline-block;color:#c1041c;margin:0;text-align:center} #head .q{position:relative;display:inline-block;margin:13px 20px 0 20px;vertical-align:top;} diff --git a/public/css/app.css b/public/css/app.css index e1f2fea..21ab131 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -42,12 +42,13 @@ ul{list-style-type:none;margin:0;padding:0;} ul li{display:inline-block} .wrap{width:800px;margin:0 auto} #notify{position:fixed;top:-68px;z-index:1;width:100%;background:#fcfcfc;color:#aaafbf;border-top:3px solid #aaafbf;border-bottom:1px solid #f3f4f8;} +#notify.system{top:0%;left:50%;-webkit-transform:translateX(-50%) translateY(-50%);-moz-transform:translateX(-50%) translateY(-50%);-o-transform:translateX(-50%) translateY(-50%);-ms-transform:translateX(-50%) translateY(-50%);transform:translateX(-50%) translateY(-50%);width:500px} #notify.success{border-top-color:#00b361;color:#00b361} #notify.warn{border-top-color:#ea9712;color:#ea9712} #notify.alert{border-top-color:#c1041c;color:#c1041c} -#notify .icon,#notify p{display:inline-block} -#notify .icon{font-size:26px;padding:18px;width:38px} -#notify p{margin-right:10px} +#notify .icon,#notify p{display:block} +#notify .icon{font-size:26px;padding:18px;width:38px;float:left} +#notify p{padding:20px 20px 20px 74px;text-align:justify} #head{background:#c1041c;height:64px;} #head #icon{font-size:26px;margin:0;padding:10px 0;line-height:44px;height:44px;width:74px;background:#77000e;display:inline-block;color:#c1041c;margin:0;text-align:center} #head .q{position:relative;display:inline-block;margin:13px 20px 0 20px;vertical-align:top;} diff --git a/public/js/app.bundle.js b/public/js/app.bundle.js index f382941..b282a7d 100644 --- a/public/js/app.bundle.js +++ b/public/js/app.bundle.js @@ -40460,7 +40460,7 @@ Router.prototype.mount = function(routes, path) { // app.coffee root.require.register('burnchart/src/app.js', function(exports, require, module) { - var App, Header, Notify, Router, key, _i, _len, _ref; + var App, Header, Notify, key, router, _i, _len, _ref; _ref = ['utils/mixins', 'models/projects']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -40468,12 +40468,12 @@ Router.prototype.mount = function(routes, path) { require("./" + key); } - Router = require('./modules/router'); - Header = require('./views/header'); Notify = require('./views/notify'); + router = require('./modules/router'); + App = Ractive.extend({ 'template': require('./templates/layout'), 'components': { @@ -40481,7 +40481,7 @@ Router.prototype.mount = function(routes, path) { Notify: Notify }, init: function() { - return new Router(); + return router.init('/'); } }); @@ -41197,7 +41197,7 @@ Router.prototype.mount = function(routes, path) { }); }; - router = Router({ + module.exports = window.router = router = Router({ '/': _.partial(route, 'index'), '/new/project': _.partial(route, 'new'), '/:owner/:name': _.partial(route, 'project'), @@ -41208,16 +41208,14 @@ Router.prototype.mount = function(routes, path) { }, '/notify': function() { mediator.fire('!app/loading', true); - mediator.fire('!app/notify', 'You did something real good', 'warn'); + mediator.fire('!app/notify', { + 'text': 'You have some interesting news in your inbox. Go check it out now.', + 'type': 'warn' + }); return window.location.hash = '#'; } }); - module.exports = function() { - router.init('/'); - return router; - }; - }); // header.mustache @@ -41244,10 +41242,16 @@ Router.prototype.mount = function(routes, path) { module.exports = ["","
","","
"," ","
","","
","
"," © 2012-2014 Cloudfire Systems","
","
"].join("\n"); }); + // milestones.mustache + root.require.register('burnchart/src/templates/milestones.js', function(exports, require, module) { + + module.exports = ["{{#milestones.length}}","
","
"," Sorted by priority","

Milestones

","
",""," "," {{#milestones}}"," "," "," "," "," {{/milestones}}","
"," {{ title }}"," ","
"," {{Math.floor(format.progress(closed_issues, open_issues))}}%"," {{{ format.due(due_on) }}}","
","
","
","
","
","","
"," Edit","
","
","{{/milestones.length}}"].join("\n"); + }); + // notify.mustache root.require.register('burnchart/src/templates/notify.js', function(exports, require, module) { - module.exports = ["{{#text}}","
"," ","

{{text}}

","
","{{/text}}"].join("\n"); + module.exports = ["{{#text}}"," {{#system}}","
"," ","

{{text}}

","
"," {{else}}","
"," ","

{{text}}

","
"," {{/system}}","{{/text}}"].join("\n"); }); // chart.mustache @@ -41271,13 +41275,13 @@ Router.prototype.mount = function(routes, path) { // project.mustache root.require.register('burnchart/src/templates/pages/project.js', function(exports, require, module) { - module.exports = ["
","
","

radekstepan/disposable

","
","
","","
"," Project milestones go here","
"].join("\n"); + module.exports = ["
","
","

{{route.join('/')}}

","
","
","","
"," ","
"].join("\n"); }); // projects.mustache root.require.register('burnchart/src/templates/projects.js', function(exports, require, module) { - module.exports = ["{{#projects.list.length}}","
","
"," Sorted by priority","

Projects

","
",""," "," {{#projects.list}}"," {{#milestones}}"," "," "," "," "," "," {{/milestones}}"," {{/projects.list}}",""," ","
"," {{owner}}/{{name}}"," "," {{ title }}"," ","
"," {{Math.floor(format.progress(closed_issues, open_issues))}}%"," {{{ format.due(due_on) }}}","
","
","
","
","
","","
"," Edit","
","
","{{/projects.list}}"].join("\n"); + module.exports = ["{{#projects.list.length}}","
","
"," Sorted by priority","

Projects

","
",""," "," {{#projects.list}}"," {{#milestones}}"," "," "," "," "," "," {{/milestones}}"," {{/projects.list}}","
"," {{owner}}/{{name}}"," "," {{ title }}"," ","
"," {{Math.floor(format.progress(closed_issues, open_issues))}}%"," {{{ format.due(due_on) }}}","
","
","
","
","
","","
"," Edit","
","
","{{/projects.list.length}}","",""].join("\n"); }); // date.coffee @@ -41479,6 +41483,33 @@ Router.prototype.mount = function(routes, path) { }); + // milestones.coffee + root.require.register('burnchart/src/views/milestones.js', function(exports, require, module) { + + var Icons, mediator, projects; + + mediator = require('../modules/mediator'); + + projects = require('../models/projects'); + + Icons = require('./icons'); + + module.exports = Ractive.extend({ + 'template': require('../templates/milestones'), + 'components': { + Icons: Icons + }, + 'adapt': [Ractive.adaptors.Ractive], + init: function() { + return this.set('milestones', _.filter(projects.get('list'), { + 'owner': this.get('owner'), + 'name': this.get('name') + })); + } + }); + + }); + // notify.coffee root.require.register('burnchart/src/views/notify.js', function(exports, require, module) { @@ -41496,16 +41527,20 @@ Router.prototype.mount = function(routes, path) { 'top': HEIGHT }, init: function() { - var _this = this; - return mediator.on('!app/notify', function(text, type) { - if (type == null) { - type = ''; - } - _this.set({ - text: text, - type: type - }); - _this.animate('top', 0, { + var defaults, + _this = this; + defaults = { + 'text': '', + 'type': '', + 'system': false, + 'icon': 'megaphone' + }; + return mediator.on('!app/notify', function(opts) { + var pos; + opts = _.defaults(opts, defaults); + _this.set(opts); + pos = [0, 50][+opts.system]; + _this.animate('top', pos, { 'easing': d3.ease('bounce'), 'duration': 800 }); @@ -41643,30 +41678,18 @@ Router.prototype.mount = function(routes, path) { // project.coffee root.require.register('burnchart/src/views/pages/project.js', function(exports, require, module) { - var Hero, Projects, format; + var Milestones; - Hero = require('../hero'); - - Projects = require('../projects'); - - format = require('../../utils/format'); + Milestones = require('../milestones'); module.exports = Ractive.extend({ 'template': require('../../templates/pages/project'), 'components': { - Hero: Hero, - Projects: Projects - }, - 'data': { - format: format + Milestones: Milestones }, init: function() { - var name, owner, route, _ref; + var name, owner, _ref; _ref = this.get('route'), owner = _ref[0], name = _ref[1]; - route = { - owner: owner, - name: name - }; return document.title = "" + owner + "/" + name; } }); diff --git a/public/js/app.js b/public/js/app.js index 449dae1..8790e1d 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -7,7 +7,7 @@ // app.coffee root.require.register('burnchart/src/app.js', function(exports, require, module) { - var App, Header, Notify, Router, key, _i, _len, _ref; + var App, Header, Notify, key, router, _i, _len, _ref; _ref = ['utils/mixins', 'models/projects']; for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -15,12 +15,12 @@ require("./" + key); } - Router = require('./modules/router'); - Header = require('./views/header'); Notify = require('./views/notify'); + router = require('./modules/router'); + App = Ractive.extend({ 'template': require('./templates/layout'), 'components': { @@ -28,7 +28,7 @@ Notify: Notify }, init: function() { - return new Router(); + return router.init('/'); } }); @@ -744,7 +744,7 @@ }); }; - router = Router({ + module.exports = window.router = router = Router({ '/': _.partial(route, 'index'), '/new/project': _.partial(route, 'new'), '/:owner/:name': _.partial(route, 'project'), @@ -755,16 +755,14 @@ }, '/notify': function() { mediator.fire('!app/loading', true); - mediator.fire('!app/notify', 'You did something real good', 'warn'); + mediator.fire('!app/notify', { + 'text': 'You have some interesting news in your inbox. Go check it out now.', + 'type': 'warn' + }); return window.location.hash = '#'; } }); - module.exports = function() { - router.init('/'); - return router; - }; - }); // header.mustache @@ -791,10 +789,16 @@ module.exports = ["","
","","
"," ","
","","
","
"," © 2012-2014 Cloudfire Systems","
","
"].join("\n"); }); + // milestones.mustache + root.require.register('burnchart/src/templates/milestones.js', function(exports, require, module) { + + module.exports = ["{{#milestones.length}}","
","
"," Sorted by priority","

Milestones

","
",""," "," {{#milestones}}"," "," "," "," "," {{/milestones}}","
"," {{ title }}"," ","
"," {{Math.floor(format.progress(closed_issues, open_issues))}}%"," {{{ format.due(due_on) }}}","
","
","
","
","
","","
"," Edit","
","
","{{/milestones.length}}"].join("\n"); + }); + // notify.mustache root.require.register('burnchart/src/templates/notify.js', function(exports, require, module) { - module.exports = ["{{#text}}","
"," ","

{{text}}

","
","{{/text}}"].join("\n"); + module.exports = ["{{#text}}"," {{#system}}","
"," ","

{{text}}

","
"," {{else}}","
"," ","

{{text}}

","
"," {{/system}}","{{/text}}"].join("\n"); }); // chart.mustache @@ -818,13 +822,13 @@ // project.mustache root.require.register('burnchart/src/templates/pages/project.js', function(exports, require, module) { - module.exports = ["
","
","

radekstepan/disposable

","
","
","","
"," Project milestones go here","
"].join("\n"); + module.exports = ["
","
","

{{route.join('/')}}

","
","
","","
"," ","
"].join("\n"); }); // projects.mustache root.require.register('burnchart/src/templates/projects.js', function(exports, require, module) { - module.exports = ["{{#projects.list.length}}","
","
"," Sorted by priority","

Projects

","
",""," "," {{#projects.list}}"," {{#milestones}}"," "," "," "," "," "," {{/milestones}}"," {{/projects.list}}",""," ","
"," {{owner}}/{{name}}"," "," {{ title }}"," ","
"," {{Math.floor(format.progress(closed_issues, open_issues))}}%"," {{{ format.due(due_on) }}}","
","
","
","
","
","","
"," Edit","
","
","{{/projects.list}}"].join("\n"); + module.exports = ["{{#projects.list.length}}","
","
"," Sorted by priority","

Projects

","
",""," "," {{#projects.list}}"," {{#milestones}}"," "," "," "," "," "," {{/milestones}}"," {{/projects.list}}","
"," {{owner}}/{{name}}"," "," {{ title }}"," ","
"," {{Math.floor(format.progress(closed_issues, open_issues))}}%"," {{{ format.due(due_on) }}}","
","
","
","
","
","","
"," Edit","
","
","{{/projects.list.length}}","",""].join("\n"); }); // date.coffee @@ -1026,6 +1030,33 @@ }); + // milestones.coffee + root.require.register('burnchart/src/views/milestones.js', function(exports, require, module) { + + var Icons, mediator, projects; + + mediator = require('../modules/mediator'); + + projects = require('../models/projects'); + + Icons = require('./icons'); + + module.exports = Ractive.extend({ + 'template': require('../templates/milestones'), + 'components': { + Icons: Icons + }, + 'adapt': [Ractive.adaptors.Ractive], + init: function() { + return this.set('milestones', _.filter(projects.get('list'), { + 'owner': this.get('owner'), + 'name': this.get('name') + })); + } + }); + + }); + // notify.coffee root.require.register('burnchart/src/views/notify.js', function(exports, require, module) { @@ -1043,16 +1074,20 @@ 'top': HEIGHT }, init: function() { - var _this = this; - return mediator.on('!app/notify', function(text, type) { - if (type == null) { - type = ''; - } - _this.set({ - text: text, - type: type - }); - _this.animate('top', 0, { + var defaults, + _this = this; + defaults = { + 'text': '', + 'type': '', + 'system': false, + 'icon': 'megaphone' + }; + return mediator.on('!app/notify', function(opts) { + var pos; + opts = _.defaults(opts, defaults); + _this.set(opts); + pos = [0, 50][+opts.system]; + _this.animate('top', pos, { 'easing': d3.ease('bounce'), 'duration': 800 }); @@ -1190,30 +1225,18 @@ // project.coffee root.require.register('burnchart/src/views/pages/project.js', function(exports, require, module) { - var Hero, Projects, format; + var Milestones; - Hero = require('../hero'); - - Projects = require('../projects'); - - format = require('../../utils/format'); + Milestones = require('../milestones'); module.exports = Ractive.extend({ 'template': require('../../templates/pages/project'), 'components': { - Hero: Hero, - Projects: Projects - }, - 'data': { - format: format + Milestones: Milestones }, init: function() { - var name, owner, route, _ref; + var name, owner, _ref; _ref = this.get('route'), owner = _ref[0], name = _ref[1]; - route = { - owner: owner, - name: name - }; return document.title = "" + owner + "/" + name; } }); diff --git a/src/app.coffee b/src/app.coffee index 832a300..5e2e69b 100644 --- a/src/app.coffee +++ b/src/app.coffee @@ -4,9 +4,9 @@ ] ) -Router = require './modules/router' Header = require './views/header' Notify = require './views/notify' +router = require './modules/router' App = Ractive.extend @@ -15,6 +15,6 @@ App = Ractive.extend 'components': { Header, Notify } init: -> - new Router() + router.init '/' module.exports = new App() \ No newline at end of file diff --git a/src/modules/router.coffee b/src/modules/router.coffee index 2abb928..b7489a1 100644 --- a/src/modules/router.coffee +++ b/src/modules/router.coffee @@ -6,7 +6,7 @@ route = (page, args...) -> Page = require "../views/pages/#{page}" new Page { el, 'data': { 'route': args } } -router = Router +module.exports = window.router = router = Router '/': _.partial route, 'index' '/new/project': _.partial route, 'new' '/:owner/:name': _.partial route, 'project' @@ -17,10 +17,7 @@ router = Router window.location.hash = '#' '/notify': -> mediator.fire '!app/loading', yes - mediator.fire '!app/notify', 'You did something real good', 'warn' - window.location.hash = '#' - -module.exports = -> - # Init the routes. - router.init '/' - router \ No newline at end of file + mediator.fire '!app/notify', + 'text': 'You have some interesting news in your inbox. Go check it out now.' + 'type': 'warn' + window.location.hash = '#' \ No newline at end of file diff --git a/src/styles/app.styl b/src/styles/app.styl index 250828a..4ea328e 100644 --- a/src/styles/app.styl +++ b/src/styles/app.styl @@ -40,6 +40,13 @@ ul border-top: 3px solid #AAAFBF border-bottom: 1px solid #F3F4F8 + // system-wide notification + &.system + top: 0% // hide from view + left: 50% + transform: translateX(-50%) translateY(-50%) + width: 500px + &.success border-top-color: #00B361 color: #00B361 @@ -53,15 +60,17 @@ ul color: $strong_color .icon, p - display: inline-block + display: block .icon font-size: 26px padding: 18px width: 38px + float: left p - margin-right: 10px + padding: 20px 20px 20px 74px + text-align: justify #head background: $strong_color diff --git a/src/templates/milestones.mustache b/src/templates/milestones.mustache new file mode 100644 index 0000000..84cd6a7 --- /dev/null +++ b/src/templates/milestones.mustache @@ -0,0 +1,31 @@ +{{#milestones.length}} +
+
+ Sorted by priority +

Milestones

+
+ + + {{#milestones}} + + + + + {{/milestones}} +
+ {{ title }} + +
+ {{Math.floor(format.progress(closed_issues, open_issues))}}% + {{{ format.due(due_on) }}} +
+
+
+
+
+ + +
+{{/milestones.length}} \ No newline at end of file diff --git a/src/templates/notify.mustache b/src/templates/notify.mustache index d38d846..c0fe1b5 100644 --- a/src/templates/notify.mustache +++ b/src/templates/notify.mustache @@ -1,6 +1,13 @@ {{#text}} -
- -

{{text}}

-
+ {{#system}} +
+ +

{{text}}

+
+ {{else}} +
+ +

{{text}}

+
+ {{/system}} {{/text}} \ No newline at end of file diff --git a/src/templates/pages/project.mustache b/src/templates/pages/project.mustache index bf37e56..efd7761 100644 --- a/src/templates/pages/project.mustache +++ b/src/templates/pages/project.mustache @@ -1,9 +1,9 @@
-

radekstepan/disposable

+

{{route.join('/')}}

- Project milestones go here +
\ No newline at end of file diff --git a/src/templates/projects.mustache b/src/templates/projects.mustache index c2c55e4..e55e8cc 100644 --- a/src/templates/projects.mustache +++ b/src/templates/projects.mustache @@ -15,7 +15,7 @@ {{ title }} - +
{{Math.floor(format.progress(closed_issues, open_issues))}}% {{{ format.due(due_on) }}} @@ -27,65 +27,65 @@ {{/milestones}} {{/projects.list}} - -
-{{/projects.list}} \ No newline at end of file +{{/projects.list.length}} + + \ No newline at end of file diff --git a/src/views/milestones.coffee b/src/views/milestones.coffee new file mode 100644 index 0000000..8f3c999 --- /dev/null +++ b/src/views/milestones.coffee @@ -0,0 +1,16 @@ +mediator = require '../modules/mediator' +projects = require '../models/projects' +Icons = require './icons' + +module.exports = Ractive.extend + + 'template': require '../templates/milestones' + + 'components': { Icons } + + 'adapt': [ Ractive.adaptors.Ractive ] + + init: -> + @set 'milestones', _.filter projects.get('list'), + 'owner': @get 'owner' + 'name': @get 'name' \ No newline at end of file diff --git a/src/views/notify.coffee b/src/views/notify.coffee index a05bd17..e654cf2 100644 --- a/src/views/notify.coffee +++ b/src/views/notify.coffee @@ -11,18 +11,35 @@ module.exports = Ractive.extend 'top': HEIGHT init: -> - # Save the notify text on us. - mediator.on '!app/notify', (text, type='') => - @set { text, type } - @animate 'top', 0, # slide to view + defaults = + 'text': '' + 'type': '' + 'system': no + 'icon': 'megaphone' + + # Animate. + # type: alert/warn/success + # system: yes/no + mediator.on '!app/notify', (opts) => + opts = _.defaults opts, defaults + + # Set the text. + @set opts + # Which position to slide to? + pos = [ 0, 50 ][ +opts.system ] # 0px or 50% from top + # Slide into view. + @animate 'top', pos, 'easing': d3.ease('bounce') 'duration': 800 _.delay => - @animate 'top', HEIGHT, # slide out of view + # Slide out of the view. + @animate 'top', HEIGHT, 'easing': d3.ease('back') 'complete': => - @set 'text', null # reset - , 5e3 # ttl + # Reset the text when all is done. + @set 'text', null + # Ttl. + , 5e3 'components': { Icons } diff --git a/src/views/pages/project.coffee b/src/views/pages/project.coffee index 5a48e9d..cc52706 100644 --- a/src/views/pages/project.coffee +++ b/src/views/pages/project.coffee @@ -1,17 +1,12 @@ -Hero = require '../hero' -Projects = require '../projects' -format = require '../../utils/format' +Milestones = require '../milestones' module.exports = Ractive.extend 'template': require '../../templates/pages/project' - 'components': { Hero, Projects } - - 'data': { format } + 'components': { Milestones } init: -> [ owner, name ] = @get 'route' - route = { owner, name } document.title = "#{owner}/#{name}" \ No newline at end of file