From 0e361e9a25c8d124ce54f7e7eac8adcaa70ef171 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sat, 30 Jun 2012 15:38:38 +0200 Subject: [PATCH] refactor and add sidebar --- AssetFile | 4 - assets/javascripts/app/app.coffee | 15 +- assets/javascripts/app/controllers.coffee | 16 +- assets/javascripts/app/layout.coffee | 11 + assets/javascripts/app/layout/base.coffee | 20 ++ .../app/{layouts => layout}/default.coffee | 34 +- assets/javascripts/app/layout/profile.coffee | 7 + assets/javascripts/app/layout/sidebar.coffee | 24 ++ assets/javascripts/app/layouts.coffee | 27 -- assets/javascripts/app/layouts/profile.coffee | 3 - assets/javascripts/app/models.coffee | 5 +- assets/javascripts/app/models/job.coffee | 3 +- assets/javascripts/app/models/worker.coffee | 12 +- .../{application.hbs => layouts/default.hbs} | 4 + .../app/templates/layouts/profile.hbs | 9 + .../app/templates/layouts/sidebar.hbs | 40 +++ .../javascripts/app/templates/queues/list.hbs | 15 + .../app/templates/workers/list.hbs | 22 ++ assets/javascripts/app/views.coffee | 7 +- assets/javascripts/app/views/app.coffee | 3 - assets/javascripts/lib/mocks.coffee | 18 ++ public/index.html | 2 +- public/javascripts/application.js | 2 +- public/javascripts/mocks.js | 299 ------------------ 24 files changed, 230 insertions(+), 372 deletions(-) create mode 100644 assets/javascripts/app/layout.coffee create mode 100644 assets/javascripts/app/layout/base.coffee rename assets/javascripts/app/{layouts => layout}/default.coffee (59%) create mode 100644 assets/javascripts/app/layout/profile.coffee create mode 100644 assets/javascripts/app/layout/sidebar.coffee delete mode 100644 assets/javascripts/app/layouts.coffee delete mode 100644 assets/javascripts/app/layouts/profile.coffee rename assets/javascripts/app/templates/{application.hbs => layouts/default.hbs} (77%) create mode 100644 assets/javascripts/app/templates/layouts/profile.hbs create mode 100644 assets/javascripts/app/templates/layouts/sidebar.hbs create mode 100644 assets/javascripts/app/templates/queues/list.hbs create mode 100644 assets/javascripts/app/templates/workers/list.hbs delete mode 100644 assets/javascripts/app/views/app.coffee delete mode 100644 public/javascripts/mocks.js diff --git a/AssetFile b/AssetFile index 9d275f62..3e47163f 100644 --- a/AssetFile +++ b/AssetFile @@ -34,10 +34,6 @@ input 'assets/javascripts' do concat 'app/templates.js' end - match 'mocks.js' do - concat 'mocks.js' - end - match '{app,config,lib}/**/*.js' do minispade( string: true, diff --git a/assets/javascripts/app/app.coffee b/assets/javascripts/app/app.coffee index 2404a7e5..f315881f 100644 --- a/assets/javascripts/app/app.coffee +++ b/assets/javascripts/app/app.coffee @@ -1,4 +1,5 @@ require 'hax0rs' +require 'ext/jquery' # $.mockjaxSettings.log = false # Ember.LOG_BINDINGS = true @@ -6,6 +7,19 @@ require 'hax0rs' @Travis = Em.Namespace.create CONFIG_KEYS: ['rvm', 'gemfile', 'env', 'otp_release', 'php', 'node_js', 'perl', 'python', 'scala'] + # QUEUES: [ + # { name: 'common', display: 'Common' }, + # { name: 'php', display: 'PHP, Perl and Python' }, + # { name: 'node_js', display: 'Node.js' }, + # { name: 'jvmotp', display: 'JVM and Erlang' }, + # { name: 'rails', display: 'Rails' }, + # { name: 'spree', display: 'Spree' }, + # ], + QUEUES: [ + { name: 'common', display: 'Common' }, + { name: 'jvmotp', display: 'JVM and Erlang' }, + ], + run: -> @app = Travis.App.create(this) @app.initialize() @@ -17,7 +31,6 @@ require 'hax0rs' @_super(Em.Object.create()) @routes.start() -require 'ext/jquery' require 'controllers' require 'helpers' require 'layout' diff --git a/assets/javascripts/app/controllers.coffee b/assets/javascripts/app/controllers.coffee index 2b7a50c3..b2a225a8 100644 --- a/assets/javascripts/app/controllers.coffee +++ b/assets/javascripts/app/controllers.coffee @@ -1,12 +1,24 @@ require 'helpers' -Travis.Controllers = - ApplicationController: Em.Controller.extend() +Travis.Controllers = Em.Namespace.create + AppController: Em.Controller.extend() RepositoriesController: Em.ArrayController.extend() RepositoryController: Em.ObjectController.extend(Travis.Urls.Repository) TabsController: Em.Controller.extend() BuildsController: Em.ArrayController.extend() BuildController: Em.ObjectController.extend(Travis.Urls.Commit) JobController: Em.ObjectController.extend(Travis.Urls.Commit) + SidebarController: Em.Controller.extend() + QueuesController: Em.ArrayController.extend() + + WorkersController: Em.ArrayController.extend + groups: (-> + groups = {} + for worker in @get('content').toArray() + host = worker.get('host') + groups[host] = Em.ArrayProxy.create(content: []) if !(host in groups) + groups[host].pushObject(worker) + $.values(groups) + ).property('content.length') diff --git a/assets/javascripts/app/layout.coffee b/assets/javascripts/app/layout.coffee new file mode 100644 index 00000000..2ac09ba5 --- /dev/null +++ b/assets/javascripts/app/layout.coffee @@ -0,0 +1,11 @@ +Travis.Layout = Em.Namespace.create() +Travis.Layout.instance = (name) -> + if @layout && @layout.name == name + @layout + else + @layout = Travis.Layout[name].create(name: name) + +require 'layout/default' +require 'layout/sidebar' +require 'layout/profile' + diff --git a/assets/javascripts/app/layout/base.coffee b/assets/javascripts/app/layout/base.coffee new file mode 100644 index 00000000..8a5c199e --- /dev/null +++ b/assets/javascripts/app/layout/base.coffee @@ -0,0 +1,20 @@ +Travis.Layout.Base = Em.Object.extend + init: -> + @setup(arguments) + @connect() + + setup: (controllers) -> + $.extend this, Travis.Controllers + $.extend this, Travis.Views + + for name in controllers + key = "#{name}Controller" + name = $.camelize(key) + this[key] = Travis.Controllers[name].create(namespace: this, controllers: this) + + connect: -> + view = Em.View.create(templateName: "layouts/#{@get('name').toLowerCase()}") + view.set('controller', @appController) + view.appendTo(@get('rootElement') || 'body') + + diff --git a/assets/javascripts/app/layouts/default.coffee b/assets/javascripts/app/layout/default.coffee similarity index 59% rename from assets/javascripts/app/layouts/default.coffee rename to assets/javascripts/app/layout/default.coffee index e3e62c5a..07fdd8ae 100644 --- a/assets/javascripts/app/layouts/default.coffee +++ b/assets/javascripts/app/layout/default.coffee @@ -1,12 +1,14 @@ -Travis.Layout.Default = Travis.Layout.extend +require 'layout/base' + +Travis.Layout.Default = Travis.Layout.Base.extend init: -> - @_super() + @_super('app', 'repositories', 'repository', 'tabs', 'build', 'job') @connectLeft(Travis.Repository.find()) + Travis.Layout.Sidebar.create(appController: @get('appController')) viewIndex: (params) -> - repositories = @get('repositories') - onceLoaded repositories, => - repository = repositories.get('firstObject') + onceLoaded @repositories, => + repository = @repositories.get('firstObject') @connectRepository(repository) @connectTabs() @connectBuild(repository.get('lastBuild')) @@ -56,26 +58,26 @@ Travis.Layout.Default = Travis.Layout.extend connectLeft: (repositories) -> - @set('repositories', repositories) - @get('applicationController').connectOutlet(outletName: 'left', name: 'repositories', context: repositories) + @repositories = repositories + @appController.connectOutlet(outletName: 'left', name: 'repositories', context: repositories) connectRepository: (repository) -> - @set('repository', repository) - @get('applicationController').connectOutlet(outletName: 'main', name: 'repository', context: repository) + @repository = repository + @appController.connectOutlet(outletName: 'main', name: 'repository', context: repository) connectTabs: (build, job) -> - @setPath('tabsController.repository', @get('repository')) - @setPath('tabsController.build', build) - @setPath('tabsController.job', job) - @get('repositoryController').connectOutlet(outletName: 'tabs', name: 'tabs') + @tabsController.set('repository', @repository) + @tabsController.set('build', @build) + @tabsController.set('job', @job) + @repositoryController.connectOutlet(outletName: 'tabs', name: 'tabs') connectBuilds: (builds) -> - @get('repositoryController').connectOutlet(outletName: 'tab', name: 'history', context: builds) + @repositoryController.connectOutlet(outletName: 'tab', name: 'builds', context: builds) connectBuild: (build) -> - @get('repositoryController').connectOutlet(outletName: 'tab', name: 'build', context: build) + @repositoryController.connectOutlet(outletName: 'tab', name: 'build', context: build) connectJob: (job) -> - @get('repositoryController').connectOutlet(outletName: 'tab', name: 'job', context: job) + @repositoryController.connectOutlet(outletName: 'tab', name: 'job', context: job) diff --git a/assets/javascripts/app/layout/profile.coffee b/assets/javascripts/app/layout/profile.coffee new file mode 100644 index 00000000..2861ce2e --- /dev/null +++ b/assets/javascripts/app/layout/profile.coffee @@ -0,0 +1,7 @@ +require 'layout/base' + +Travis.Layout.Profile = Travis.Layout.Base.extend + name: 'default' + + init: -> + diff --git a/assets/javascripts/app/layout/sidebar.coffee b/assets/javascripts/app/layout/sidebar.coffee new file mode 100644 index 00000000..c73f25fb --- /dev/null +++ b/assets/javascripts/app/layout/sidebar.coffee @@ -0,0 +1,24 @@ +require 'layout/base' + +Travis.Layout.Sidebar = Travis.Layout.Base.extend + init: -> + @_super('sidebar', 'workers', 'queues') + @appController = @get('appController') + @connectWorkers(Travis.Worker.find()) + @connectQueues(Travis.QUEUES) + + connect: -> + @appController.connectOutlet(outletName: 'right', name: 'sidebar') + + connectWorkers: (workers) -> + @workersController.set('content', workers) + @appController.set('workers', @workersController) + + connectQueues: (queues) -> + queues = for queue in queues + Em.ArrayController.create + content: Travis.Job.queued(queue.name) + name: queue.display + @queuesController.set('content', queues) + @appController.set('queues', @queuesController) + diff --git a/assets/javascripts/app/layouts.coffee b/assets/javascripts/app/layouts.coffee deleted file mode 100644 index ee2f66c6..00000000 --- a/assets/javascripts/app/layouts.coffee +++ /dev/null @@ -1,27 +0,0 @@ -Travis.Layout = Em.Object.extend - init: -> - @setupControllers() - @setupViews() - @connectLeft(Travis.Repository.find()) - - setupControllers: -> - $.extend this, Travis.Controllers - for name, controller of Travis.Controllers - name = name.charAt(0).toLowerCase() + name.substr(1) - this[name] = controller.create(namespace: this, controllers: this) - - setupViews: -> - $.extend this, Travis.Views - view = Travis.Views.ApplicationView.create() - view.set('controller', @applicationController) - view.appendTo(@get('rootElement') || 'body') - -Travis.Layout.instance = (name) -> - if @layout && @layout.name == name - @layout - else - @layout = Travis.Layout[name].create(name: name) - -require 'layouts/default' -require 'layouts/profile' - diff --git a/assets/javascripts/app/layouts/profile.coffee b/assets/javascripts/app/layouts/profile.coffee deleted file mode 100644 index c3a5789a..00000000 --- a/assets/javascripts/app/layouts/profile.coffee +++ /dev/null @@ -1,3 +0,0 @@ -Travis.Layout.Profile = Travis.Layout.extend - init: -> - diff --git a/assets/javascripts/app/models.coffee b/assets/javascripts/app/models.coffee index fe337cd7..ac197442 100644 --- a/assets/javascripts/app/models.coffee +++ b/assets/javascripts/app/models.coffee @@ -1,6 +1,7 @@ +require 'models/artifact' require 'models/build' -require 'models/repository' require 'models/commit' require 'models/job' -require 'models/artifact' +require 'models/repository' +require 'models/worker' diff --git a/assets/javascripts/app/models/job.coffee b/assets/javascripts/app/models/job.coffee index dc5fd3e5..5bfb8e55 100644 --- a/assets/javascripts/app/models/job.coffee +++ b/assets/javascripts/app/models/job.coffee @@ -48,8 +48,9 @@ require 'travis/model' @Travis.Job.reopenClass queued: (queue) -> - @all() + @find() Travis.app.store.filter this, (job) -> job.get('queue') == 'builds.' + queue + findMany: (ids) -> Travis.app.store.findMany this, ids diff --git a/assets/javascripts/app/models/worker.coffee b/assets/javascripts/app/models/worker.coffee index 20108136..703f86ad 100644 --- a/assets/javascripts/app/models/worker.coffee +++ b/assets/javascripts/app/models/worker.coffee @@ -1,13 +1,5 @@ require 'travis/model' -@Travis.WorkerGroup = Ember.ArrayProxy.extend - init: -> - @set('content', []) - - host: (-> - @getPath 'firstObject.host' - ).property() - @Travis.Worker = Travis.Model.extend state: DS.attr('string') name: DS.attr('string') @@ -34,5 +26,5 @@ require 'travis/model' ).property('state') urlJob: (-> - '#!/%@/jobs/%@'.fmt @getPath('payload.repository.slug'), @getPath('payload.build.id') - ).property('payload', 'state') + "#!/#{@getPath('payload.repository.slug')}/jobs/#{@getPath('payload.build.id')}" + ).property('payload') diff --git a/assets/javascripts/app/templates/application.hbs b/assets/javascripts/app/templates/layouts/default.hbs similarity index 77% rename from assets/javascripts/app/templates/application.hbs rename to assets/javascripts/app/templates/layouts/default.hbs index e2178f7f..4348b089 100644 --- a/assets/javascripts/app/templates/application.hbs +++ b/assets/javascripts/app/templates/layouts/default.hbs @@ -10,3 +10,7 @@
{{outlet main}}
+ + diff --git a/assets/javascripts/app/templates/layouts/profile.hbs b/assets/javascripts/app/templates/layouts/profile.hbs new file mode 100644 index 00000000..d83ed5fc --- /dev/null +++ b/assets/javascripts/app/templates/layouts/profile.hbs @@ -0,0 +1,9 @@ + + +
+ {{outlet main}} +
+ diff --git a/assets/javascripts/app/templates/layouts/sidebar.hbs b/assets/javascripts/app/templates/layouts/sidebar.hbs new file mode 100644 index 00000000..f4580319 --- /dev/null +++ b/assets/javascripts/app/templates/layouts/sidebar.hbs @@ -0,0 +1,40 @@ + + + diff --git a/assets/javascripts/app/templates/queues/list.hbs b/assets/javascripts/app/templates/queues/list.hbs new file mode 100644 index 00000000..1281c9ce --- /dev/null +++ b/assets/javascripts/app/templates/queues/list.hbs @@ -0,0 +1,15 @@ +{{#each queue in queues}} +

{{t queue}}: {{queue.name}}

+ +{{/each}} diff --git a/assets/javascripts/app/templates/workers/list.hbs b/assets/javascripts/app/templates/workers/list.hbs new file mode 100644 index 00000000..0f0782a5 --- /dev/null +++ b/assets/javascripts/app/templates/workers/list.hbs @@ -0,0 +1,22 @@ +

{{t workers}}

+ + diff --git a/assets/javascripts/app/views.coffee b/assets/javascripts/app/views.coffee index f4228741..f75bbfb2 100644 --- a/assets/javascripts/app/views.coffee +++ b/assets/javascripts/app/views.coffee @@ -1,9 +1,12 @@ require 'ext/ember/namespace' -@Travis.Views = Em.Namespace.create() +@Travis.Views = Em.Namespace.create + SidebarView: Em.View.extend(templateName: 'layouts/sidebar') + QueuesView: Em.View.extend(templateName: 'queues/list') + WorkersView: Em.View.extend(templateName: 'workers/list') -require 'views/app' require 'views/build' require 'views/job' require 'views/repo' require 'views/tabs' + diff --git a/assets/javascripts/app/views/app.coffee b/assets/javascripts/app/views/app.coffee deleted file mode 100644 index b554cc7f..00000000 --- a/assets/javascripts/app/views/app.coffee +++ /dev/null @@ -1,3 +0,0 @@ -Travis.Views.reopen - ApplicationView: Em.View.extend - templateName: 'application' diff --git a/assets/javascripts/lib/mocks.coffee b/assets/javascripts/lib/mocks.coffee index 239cb7f4..68b12df1 100644 --- a/assets/javascripts/lib/mocks.coffee +++ b/assets/javascripts/lib/mocks.coffee @@ -1,3 +1,5 @@ +require 'ext/jquery' + responseTime = 0 repositories = [ @@ -26,6 +28,8 @@ jobs = [ { id: 3, repository_id: 1, build_id: 2, commit_id: 2, log_id: 3, number: '2.1', config: { rvm: 'rbx' } } { id: 4, repository_id: 2, build_id: 3, commit_id: 3, log_id: 4, number: '3.1', config: { rvm: 'rbx' }, finished_at: '2012-06-20T00:21:20Z', duration: 35, result: 0 } { id: 5, repository_id: 3, build_id: 4, commit_id: 4, log_id: 5, number: '4.1', config: { rvm: 'rbx' } } + { id: 6, repository_id: 1, build_id: 5, commit_id: 5, log_id: 5, number: '5.1', config: { rvm: 'rbx' }, state: 'created', queue: 'builds.common' } + { id: 7, repository_id: 1, build_id: 5, commit_id: 5, log_id: 5, number: '5.2', config: { rvm: 'rbx' }, state: 'created', queue: 'builds.common' } ] artifacts = [ @@ -36,6 +40,11 @@ artifacts = [ { id: 5, body: 'log 4' } ] +workers = [ + { id: 1, name: 'ruby-1', host: 'worker.travis-ci.org', state: 'ready' } + { id: 2, name: 'ruby-2', host: 'worker.travis-ci.org', state: 'ready' } +] + $.mockjax url: '/repositories' responseTime: responseTime @@ -80,4 +89,13 @@ for artifact in artifacts responseText: artifact: artifact +$.mockjax + url: '/workers' + responseTime: responseTime + responseText: { workers: workers } +$.mockjax + url: '/jobs' + responseTime: responseTime + responseText: + jobs: $.select(jobs, (job) -> job.state == 'created') diff --git a/public/index.html b/public/index.html index c656ed18..0b9c7287 100644 --- a/public/index.html +++ b/public/index.html @@ -5,9 +5,9 @@ Travis CI - Distributed Continuous Integration Platform for the Open Source Community - diff --git a/public/javascripts/application.js b/public/javascripts/application.js index 7d55f5b4..b869e017 100644 --- a/public/javascripts/application.js +++ b/public/javascripts/application.js @@ -1 +1 @@ -minispade.register('templates', "(function() {Ember.TEMPLATES['application']=Ember.Handlebars.compile(\"
\\n Travis CI\\n #\\n
\\n\\n
\\n {{outlet left}}\\n
\\n\\n
\\n {{outlet main}}\\n
\\n\");Ember.TEMPLATES['builds/list']=Ember.Handlebars.compile(\"\\n \\n \\n \\n \\n \\n \\n \\n \\n \\n\\n \\n {{#each build in content}}\\n {{#view Travis.Views.BuildsItemView contextBinding=\\\"build\\\"}}\\n \\n \\n \\n \\n \\n \\n \\n {{/view}}\\n {{/each}}\\n \\n
{{t builds.name}}{{t builds.commit}}{{t builds.message}}{{t builds.duration}}{{t builds.finished_at}}
#{{number}}{{formatCommit commit}}{{{formatMessage commit.message short=\\\"true\\\"}}}{{formatDuration duration}}{{formatTime finished_at}}
\\n\\n

\\n \\n

\\n\");Ember.TEMPLATES['builds/show']=Ember.Handlebars.compile(\"{{#unless isLoaded}}\\n Loading ...\\n{{else}}\\n
\\n
\\n
\\n
{{t builds.name}}
\\n
{{number}}
\\n
{{t builds.finished_at}}
\\n
{{formatTime finished_at}}
\\n
{{t builds.duration}}
\\n
{{formatDuration duration}}
\\n
\\n\\n
\\n
{{t builds.commit}}
\\n
{{formatCommit commit}}
\\n {{#if commit.compareUrl}}\\n
{{t builds.compare}}
\\n
{{pathFrom commit.compareUrl}}
\\n {{/if}}\\n {{#if commit.authorName}}\\n
{{t builds.author}}
\\n
{{commit.authorName}}
\\n {{/if}}\\n {{#if commit.committerName}}\\n
{{t builds.committer}}
\\n
{{commit.committerName}}
\\n {{/if}}\\n
\\n\\n
{{t builds.message}}
\\n
{{{formatMessage commit.message}}}
\\n\\n {{#unless isMatrix}}\\n
{{t builds.config}}
\\n
{{formatConfig config}}
\\n {{/unless}}\\n
\\n\\n {{#if isMatrix}}\\n {{view Travis.Views.JobsView jobsBinding=\\\"view.requiredJobs\\\" required=\\\"true\\\"}}\\n {{view Travis.Views.JobsView jobsBinding=\\\"view.allowedFailureJobs\\\"}}\\n {{else}}\\n {{view Travis.Views.LogView contextBinding=\\\"jobs.firstObject\\\"}}\\n {{/if}}\\n
\\n{{/unless}}\\n\");Ember.TEMPLATES['jobs/list']=Ember.Handlebars.compile(\"{{#if view.jobs.length}}\\n \\n \\n \\n \\n {{#each configKeys}}\\n \\n {{/each}}\\n \\n \\n \\n {{#each job in view.jobs}}\\n {{#view Travis.Views.JobsItemView contextBinding=\\\"job\\\"}}\\n \\n \\n \\n \\n {{#each configValues}}\\n \\n {{/each}}\\n \\n {{/view}}\\n {{/each}}\\n \\n
\\n {{#if view.required}}\\n {{t jobs.build_matrix}}\\n {{else}}\\n {{t jobs.allowed_failures}}{{whats_this allow_failure_help}}\\n {{/if}}\\n
{{this}}
#{{number}}{{formatDuration duration}}{{formatTime finished_at}}{{this}}
\\n\\n {{#unless view.required}}\\n
\\n
{{t \\\"jobs.allowed_failures\\\"}}
\\n
\\n

\\n Allowed Failures are items in your build matrix that are allowed to\\n fail without causing the entire build to be shown as failed. This lets you add\\n in experimental and preparatory builds to test against versions or\\n configurations that you are not ready to officially support.\\n

\\n

\\n You can define allowed failures in the build matrix as follows:\\n

\\n
\\n      matrix:\\n        allow_failures:\\n          - rvm: ruby-head\\n        
\\n
\\n
\\n {{/unless}}\\n{{/if}}\\n\");Ember.TEMPLATES['jobs/log']=Ember.Handlebars.compile(\"
{{{formatLog log.body}}}
\\n\\n{{#if sponsor.name}}\\n

\\n {{t builds.messages.sponsored_by}}\\n {{sponsor.name}}\\n

\\n{{/if}}\\n\");Ember.TEMPLATES['jobs/show']=Ember.Handlebars.compile(\"
\\n
\\n
\\n
Job
\\n
{{number}}
\\n
{{t jobs.finished_at}}
\\n
{{formatTime finished_at}}
\\n
{{t jobs.duration}}
\\n
{{formatDuration duration}}
\\n
\\n\\n
\\n
{{t jobs.commit}}
\\n
{{formatCommit commit}}
\\n {{#if commit.compareUrl}}\\n
{{t jobs.compare}}
\\n
{{pathFrom commit.compareUrl}}
\\n {{/if}}\\n {{#if commit.authorName}}\\n
{{t jobs.author}}
\\n
{{commit.authorName}}
\\n {{/if}}\\n {{#if commit.committerName}}\\n
{{t jobs.committer}}
\\n
{{commit.committerName}}
\\n {{/if}}\\n
\\n\\n
{{t jobs.message}}
\\n
{{formatMessage commit.message}}
\\n
{{t jobs.config}}
\\n
{{formatConfig config}}
\\n
\\n\\n {{view Travis.Views.LogView}}\\n
\\n\\n\");Ember.TEMPLATES['repositories/list']=Ember.Handlebars.compile(\"{{#unless content.lastObject.isLoaded}}\\n Loading ...\\n{{else}}\\n