diff --git a/assets/scripts/app/controllers/current_user.coffee b/assets/scripts/app/controllers/current_user.coffee index affccf9c..4b8ca9e4 100644 --- a/assets/scripts/app/controllers/current_user.coffee +++ b/assets/scripts/app/controllers/current_user.coffee @@ -1,19 +1,11 @@ -delegate = (name, options) -> - options ||= options - -> - target = @get(options.to) - target[name].apply(target, arguments) - Travis.CurrentUserController = Em.ObjectController.extend sync: -> - @get('content').sync() + @get('model').sync() - content: (-> - @get('auth.currentUser') - ).property('auth.currentUser') + model: Ember.computed.alias('auth.currentUser') syncingDidChange: (-> - if (user = @get('content')) && user.get('isSyncing') && !user.get('syncedAt') + if (user = @get('model')) && user.get('isSyncing') && !user.get('syncedAt') Ember.run.scheduleOnce 'routerTransitions', this, -> @container.lookup('router:main').send('renderFirstSync') - ).observes('isSyncing', 'content') + ).observes('isSyncing', 'auth.currentUser') diff --git a/assets/scripts/app/controllers/flash.coffee b/assets/scripts/app/controllers/flash.coffee index 721b629f..9b857656 100644 --- a/assets/scripts/app/controllers/flash.coffee +++ b/assets/scripts/app/controllers/flash.coffee @@ -6,13 +6,13 @@ Travis.FlashController = Ember.ArrayController.extend @_super.apply this, arguments @set('flashes', Travis.LimitedArray.create(limit: 2, content: [])) - content: (-> + model: (-> broadcasts = @get('unseenBroadcasts') flashes = @get('flashes') - content = [] - content = content.concat(broadcasts.toArray()) if broadcasts - content = content.concat(flashes.toArray().reverse()) if flashes - content.uniq() + model = [] + model = model.concat(broadcasts.toArray()) if broadcasts + model = model.concat(flashes.toArray().reverse()) if flashes + model.uniq() ).property('unseenBroadcasts.length', 'flashes.length') unseenBroadcasts: (-> diff --git a/assets/scripts/app/controllers/repo.coffee b/assets/scripts/app/controllers/repo.coffee index 4144b681..18468063 100644 --- a/assets/scripts/app/controllers/repo.coffee +++ b/assets/scripts/app/controllers/repo.coffee @@ -10,7 +10,8 @@ Travis.RepoController = Travis.Controller.extend init: -> @_super.apply this, arguments - Visibility.every Travis.INTERVALS.updateTimes, @updateTimes.bind(this) + if !Ember.testing + Visibility.every Travis.INTERVALS.updateTimes, @updateTimes.bind(this) updateTimes: -> Ember.run this, -> diff --git a/assets/scripts/app/helpers/handlebars.coffee b/assets/scripts/app/helpers/handlebars.coffee index bc555d96..c10bf131 100644 --- a/assets/scripts/app/helpers/handlebars.coffee +++ b/assets/scripts/app/helpers/handlebars.coffee @@ -1,5 +1,3 @@ -require 'ext/ember/bound_helper' - safe = (string) -> new Handlebars.SafeString(string) @@ -125,7 +123,7 @@ Ember.Handlebars.helper('travis-errors', (name, options) -> Handlebars.registerHelper 'tipsy', (text, tip) -> safe '' + text + '' -Ember.registerBoundHelper 'capitalize', (value, options) -> +Ember.Handlebars.registerBoundHelper 'capitalize', (value, options) -> if value? safe $.capitalize(value) else @@ -140,10 +138,10 @@ Ember.Handlebars.helper('githubCommitLink', (slug, commitSha) -> safe '' + sha + '' ) -Ember.registerBoundHelper 'formatTime', (value, options) -> +Ember.Handlebars.registerBoundHelper 'formatTime', (value, options) -> safe Travis.Helpers.timeAgoInWords(value) || '-' -Ember.registerBoundHelper 'formatDuration', (duration, options) -> +Ember.Handlebars.registerBoundHelper 'formatDuration', (duration, options) -> safe Travis.Helpers.timeInWords(duration) Ember.Handlebars.helper('formatCommit', (commit) -> @@ -153,16 +151,16 @@ Ember.Handlebars.helper('formatCommit', (commit) -> Ember.Handlebars.helper 'formatSha', (sha) -> safe Travis.Helpers.formatSha(sha) -Ember.registerBoundHelper 'pathFrom', (url, options) -> +Ember.Handlebars.registerBoundHelper 'pathFrom', (url, options) -> safe Travis.Helpers.pathFrom(url) Ember.Handlebars.helper 'formatMessage', (message, options) -> safe Travis.Helpers.formatMessage(message, options.hash) -Ember.registerBoundHelper 'formatConfig', (config, options) -> +Ember.Handlebars.registerBoundHelper 'formatConfig', (config, options) -> safe Travis.Helpers.formatConfig(config) -Ember.registerBoundHelper 'shortCompareShas', (url, options) -> +Ember.Handlebars.registerBoundHelper 'shortCompareShas', (url, options) -> path = Travis.Helpers.pathFrom(url) if path.indexOf('...') >= 0 shas = path.split('...') @@ -170,7 +168,7 @@ Ember.registerBoundHelper 'shortCompareShas', (url, options) -> else path -Ember.registerBoundHelper 'formatLog', (log, options) -> +Ember.Handlebars.registerBoundHelper 'formatLog', (log, options) -> parentView = @get 'parentView' repo = parentView.get(options.repo) item = parentView.get(options.item) diff --git a/assets/scripts/app/templates/builds/show.hbs b/assets/scripts/app/templates/builds/show.hbs index 300581cb..88907014 100644 --- a/assets/scripts/app/templates/builds/show.hbs +++ b/assets/scripts/app/templates/builds/show.hbs @@ -64,13 +64,13 @@ {{#unless build.isMatrix}} - {{view Travis.AnnotationsView annotationsBinding="build.jobs.firstObject.annotations"}} + {{view 'annotations' annotations=build.jobs.firstObject.annotations}} {{/unless}} {{#if build.isMatrix}} - {{view Travis.JobsView jobsBinding="build.requiredJobs" required="true"}} - {{view Travis.JobsView jobsBinding="build.allowedFailureJobs"}} + {{view 'jobs' jobs=build.requiredJobs required="true"}} + {{view 'jobs' jobs=build.allowedFailureJobs}} {{else}} - {{view Travis.LogView jobBinding="build.jobs.firstObject"}} + {{view 'log' job=build.jobs.firstObject}} {{/if}} {{/if}} diff --git a/assets/scripts/app/templates/jobs/list.hbs b/assets/scripts/app/templates/jobs/list.hbs index 046d2981..d576baee 100644 --- a/assets/scripts/app/templates/jobs/list.hbs +++ b/assets/scripts/app/templates/jobs/list.hbs @@ -1,15 +1,14 @@ {{#if view.jobs.length}} +
{{#if job.id}}
diff --git a/assets/scripts/app/templates/jobs/log.hbs b/assets/scripts/app/templates/jobs/log.hbs
index 1c1ffb84..0c74f7b8 100644
--- a/assets/scripts/app/templates/jobs/log.hbs
+++ b/assets/scripts/app/templates/jobs/log.hbs
@@ -1,5 +1,5 @@
{{#if view.log.isLoaded}}
- {{view Travis.PreView jobBinding="view.job" logBinding="view.log"}}
+ {{view 'pre' job=view.job log=view.log}}
{{else}}
Loading
diff --git a/assets/scripts/app/templates/jobs/show.hbs b/assets/scripts/app/templates/jobs/show.hbs
index 9d54e7d6..a0139070 100644
--- a/assets/scripts/app/templates/jobs/show.hbs
+++ b/assets/scripts/app/templates/jobs/show.hbs
@@ -62,9 +62,9 @@
- {{view Travis.AnnotationsView annotationsBinding="view.annotations"}}
+ {{view 'annotations' annotations=view.annotations}}
- {{view Travis.LogView jobBinding="job"}}
+ {{view 'log' job=job}}
{{else}}
diff --git a/assets/scripts/app/templates/repos/list.hbs b/assets/scripts/app/templates/repos/list.hbs
index 17ea50f4..961db9b6 100644
--- a/assets/scripts/app/templates/repos/list.hbs
+++ b/assets/scripts/app/templates/repos/list.hbs
@@ -1,12 +1,12 @@
- {{view Ember.TextField valueBinding="controller.search" placeholder="Search all repositories"}}
+ {{input value=controller.search placeholder="Search all repositories"}}
-{{view Travis.ReposListTabsView}}
+{{view 'repos-list-tabs'}}
{{#if isLoaded}}
- {{#collection Travis.ReposListView contentBinding="this"}}
+ {{#collection 'repos-list' content=this}}
{{#with view.repo}}
diff --git a/assets/scripts/app/templates/repos/show.hbs b/assets/scripts/app/templates/repos/show.hbs
index ec99feb8..b9de7359 100644
--- a/assets/scripts/app/templates/repos/show.hbs
+++ b/assets/scripts/app/templates/repos/show.hbs
@@ -7,20 +7,19 @@
{{#link-to "repo" this}}{{slug}}{{/link-to}}- - {{view Travis.RepoShowToolsView}} + {{view 'repo-show-tools'}}{{description}} - {{view Travis.RepoShowTabsView}} - {{view Travis.RepoActionsView}} + {{view 'repo-show-tabs'}} + {{view 'repo-actions'}} {{/with}}
diff --git a/assets/scripts/app/templates/repos/show/tabs.hbs b/assets/scripts/app/templates/repos/show/tabs.hbs
index 793bd65c..8b8174b8 100644
--- a/assets/scripts/app/templates/repos/show/tabs.hbs
+++ b/assets/scripts/app/templates/repos/show/tabs.hbs
@@ -2,7 +2,7 @@
+ ```
+
+ `unbound` can also be used in conjunction with a bound helper to
+ render it in its unbound form:
+
+ ```handlebars
+ {{#if repo.slug}} - {{#link-to "repo" repo currentWhen="repo.index"}} + {{#link-to "repo" repo current-when="repo.index"}} Current {{/link-to}} {{/if}} @@ -75,7 +75,7 @@ {{/if}}{{#if repo.slug}} diff --git a/assets/scripts/app/templates/repos/show/tools.hbs b/assets/scripts/app/templates/repos/show/tools.hbs index 83cedee0..9c93a4dc 100644 --- a/assets/scripts/app/templates/repos/show/tools.hbs +++ b/assets/scripts/app/templates/repos/show/tools.hbs @@ -19,7 +19,7 @@Integrating Code Climate's test coverage reporting with your test suite on Travis CI allows to track changes in coverage over time. If you haven't tried it out already, sign + {{bind-attr href=config.code_climate_url}}" target="_blank">sign up today to improve your code's quality. New customers get 20% off for the first three months! diff --git a/assets/scripts/app/templates/settings.hbs b/assets/scripts/app/templates/settings.hbs index 9c9458e4..ec0f6259 100644 --- a/assets/scripts/app/templates/settings.hbs +++ b/assets/scripts/app/templates/settings.hbs @@ -1,7 +1,7 @@ diff --git a/assets/scripts/app/views/application.coffee b/assets/scripts/app/views/application.coffee index 45d414e0..aa07c99e 100644 --- a/assets/scripts/app/views/application.coffee +++ b/assets/scripts/app/views/application.coffee @@ -1,6 +1,6 @@ Travis.reopen ApplicationView: Travis.View.extend - templateName: (-> @get 'controller.templateName' ).property('controller.templateName') + templateName: Ember.computed.alias('controller.templateName') classNames: ['application'] templateNameDidChange: (-> diff --git a/assets/scripts/app/views/job.coffee b/assets/scripts/app/views/job.coffee index 44cb5862..40d89b14 100644 --- a/assets/scripts/app/views/job.coffee +++ b/assets/scripts/app/views/job.coffee @@ -3,6 +3,13 @@ Travis.reopen templateName: 'jobs/list' buildBinding: 'controller.build' + jobTableId: Ember.computed(-> + if @get('required') + 'jobs' + else + 'allowed_failure_jobs' + ) + JobsItemView: Travis.View.extend tagName: 'tr' classNameBindings: ['color'] diff --git a/assets/scripts/app/views/log.coffee b/assets/scripts/app/views/log.coffee index 2f92104c..6a6ed987 100644 --- a/assets/scripts/app/views/log.coffee +++ b/assets/scripts/app/views/log.coffee @@ -37,12 +37,12 @@ Travis.reopen @lineSelector?.willDestroy() versionDidChange: (-> - @rerender() if @get('state') == 'inDOM' + @rerender() if @get('_state') == 'inDOM' ).observes('log.version') logDidChange: (-> console.log 'log view: log did change: rerender' if Log.DEBUG - @rerender() if @get('state') == 'inDOM' + @rerender() if @get('_state') == 'inDOM' ).observes('log') createEngine: -> diff --git a/assets/scripts/app/views/repo/show.coffee b/assets/scripts/app/views/repo/show.coffee index d19abc12..f2fda758 100644 --- a/assets/scripts/app/views/repo/show.coffee +++ b/assets/scripts/app/views/repo/show.coffee @@ -21,9 +21,9 @@ Travis.reopen # TODO: look into fixing it in more general way Ember.run.schedule('afterRender', this, -> pane = Ember.get('_outlets.pane') - if @get('controller.repo.isLoaded') && @state == 'inDOM' && + if @get('controller.repo.isLoaded') && @_state == 'inDOM' && @get('controller.repo.lastBuild') && - @get('controller.tab') == 'current' && (!pane || pane.state == 'destroyed') + @get('controller.tab') == 'current' && (!pane || pane._state == 'destroyed') view = @get('controller.container').lookup('view:build') view.set('controller', @get('controller.container').lookup('controller:build')) Ember.run.next => diff --git a/assets/scripts/lib/ext/ember/bound_helper.js b/assets/scripts/lib/ext/ember/bound_helper.js deleted file mode 100644 index 6b6fe699..00000000 --- a/assets/scripts/lib/ext/ember/bound_helper.js +++ /dev/null @@ -1,68 +0,0 @@ -// https://gist.github.com/2018185 -// For reference: https://github.com/wagenet/ember.js/blob/ac66dcb8a1cbe91d736074441f853e0da474ee6e/packages/ember-handlebars/lib/views/bound_property_view.js -var BoundHelperView = Ember.View.extend(Ember._Metamorph, { - - context: null, - options: null, - property: null, - // paths of the property that are also observed - propertyPaths: [], - - value: Ember.K, - - valueForRender: function() { - var value = this.value(Ember.get(this.context, this.property), this.options); - if (this.options.escaped) { value = Handlebars.Utils.escapeExpression(value); } - return value; - }, - - render: function(buffer) { - buffer.push(this.valueForRender()); - }, - - valueDidChange: function() { - if (this.morph.isRemoved()) { return; } - this.morph.html(this.valueForRender()); - }, - - didInsertElement: function() { - this.valueDidChange(); - }, - - init: function() { - this._super(); - Ember.addObserver(this.context, this.property, this, 'valueDidChange'); - this.get('propertyPaths').forEach(function(propName) { - Ember.addObserver(this.context, this.property + '.' + propName, this, 'valueDidChange'); - }, this); - }, - - destroy: function() { - Ember.removeObserver(this.context, this.property, this, 'valueDidChange'); - this.get('propertyPaths').forEach(function(propName) { - this.context.removeObserver(this.property + '.' + propName, this, 'valueDidChange'); - }, this); - this._super(); - } - -}); - -Ember.registerBoundHelper = function(name, func) { - var propertyPaths = Array.prototype.slice.call(arguments, 2); - Ember.Handlebars.registerHelper(name, function(property, options) { - var data = options.data, - view = data.view, - ctx = this; - - var bindView = view.createChildView(BoundHelperView, { - property: property, - propertyPaths: propertyPaths, - context: ctx, - options: options.hash, - value: func - }); - - view.appendChild(bindView); - }); -}; - diff --git a/assets/scripts/lib/travis/log_chunks.coffee b/assets/scripts/lib/travis/log_chunks.coffee index 5ed7596a..9570ad4a 100644 --- a/assets/scripts/lib/travis/log_chunks.coffee +++ b/assets/scripts/lib/travis/log_chunks.coffee @@ -76,6 +76,7 @@ Travis.LogChunks = Em.ArrayProxy.extend if part.final @notifyPropertyChange('final') - Ember.run.once this, -> - @tryFinalizing() - @resetTimeout() + Ember.run this, -> + Ember.run.once this, -> + @tryFinalizing() + @resetTimeout() diff --git a/assets/scripts/spec/integration/build_spec.coffee b/assets/scripts/spec/integration/build_spec.coffee index 9716d7f4..8e9b4f88 100644 --- a/assets/scripts/spec/integration/build_spec.coffee +++ b/assets/scripts/spec/integration/build_spec.coffee @@ -5,53 +5,56 @@ module "Build page", Ember.run -> Travis.reset() test "displaying information on build page", -> - visit('/travis-ci/travis-core/builds').then -> - visit('/travis-ci/travis-core/builds/1').then -> - listsRepos [ - { slug: 'travis-ci/travis-hub', build: { number: 4, url: '/travis-ci/travis-hub/builds/4', duration: '1 min', finishedAt: '-' } } - { slug: 'travis-ci/travis-core', build: { number: 1, url: '/travis-ci/travis-core/builds/1', duration: '30 sec', finishedAt: '3 minutes ago' } } - { slug: 'travis-ci/travis-assets', build: { number: 3, url: '/travis-ci/travis-assets/builds/3', duration: '30 sec', finishedAt: 'a day ago' } } + visit('/travis-ci/travis-core/builds') + visit('/travis-ci/travis-core/builds/1') + andThen(-> + listsRepos [ + { slug: 'travis-ci/travis-hub', build: { number: 4, url: '/travis-ci/travis-hub/builds/4', duration: '1 min', finishedAt: '-' } } + { slug: 'travis-ci/travis-core', build: { number: 1, url: '/travis-ci/travis-core/builds/1', duration: '30 sec', finishedAt: '3 minutes ago' } } + { slug: 'travis-ci/travis-assets', build: { number: 3, url: '/travis-ci/travis-assets/builds/3', duration: '30 sec', finishedAt: 'a day ago' } } + ] + + displaysRepository + href: '/travis-ci/travis-core' + + displaysSummary + type: 'build' + id: 1 + repo: 'travis-ci/travis-core' + commit: '1234567' + branch: 'master' + compare: '0123456..1234567' + finishedAt: '3 minutes ago' + duration: '30 sec' + message: 'commit message 1' + + displaysSummaryGravatars + authorEmail: 'author@email.com' + committerEmail: 'committer@email.com' + + + displaysTabs + current: { href: '/travis-ci/travis-core' } + builds: { href: '/travis-ci/travis-core/builds' } + build: { href: '/travis-ci/travis-core/builds/1', active: true } + job: { hidden: true } + + listsJobs + table: '#jobs' + headers: ['Job', 'Duration', 'Finished', 'Ruby'] + jobs: [ + { color: 'green', id: 1, number: '1.1', repo: 'travis-ci/travis-core', finishedAt: '3 minutes ago', duration: '30 sec', rvm: 'rbx' } + { color: 'red', id: 2, number: '1.2', repo: 'travis-ci/travis-core', finishedAt: '2 minutes ago', duration: '40 sec', rvm: '1.9.3' } ] - displaysRepository - href: '/travis-ci/travis-core' + listsJobs + table: '#allowed_failure_jobs' + headers: ['Job', 'Duration', 'Finished', 'Ruby'] + jobs: [ + { color: '', id: 3, number: '1.3', repo: 'travis-ci/travis-core', finishedAt: '-', duration: '-', rvm: 'jruby' } + ] + ) - displaysSummary - type: 'build' - id: 1 - repo: 'travis-ci/travis-core' - commit: '1234567' - branch: 'master' - compare: '0123456..1234567' - finishedAt: '3 minutes ago' - duration: '30 sec' - message: 'commit message 1' - - displaysSummaryGravatars - authorEmail: 'author@email.com' - committerEmail: 'committer@email.com' - - - displaysTabs - current: { href: '/travis-ci/travis-core' } - builds: { href: '/travis-ci/travis-core/builds' } - build: { href: '/travis-ci/travis-core/builds/1', active: true } - job: { hidden: true } - - listsJobs - table: '#jobs' - headers: ['Job', 'Duration', 'Finished', 'Ruby'] - jobs: [ - { color: 'green', id: 1, number: '1.1', repo: 'travis-ci/travis-core', finishedAt: '3 minutes ago', duration: '30 sec', rvm: 'rbx' } - { color: 'red', id: 2, number: '1.2', repo: 'travis-ci/travis-core', finishedAt: '2 minutes ago', duration: '40 sec', rvm: '1.9.3' } - ] - - listsJobs - table: '#allowed_failure_jobs' - headers: ['Job', 'Duration', 'Finished', 'Ruby'] - jobs: [ - { color: '', id: 3, number: '1.3', repo: 'travis-ci/travis-core', finishedAt: '-', duration: '-', rvm: 'jruby' } - ] test "updating current build", -> visit('/travis-ci/travis-core').then -> diff --git a/assets/scripts/spec/integration/routes_spec.coffee b/assets/scripts/spec/integration/routes_spec.coffee index 1cfe3fe8..1992cfab 100644 --- a/assets/scripts/spec/integration/routes_spec.coffee +++ b/assets/scripts/spec/integration/routes_spec.coffee @@ -1,6 +1,4 @@ module "Router", - setup: -> - Ember.run -> Travis.advanceReadiness() teardown: -> Ember.run -> Travis.reset() diff --git a/assets/scripts/spec/support/expectations.coffee b/assets/scripts/spec/support/expectations.coffee index 8d9c5531..d0a74bf7 100644 --- a/assets/scripts/spec/support/expectations.coffee +++ b/assets/scripts/spec/support/expectations.coffee @@ -105,7 +105,7 @@ element = $("td.finished_at", row) equal(element.text().trim(), job.finishedAt) - element = $("td:nth-child(6)", row) + element = $("td:nth-child(4)", row) equal(element.text().trim(), job.rvm) @listsQueuedJobs = (jobs) -> diff --git a/assets/scripts/spec/unit/log_chunks_spec.coffee b/assets/scripts/spec/unit/log_chunks_spec.coffee index d08bedd0..724ace17 100644 --- a/assets/scripts/spec/unit/log_chunks_spec.coffee +++ b/assets/scripts/spec/unit/log_chunks_spec.coffee @@ -40,7 +40,7 @@ test "it triggers downloading missing parts if there is a missing part, even tho , 40 test "it triggers downloading next parts if there is no final part", -> - expect(2) + expect(4) stop() callback = (missingNumbers, after) -> @@ -58,7 +58,7 @@ test "it triggers downloading next parts if there is no final part", -> , 35 test "it triggers downloading all available parts if there is no parts yet", -> - expect(1) + expect(2) stop() callback = (missingNumbers, after) -> diff --git a/assets/scripts/travis.coffee b/assets/scripts/travis.coffee index f9d48ab6..90e69f30 100644 --- a/assets/scripts/travis.coffee +++ b/assets/scripts/travis.coffee @@ -158,6 +158,15 @@ Travis.initializer s = document.getElementsByTagName('script')[0] s.parentNode.insertBefore(ga, s) +Travis.initializer + name: 'inject-config' + + initialize: (container, application) -> + application.register 'config:main', Travis.config, { instantiate: false } + + application.inject('controller', 'config', 'config:main') + + Travis.Router.reopen didTransition: -> @_super.apply @, arguments diff --git a/assets/scripts/vendor/ember.js b/assets/scripts/vendor/ember.js index 647ee015..54e8739e 100644 --- a/assets/scripts/vendor/ember.js +++ b/assets/scripts/vendor/ember.js @@ -5,25 +5,24 @@ * Portions Copyright 2008-2011 Apple Inc. All rights reserved. * @license Licensed under MIT license * See https://raw.github.com/emberjs/ember.js/master/LICENSE - * @version 1.6.1 + * @version 1.8.1 */ - (function() { -var define, requireModule, require, requirejs, Ember; +var enifed, requireModule, eriuqer, requirejs, Ember; (function() { Ember = this.Ember = this.Ember || {}; - if (typeof Ember === 'undefined') { Ember = {} }; + if (typeof Ember === 'undefined') { Ember = {}; }; if (typeof Ember.__loader === 'undefined') { var registry = {}, seen = {}; - define = function(name, deps, callback) { + enifed = function(name, deps, callback) { registry[name] = { deps: deps, callback: callback }; }; - requirejs = require = requireModule = function(name) { + requirejs = eriuqer = requireModule = function(name) { if (seen.hasOwnProperty(name)) { return seen[name]; } seen[name] = {}; @@ -31,11 +30,11 @@ var define, requireModule, require, requirejs, Ember; throw new Error("Could not find module " + name); } - var mod = registry[name], - deps = mod.deps, - callback = mod.callback, - reified = [], - exports; + var mod = registry[name]; + var deps = mod.deps; + var callback = mod.callback; + var reified = []; + var exports; for (var i=0, l=deps.length; i
{{unbound helperName somePropertyThatDoesntChange}}
+ ```
+
+ @method unbound
+ @for Ember.Handlebars.helpers
+ @param {String} property
+ @return {String} HTML string
+ */
+ __exports__["default"] = function unboundHelper(property, fn) {
+ var options = arguments[arguments.length - 1];
+ var container = options.data.view.container;
+ var helper, context, out, ctx;
+
+ ctx = this;
+ if (arguments.length > 2) {
+ // Unbound helper call.
+ options.data.isUnbound = true;
+ helper = resolveHelper(container, property) || helpers.helperMissing;
+ out = helper.apply(ctx, slice.call(arguments, 1));
+ delete options.data.isUnbound;
+ return out;
+ }
+
+ context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : ctx;
+ return handlebarsGet(context, property, fn);
+ }
+ });
+enifed("ember-handlebars/helpers/view",
+ ["ember-metal/core","ember-runtime/system/object","ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-views/system/jquery","ember-views/views/view","ember-metal/binding","ember-metal/keys","ember-handlebars/ext","ember-runtime/system/string","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) {
+ "use strict";
+ /*globals Handlebars */
+
+ /**
+ @module ember
+ @submodule ember-handlebars
+ */
+
+ var Ember = __dependency1__["default"];
+ // Ember.warn, Ember.assert
+ // var emberWarn = Ember.warn, emberAssert = Ember.assert;
+
+ var EmberObject = __dependency2__["default"];
+ var get = __dependency3__.get;
+ var set = __dependency4__.set;
+ var IS_BINDING = __dependency5__.IS_BINDING;
+ var jQuery = __dependency6__["default"];
+ var View = __dependency7__["default"];
+ var isGlobalPath = __dependency8__.isGlobalPath;
+ var keys = __dependency9__["default"];
+ var normalizePath = __dependency10__.normalizePath;
+ var handlebarsGet = __dependency10__.handlebarsGet;
+ var handlebarsGetView = __dependency10__.handlebarsGetView;
+ var EmberString = __dependency11__["default"];
+
+
+ var LOWERCASE_A_Z = /^[a-z]/;
+ var VIEW_PREFIX = /^view\./;
+
+ function makeBindings(thisContext, options) {
+ var hash = options.hash;
+ var hashType = options.hashTypes;
+
+ for (var prop in hash) {
+ if (hashType[prop] === 'ID') {
+
+ var value = hash[prop];
+
+ if (IS_BINDING.test(prop)) {
+ Ember.warn("You're attempting to render a view by passing " + prop + "=" + value + " to a view helper, but this syntax is ambiguous. You should either surround " + value + " in quotes or remove `Binding` from " + prop + ".");
+ } else {
+ hash[prop + 'Binding'] = value;
+ hashType[prop + 'Binding'] = 'STRING';
+ delete hash[prop];
+ delete hashType[prop];
+ }
+ }
+ }
+
+ if (hash.hasOwnProperty('idBinding')) {
+ // id can't be bound, so just perform one-time lookup.
+ hash.id = handlebarsGet(thisContext, hash.idBinding, options);
+ hashType.id = 'STRING';
+ delete hash.idBinding;
+ delete hashType.idBinding;
+ }
+ }
+
+ var ViewHelper = EmberObject.create({
+ propertiesFromHTMLOptions: function(options) {
+ var hash = options.hash;
+ var data = options.data;
+ var classes = hash['class'];
+
+ var extensions = {
+ helperName: options.helperName || ''
+ };
+
+ if (hash.id) {
+ extensions.elementId = hash.id;
+ }
+
+ if (hash.tag) {
+ extensions.tagName = hash.tag;
+ }
+
+ if (classes) {
+ classes = classes.split(' ');
+ extensions.classNames = classes;
+ }
+
+ if (hash.classBinding) {
+ extensions.classNameBindings = hash.classBinding.split(' ');
+ }
+
+ if (hash.classNameBindings) {
+ if (extensions.classNameBindings === undefined) {
+ extensions.classNameBindings = [];
+ }
+ extensions.classNameBindings = extensions.classNameBindings.concat(hash.classNameBindings.split(' '));
+ }
+
+ if (hash.attributeBindings) {
+ Ember.assert("Setting 'attributeBindings' via Handlebars is not allowed. Please subclass Ember.View and set it there instead.");
+ extensions.attributeBindings = null;
+ }
+
+ // Set the proper context for all bindings passed to the helper. This applies to regular attribute bindings
+ // as well as class name bindings. If the bindings are local, make them relative to the current context
+ // instead of the view.
+ var path;
+ var hashKeys = keys(hash);
+
+ for (var i = 0, l = hashKeys.length; i < l; i++) {
+ var prop = hashKeys[i];
+ var isBinding = IS_BINDING.test(prop);
+
+ if (prop !== 'classNameBindings') {
+ extensions[prop] = hash[prop];
+ }
+
+ // Test if the property ends in "Binding"
+ if (isBinding && typeof extensions[prop] === 'string') {
+ path = this.contextualizeBindingPath(hash[prop], data);
+ if (path) {
+ extensions[prop] = path;
+ }
+ }
+ }
+
+ if (extensions.classNameBindings) {
+ // Evaluate the context of class name bindings:
+ for (var j = 0, k = extensions.classNameBindings.length; j < k; j++) {
+ var full = extensions.classNameBindings[j];
+
+ if (typeof full === 'string') {
+ // Contextualize the path of classNameBinding so this:
+ //
+ // classNameBinding="isGreen:green"
+ //
+ // is converted to this:
+ //
+ // classNameBinding="_parentView.context.isGreen:green"
+ var parsedPath = View._parsePropertyPath(full);
+ if (parsedPath.path !== '') {
+ path = this.contextualizeBindingPath(parsedPath.path, data);
+ if (path) {
+ extensions.classNameBindings[j] = path + parsedPath.classNames;
+ }
+ }
+ }
+ }
+ }
+
+ return extensions;
+ },
+
+ // Transform bindings from the current context to a context that can be evaluated within the view.
+ // Returns null if the path shouldn't be changed.
+ //
+ // TODO: consider the addition of a prefix that would allow this method to return `path`.
+ contextualizeBindingPath: function(path, data) {
+ var normalized = normalizePath(null, path, data);
+ if (normalized.isKeyword) {
+ return 'templateData.keywords.' + path;
+ } else if (isGlobalPath(path)) {
+ return null;
+ } else if (path === 'this' || path === '') {
+ return '_parentView.context';
+ } else {
+ return '_parentView.context.' + path;
+ }
+ },
+
+ helper: function(thisContext, path, options) {
+ var data = options.data;
+ var fn = options.fn;
+ var newView;
+ var newViewProto;
+
+ makeBindings(thisContext, options);
+
+ var container = this.container || (data && data.view && data.view.container);
+ newView = handlebarsGetView(thisContext, path, container, options);
+
+ if (View.detectInstance(newView)) {
+ newViewProto = newView;
+ } else {
+ newViewProto = newView.proto();
+ }
+
+ var viewOptions = this.propertiesFromHTMLOptions(options, thisContext);
+ var currentView = data.view;
+ viewOptions.templateData = data;
+
+ if (fn) {
+ Ember.assert("You cannot provide a template block if you also specified a templateName", !get(viewOptions, 'templateName') && !get(newViewProto, 'templateName'));
+ viewOptions.template = fn;
+ }
+
+ // We only want to override the `_context` computed property if there is
+ // no specified controller. See View#_context for more information.
+ if (!newViewProto.controller && !newViewProto.controllerBinding && !viewOptions.controller && !viewOptions.controllerBinding) {
+ viewOptions._context = thisContext;
+ }
+
+ currentView.appendChild(newView, viewOptions);
+ },
+
+ instanceHelper: function(thisContext, newView, options) {
+ var data = options.data;
+ var fn = options.fn;
+
+ makeBindings(thisContext, options);
+
+ Ember.assert(
+ 'Only a instance of a view may be passed to the ViewHelper.instanceHelper',
+ View.detectInstance(newView)
+ );
+
+ var viewOptions = this.propertiesFromHTMLOptions(options, thisContext);
+ var currentView = data.view;
+ viewOptions.templateData = data;
+
+ if (fn) {
+ Ember.assert("You cannot provide a template block if you also specified a templateName", !get(viewOptions, 'templateName') && !get(newView, 'templateName'));
+ viewOptions.template = fn;
+ }
+
+ // We only want to override the `_context` computed property if there is
+ // no specified controller. See View#_context for more information.
+ if (!newView.controller && !newView.controllerBinding && !viewOptions.controller && !viewOptions.controllerBinding) {
+ viewOptions._context = thisContext;
+ }
+
+ currentView.appendChild(newView, viewOptions);
+ }
+ });
+ __exports__.ViewHelper = ViewHelper;
+ /**
+ `{{view}}` inserts a new instance of an `Ember.View` into a template passing its
+ options to the `Ember.View`'s `create` method and using the supplied block as
+ the view's own template.
+
+ An empty `` and the following template:
+
+ ```handlebars
+ A span:
+ {{#view tagName="span"}}
+ hello.
+ {{/view}}
+ ```
+
+ Will result in HTML structure:
+
+ ```html
+
+
+
+
+ A span:
+
+ Hello.
+
+
+
+ ```
+
+ ### `parentView` setting
+
+ The `parentView` property of the new `Ember.View` instance created through
+ `{{view}}` will be set to the `Ember.View` instance of the template where
+ `{{view}}` was called.
+
+ ```javascript
+ aView = Ember.View.create({
+ template: Ember.Handlebars.compile("{{#view}} my parent: {{parentView.elementId}} {{/view}}")
+ });
+
+ aView.appendTo('body');
+ ```
+
+ Will result in HTML structure:
+
+ ```html
+
+
+ ```
+
+ ### Setting CSS id and class attributes
+
+ The HTML `id` attribute can be set on the `{{view}}`'s resulting element with
+ the `id` option. This option will _not_ be passed to `Ember.View.create`.
+
+ ```handlebars
+ {{#view tagName="span" id="a-custom-id"}}
+ hello.
+ {{/view}}
+ ```
+
+ Results in the following HTML structure:
+
+ ```html
+
+ my parent: ember1
+
+
+
+ hello.
+
+
+ ```
+
+ The HTML `class` attribute can be set on the `{{view}}`'s resulting element
+ with the `class` or `classNameBindings` options. The `class` option will
+ directly set the CSS `class` attribute and will not be passed to
+ `Ember.View.create`. `classNameBindings` will be passed to `create` and use
+ `Ember.View`'s class name binding functionality:
+
+ ```handlebars
+ {{#view tagName="span" class="a-custom-class"}}
+ hello.
+ {{/view}}
+ ```
+
+ Results in the following HTML structure:
+
+ ```html
+
+
+ hello.
+
+
+ ```
+
+ ### Supplying a different view class
+
+ `{{view}}` can take an optional first argument before its supplied options to
+ specify a path to a custom view class.
+
+ ```handlebars
+ {{#view "custom"}}{{! will look up App.CustomView }}
+ hello.
+ {{/view}}
+ ```
+
+ The first argument can also be a relative path accessible from the current
+ context.
+
+ ```javascript
+ MyApp = Ember.Application.create({});
+ MyApp.OuterView = Ember.View.extend({
+ innerViewClass: Ember.View.extend({
+ classNames: ['a-custom-view-class-as-property']
+ }),
+ template: Ember.Handlebars.compile('{{#view view.innerViewClass}} hi {{/view}}')
+ });
+
+ MyApp.OuterView.create().appendTo('body');
+ ```
+
+ Will result in the following HTML:
+
+ ```html
+
+
+ ```
+
+ ### Blockless use
+
+ If you supply a custom `Ember.View` subclass that specifies its own template
+ or provide a `templateName` option to `{{view}}` it can be used without
+ supplying a block. Attempts to use both a `templateName` option and supply a
+ block will throw an error.
+
+ ```javascript
+ var App = Ember.Application.create();
+ App.WithTemplateDefinedView = Ember.View.extend({
+ templateName: 'defined-template'
+ });
+ ```
+
+ ```handlebars
+ {{! application.hbs }}
+ {{view 'with-template-defined'}}
+ ```
+
+ ```handlebars
+ {{! defined-template.hbs }}
+ Some content for the defined template view.
+ ```
+
+ ### `viewName` property
+
+ You can supply a `viewName` option to `{{view}}`. The `Ember.View` instance
+ will be referenced as a property of its parent view by this name.
+
+ ```javascript
+ aView = Ember.View.create({
+ template: Ember.Handlebars.compile('{{#view viewName="aChildByName"}} hi {{/view}}')
+ });
+
+ aView.appendTo('body');
+ aView.get('aChildByName') // the instance of Ember.View created by {{view}} helper
+ ```
+
+ @method view
+ @for Ember.Handlebars.helpers
+ @param {String} path
+ @param {Hash} options
+ @return {String} HTML string
+ */
+ function viewHelper(path, options) {
+ Ember.assert("The view helper only takes a single argument", arguments.length <= 2);
+
+ // If no path is provided, treat path param as options
+ // and get an instance of the registered `view:toplevel`
+ if (path && path.data && path.data.isRenderData) {
+ options = path;
+ if (options.data && options.data.view && options.data.view.container) {
+ path = options.data.view.container.lookupFactory('view:toplevel');
+ } else {
+ path = View;
+ }
+ }
+
+ options.helperName = options.helperName || 'view';
+
+ return ViewHelper.helper(this, path, options);
+ }
+
+ __exports__.viewHelper = viewHelper;
+ });
+enifed("ember-handlebars/helpers/yield",
+ ["ember-metal/core","ember-metal/property_get","exports"],
+ function(__dependency1__, __dependency2__, __exports__) {
+ "use strict";
+ /**
+ @module ember
+ @submodule ember-handlebars
+ */
+
+ var Ember = __dependency1__["default"];
+ // var emberAssert = Ember.assert;
+
+ var get = __dependency2__.get;
+
+ /**
+ `{{yield}}` denotes an area of a template that will be rendered inside
+ of another template. It has two main uses:
+
+ ### Use with `layout`
+ When used in a Handlebars template that is assigned to an `Ember.View`
+ instance's `layout` property Ember will render the layout template first,
+ inserting the view's own rendered output at the `{{yield}}` location.
+
+ An empty `` and the following application code:
+
+ ```javascript
+ AView = Ember.View.extend({
+ classNames: ['a-view-with-layout'],
+ layout: Ember.Handlebars.compile('
+ hi
+
+ {{yield}} '),
+ template: Ember.Handlebars.compile('I am wrapped')
+ });
+
+ aView = AView.create();
+ aView.appendTo('body');
+ ```
+
+ Will result in the following HTML output:
+
+ ```html
+
+
+
+
+ ```
+
+ The `yield` helper cannot be used outside of a template assigned to an
+ `Ember.View`'s `layout` property and will throw an error if attempted.
+
+ ```javascript
+ BView = Ember.View.extend({
+ classNames: ['a-view-with-layout'],
+ template: Ember.Handlebars.compile('{{yield}}')
+ });
+
+ bView = BView.create();
+ bView.appendTo('body');
+
+ // throws
+ // Uncaught Error: assertion failed:
+ // You called yield in a template that was not a layout
+ ```
+
+ ### Use with Ember.Component
+ When designing components `{{yield}}` is used to denote where, inside the component's
+ template, an optional block passed to the component should render:
+
+ ```handlebars
+
+ {{#labeled-textfield value=someProperty}}
+ First name:
+ {{/labeled-textfield}}
+ ```
+
+ ```handlebars
+
+
+ ```
+
+ Result:
+
+ ```html
+
+ ```
+
+ @method yield
+ @for Ember.Handlebars.helpers
+ @param {Hash} options
+ @return {String} HTML string
+ */
+ __exports__["default"] = function yieldHelper(options) {
+ var view = options.data.view;
+
+ while (view && !get(view, 'layout')) {
+ if (view._contextView) {
+ view = view._contextView;
+ } else {
+ view = get(view, '_parentView');
+ }
+ }
+
+ Ember.assert("You called yield in a template that was not a layout", !!view);
+
+ view._yield(this, options);
+ }
+ });
+enifed("ember-handlebars/loader",
+ ["ember-handlebars/component_lookup","ember-views/system/jquery","ember-metal/error","ember-runtime/system/lazy_load","ember-handlebars-compiler","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) {
+ "use strict";
+ /*globals Handlebars */
+
+ var ComponentLookup = __dependency1__["default"];
+ var jQuery = __dependency2__["default"];
+ var EmberError = __dependency3__["default"];
+ var onLoad = __dependency4__.onLoad;
+
+ var EmberHandlebars = __dependency5__["default"];
+
+ /**
+ @module ember
+ @submodule ember-handlebars
+ */
+
+ /**
+ Find templates stored in the head tag as script tags and make them available
+ to `Ember.CoreView` in the global `Ember.TEMPLATES` object. This will be run
+ as as jQuery DOM-ready callback.
+
+ Script tags with `text/x-handlebars` will be compiled
+ with Ember's Handlebars and are suitable for use as a view's template.
+ Those with type `text/x-raw-handlebars` will be compiled with regular
+ Handlebars and are suitable for use in views' computed properties.
+
+ @private
+ @method bootstrap
+ @for Ember.Handlebars
+ @static
+ @param ctx
+ */
+ function bootstrap(ctx) {
+ var selectors = 'script[type="text/x-handlebars"], script[type="text/x-raw-handlebars"]';
+
+ jQuery(selectors, ctx)
+ .each(function() {
+ // Get a reference to the script tag
+ var script = jQuery(this);
+
+ var compile = (script.attr('type') === 'text/x-raw-handlebars') ?
+ jQuery.proxy(Handlebars.compile, Handlebars) :
+ jQuery.proxy(EmberHandlebars.compile, EmberHandlebars);
+ // Get the name of the script, used by Ember.View's templateName property.
+ // First look for data-template-name attribute, then fall back to its
+ // id if no name is found.
+ var templateName = script.attr('data-template-name') || script.attr('id') || 'application';
+ var template = compile(script.html());
+
+ // Check if template of same name already exists
+ if (Ember.TEMPLATES[templateName] !== undefined) {
+ throw new EmberError('Template named "' + templateName + '" already exists.');
+ }
+
+ // For templates which have a name, we save them and then remove them from the DOM
+ Ember.TEMPLATES[templateName] = template;
+
+ // Remove script tag from DOM
+ script.remove();
+ });
+ }
+
+ function _bootstrap() {
+ bootstrap( jQuery(document) );
+ }
+
+ function registerComponentLookup(container) {
+ container.register('component-lookup:main', ComponentLookup);
+ }
+
+ /*
+ We tie this to application.load to ensure that we've at least
+ attempted to bootstrap at the point that the application is loaded.
+
+ We also tie this to document ready since we're guaranteed that all
+ the inline templates are present at this point.
+
+ There's no harm to running this twice, since we remove the templates
+ from the DOM after processing.
+ */
+
+ onLoad('Ember.Application', function(Application) {
+ Application.initializer({
+ name: 'domTemplates',
+ initialize: _bootstrap
+ });
+
+ Application.initializer({
+ name: 'registerComponentLookup',
+ after: 'domTemplates',
+ initialize: registerComponentLookup
+ });
+ });
+
+ __exports__["default"] = bootstrap;
+ });
+enifed("ember-handlebars/string",
+ ["ember-runtime/system/string","exports"],
+ function(__dependency1__, __exports__) {
+ "use strict";
+ // required so we can extend this object.
+ var EmberStringUtils = __dependency1__["default"];
+
+ /**
+ Mark a string as safe for unescaped output with Handlebars. If you
+ return HTML from a Handlebars helper, use this function to
+ ensure Handlebars does not escape the HTML.
+
+ ```javascript
+ Ember.String.htmlSafe('
+ I am wrapped
+
+ someString ')
+ ```
+
+ @method htmlSafe
+ @for Ember.String
+ @static
+ @return {Handlebars.SafeString} a string that will not be html escaped by Handlebars
+ */
+ function htmlSafe(str) {
+ if (typeof str !== 'string') {
+ str = ''+str;
+ }
+ return new Handlebars.SafeString(str);
+ }
+
+ EmberStringUtils.htmlSafe = htmlSafe;
+ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) {
+
+ /**
+ Mark a string as being safe for unescaped output with Handlebars.
+
+ ```javascript
+ 'someString '.htmlSafe()
+ ```
+
+ See [Ember.String.htmlSafe](/api/classes/Ember.String.html#method_htmlSafe).
+
+ @method htmlSafe
+ @for String
+ @return {Handlebars.SafeString} a string that will not be html escaped by Handlebars
+ */
+ String.prototype.htmlSafe = function() {
+ return htmlSafe(this);
+ };
+ }
+
+ __exports__["default"] = htmlSafe;
+ });
+enifed("ember-handlebars/views/handlebars_bound_view",
+ ["ember-handlebars-compiler","ember-metal/core","ember-metal/error","ember-metal/property_get","ember-metal/property_set","ember-metal/merge","ember-metal/run_loop","ember-views/views/view","ember-handlebars/string","ember-views/views/states","ember-handlebars/views/metamorph_view","ember-handlebars/ext","ember-metal/utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __exports__) {
+ "use strict";
+ /*globals Handlebars, Metamorph:true */
+ /*jshint newcap:false*/
+
+
+ /**
+ @module ember
+ @submodule ember-handlebars
+ */
+
+ var EmberHandlebars = __dependency1__["default"];
+ // EmberHandlebars.SafeString;
+ var SafeString = EmberHandlebars.SafeString;
+
+ var Ember = __dependency2__["default"];
+ // Ember.K
+ var K = Ember.K;
+
+ var EmberError = __dependency3__["default"];
+ var get = __dependency4__.get;
+ var set = __dependency5__.set;
+ var merge = __dependency6__["default"];
+ var run = __dependency7__["default"];
+ var View = __dependency8__["default"];
+ var htmlSafe = __dependency9__["default"];
+ var cloneStates = __dependency10__.cloneStates;
+ var states = __dependency10__.states;
+ var viewStates = states;
+
+ var _MetamorphView = __dependency11__["default"];
+ var handlebarsGet = __dependency12__.handlebarsGet;
+ var uuid = __dependency13__.uuid;
+
+ function SimpleHandlebarsView(path, pathRoot, isEscaped, templateData) {
+ this.path = path;
+ this.pathRoot = pathRoot;
+ this.isEscaped = isEscaped;
+ this.templateData = templateData;
+ this[Ember.GUID_KEY] = uuid();
+ this._lastNormalizedValue = undefined;
+ this.state = 'preRender';
+ this.updateId = null;
+ this._parentView = null;
+ this.buffer = null;
+ this._morph = null;
+ }
+
+ SimpleHandlebarsView.prototype = {
+ isVirtual: true,
+ isView: true,
+
+ destroy: function () {
+ if (this.updateId) {
+ run.cancel(this.updateId);
+ this.updateId = null;
+ }
+ if (this._parentView) {
+ this._parentView.removeChild(this);
+ }
+ this.morph = null;
+ this.state = 'destroyed';
+ },
+
+ propertyWillChange: K,
+
+ propertyDidChange: K,
+
+ normalizedValue: function() {
+ var path = this.path;
+ var pathRoot = this.pathRoot;
+ var escape = this.isEscaped;
+ var result, templateData;
+
+ // Use the pathRoot as the result if no path is provided. This
+ // happens if the path is `this`, which gets normalized into
+ // a `pathRoot` of the current Handlebars context and a path
+ // of `''`.
+ if (path === '') {
+ result = pathRoot;
+ } else {
+ templateData = this.templateData;
+ result = handlebarsGet(pathRoot, path, { data: templateData });
+ }
+
+ if (!escape && !(result instanceof SafeString)) {
+ result = htmlSafe(result);
+ }
+
+ return result;
+ },
+
+ render: function(buffer) {
+ var value = this.normalizedValue();
+ this._lastNormalizedValue = value;
+ buffer._element = value;
+ },
+
+ rerender: function() {
+ switch(this.state) {
+ case 'preRender':
+ case 'destroyed':
+ break;
+ case 'inBuffer':
+ throw new EmberError("Something you did tried to replace an {{expression}} before it was inserted into the DOM.");
+ case 'hasElement':
+ case 'inDOM':
+ this.updateId = run.scheduleOnce('render', this, 'update');
+ break;
+ }
+ return this;
+ },
+
+ update: function () {
+ this.updateId = null;
+ var value = this.normalizedValue();
+ // doesn't diff SafeString instances
+ if (value !== this._lastNormalizedValue) {
+ this._lastNormalizedValue = value;
+ this._morph.update(value);
+ }
+ },
+
+ _transitionTo: function(state) {
+ this.state = state;
+ }
+ };
+
+ states = cloneStates(viewStates);
+
+ merge(states._default, {
+ rerenderIfNeeded: K
+ });
+
+ merge(states.inDOM, {
+ rerenderIfNeeded: function(view) {
+ if (view.normalizedValue() !== view._lastNormalizedValue) {
+ view.rerender();
+ }
+ }
+ });
+
+ /**
+ `Ember._HandlebarsBoundView` is a private view created by the Handlebars
+ `{{bind}}` helpers that is used to keep track of bound properties.
+
+ Every time a property is bound using a `{{mustache}}`, an anonymous subclass
+ of `Ember._HandlebarsBoundView` is created with the appropriate sub-template
+ and context set up. When the associated property changes, just the template
+ for this view will re-render.
+
+ @class _HandlebarsBoundView
+ @namespace Ember
+ @extends Ember._MetamorphView
+ @private
+ */
+ var _HandlebarsBoundView = _MetamorphView.extend({
+ instrumentName: 'boundHandlebars',
+
+ _states: states,
+
+ /**
+ The function used to determine if the `displayTemplate` or
+ `inverseTemplate` should be rendered. This should be a function that takes
+ a value and returns a Boolean.
+
+ @property shouldDisplayFunc
+ @type Function
+ @default null
+ */
+ shouldDisplayFunc: null,
+
+ /**
+ Whether the template rendered by this view gets passed the context object
+ of its parent template, or gets passed the value of retrieving `path`
+ from the `pathRoot`.
+
+ For example, this is true when using the `{{#if}}` helper, because the
+ template inside the helper should look up properties relative to the same
+ object as outside the block. This would be `false` when used with `{{#with
+ foo}}` because the template should receive the object found by evaluating
+ `foo`.
+
+ @property preserveContext
+ @type Boolean
+ @default false
+ */
+ preserveContext: false,
+
+ /**
+ If `preserveContext` is true, this is the object that will be used
+ to render the template.
+
+ @property previousContext
+ @type Object
+ */
+ previousContext: null,
+
+ /**
+ The template to render when `shouldDisplayFunc` evaluates to `true`.
+
+ @property displayTemplate
+ @type Function
+ @default null
+ */
+ displayTemplate: null,
+
+ /**
+ The template to render when `shouldDisplayFunc` evaluates to `false`.
+
+ @property inverseTemplate
+ @type Function
+ @default null
+ */
+ inverseTemplate: null,
+
+
+ /**
+ The path to look up on `pathRoot` that is passed to
+ `shouldDisplayFunc` to determine which template to render.
+
+ In addition, if `preserveContext` is `false,` the object at this path will
+ be passed to the template when rendering.
+
+ @property path
+ @type String
+ @default null
+ */
+ path: null,
+
+ /**
+ The object from which the `path` will be looked up. Sometimes this is the
+ same as the `previousContext`, but in cases where this view has been
+ generated for paths that start with a keyword such as `view` or
+ `controller`, the path root will be that resolved object.
+
+ @property pathRoot
+ @type Object
+ */
+ pathRoot: null,
+
+ normalizedValue: function() {
+ var path = get(this, 'path');
+ var pathRoot = get(this, 'pathRoot');
+ var valueNormalizer = get(this, 'valueNormalizerFunc');
+ var result, templateData;
+
+ // Use the pathRoot as the result if no path is provided. This
+ // happens if the path is `this`, which gets normalized into
+ // a `pathRoot` of the current Handlebars context and a path
+ // of `''`.
+ if (path === '') {
+ result = pathRoot;
+ } else {
+ templateData = get(this, 'templateData');
+ result = handlebarsGet(pathRoot, path, { data: templateData });
+ }
+
+ return valueNormalizer ? valueNormalizer(result) : result;
+ },
+
+ rerenderIfNeeded: function() {
+ this.currentState.rerenderIfNeeded(this);
+ },
+
+ /**
+ Determines which template to invoke, sets up the correct state based on
+ that logic, then invokes the default `Ember.View` `render` implementation.
+
+ This method will first look up the `path` key on `pathRoot`,
+ then pass that value to the `shouldDisplayFunc` function. If that returns
+ `true,` the `displayTemplate` function will be rendered to DOM. Otherwise,
+ `inverseTemplate`, if specified, will be rendered.
+
+ For example, if this `Ember._HandlebarsBoundView` represented the `{{#with
+ foo}}` helper, it would look up the `foo` property of its context, and
+ `shouldDisplayFunc` would always return true. The object found by looking
+ up `foo` would be passed to `displayTemplate`.
+
+ @method render
+ @param {Ember.RenderBuffer} buffer
+ */
+ render: function(buffer) {
+ // If not invoked via a triple-mustache ({{{foo}}}), escape
+ // the content of the template.
+ var escape = get(this, 'isEscaped');
+
+ var shouldDisplay = get(this, 'shouldDisplayFunc');
+ var preserveContext = get(this, 'preserveContext');
+ var context = get(this, 'previousContext');
+
+ var inverseTemplate = get(this, 'inverseTemplate');
+ var displayTemplate = get(this, 'displayTemplate');
+
+ var result = this.normalizedValue();
+
+ this._lastNormalizedValue = result;
+
+ // First, test the conditional to see if we should
+ // render the template or not.
+ if (shouldDisplay(result)) {
+ set(this, 'template', displayTemplate);
+
+ // If we are preserving the context (for example, if this
+ // is an #if block, call the template with the same object.
+ if (preserveContext) {
+ set(this, '_context', context);
+ } else {
+ // Otherwise, determine if this is a block bind or not.
+ // If so, pass the specified object to the template
+ if (displayTemplate) {
+ set(this, '_context', result);
+ } else {
+ // This is not a bind block, just push the result of the
+ // expression to the render context and return.
+ if (result === null || result === undefined) {
+ result = "";
+ } else if (!(result instanceof SafeString)) {
+ result = String(result);
+ }
+
+ if (escape) { result = Handlebars.Utils.escapeExpression(result); }
+ buffer.push(result);
+ return;
+ }
+ }
+ } else if (inverseTemplate) {
+ set(this, 'template', inverseTemplate);
+
+ if (preserveContext) {
+ set(this, '_context', context);
+ } else {
+ set(this, '_context', result);
+ }
+ } else {
+ set(this, 'template', function() { return ''; });
+ }
+
+ return this._super(buffer);
+ }
+ });
+
+ __exports__._HandlebarsBoundView = _HandlebarsBoundView;
+ __exports__.SimpleHandlebarsView = SimpleHandlebarsView;
+ });
+enifed("ember-handlebars/views/metamorph_view",
+ ["ember-metal/core","ember-views/views/core_view","ember-views/views/view","ember-metal/mixin","ember-metal/run_loop","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) {
+ "use strict";
+ /* global Metamorph:true */
+
+ /*jshint newcap:false*/
+ var Ember = __dependency1__["default"];
+ // Ember.deprecate
+
+ var CoreView = __dependency2__["default"];
+ var View = __dependency3__["default"];
+ var Mixin = __dependency4__.Mixin;
+ var run = __dependency5__["default"];
+
+ /**
+ @module ember
+ @submodule ember-handlebars
+ */
+
+ function notifyMutationListeners() {
+ run.once(View, 'notifyMutationListeners');
+ }
+
+ // The `morph` and `outerHTML` properties are internal only
+ // and not observable.
+
+ /**
+ @class _Metamorph
+ @namespace Ember
+ @private
+ */
+ var _Metamorph = Mixin.create({
+ isVirtual: true,
+ tagName: '',
+
+ instrumentName: 'metamorph',
+
+ init: function() {
+ this._super();
+ Ember.deprecate('Supplying a tagName to Metamorph views is unreliable and is deprecated. You may be setting the tagName on a Handlebars helper that creates a Metamorph.', !this.tagName);
+ }
+ });
+ __exports__._Metamorph = _Metamorph;
+ /**
+ @class _MetamorphView
+ @namespace Ember
+ @extends Ember.View
+ @uses Ember._Metamorph
+ @private
+ */
+ var _MetamorphView = View.extend(_Metamorph);
+ __exports__._MetamorphView = _MetamorphView;
+ /**
+ @class _SimpleMetamorphView
+ @namespace Ember
+ @extends Ember.CoreView
+ @uses Ember._Metamorph
+ @private
+ */
+ var _SimpleMetamorphView = CoreView.extend(_Metamorph);
+ __exports__._SimpleMetamorphView = _SimpleMetamorphView;__exports__["default"] = View.extend(_Metamorph);
+ });
+enifed("ember-metal-views",
+ ["ember-metal-views/renderer","exports"],
+ function(__dependency1__, __exports__) {
+ "use strict";
+ var Renderer = __dependency1__["default"];
+ __exports__.Renderer = Renderer;
+ });
+enifed("ember-metal-views/renderer",
+ ["morph","exports"],
+ function(__dependency1__, __exports__) {
+ "use strict";
+ var DOMHelper = __dependency1__.DOMHelper;
+
+ function Renderer() {
+ this._uuid = 0;
+ this._views = new Array(2000);
+ this._queue = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
+ this._parents = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
+ this._elements = new Array(17);
+ this._inserts = {};
+ this._dom = new DOMHelper();
+ }
+
+ function Renderer_renderTree(_view, _parentView, _insertAt) {
+ var views = this._views;
+ views[0] = _view;
+ var insertAt = _insertAt === undefined ? -1 : _insertAt;
+ var index = 0;
+ var total = 1;
+ var levelBase = _parentView ? _parentView._level+1 : 0;
+
+ var root = _parentView == null ? _view : _parentView._root;
+
+ // if root view has a _morph assigned
+ var willInsert = !!root._morph;
+
+ var queue = this._queue;
+ queue[0] = 0;
+ var length = 1;
+
+ var parentIndex = -1;
+ var parents = this._parents;
+ var parent = _parentView || null;
+ var elements = this._elements;
+ var element = null;
+ var contextualElement = null;
+ var level = 0;
+
+ var view = _view;
+ var children, i, l, child;
+ while (length) {
+ elements[level] = element;
+ if (!view._morph) {
+ // ensure props we add are in same order
+ view._morph = null;
+ }
+ view._root = root;
+ this.uuid(view);
+ view._level = levelBase + level;
+ if (view._elementCreated) {
+ this.remove(view, false, true);
+ }
+
+ this.willCreateElement(view);
+
+ contextualElement = view._morph && view._morph.contextualElement;
+ if (!contextualElement && parent && parent._childViewsMorph) {
+ contextualElement = parent._childViewsMorph.contextualElement;
+ }
+ if (!contextualElement && view._didCreateElementWithoutMorph) {
+ // This code path is only used by createElement and rerender when createElement
+ // was previously called on a view.
+ contextualElement = document.body;
+ }
+ Ember.assert("Required contextualElement for view "+_view+" is missing", contextualElement);
+ element = this.createElement(view, contextualElement);
+
+ parents[level++] = parentIndex;
+ parentIndex = index;
+ parent = view;
+
+ // enqueue for end
+ queue[length++] = index;
+ // enqueue children
+ children = this.childViews(view);
+ if (children) {
+ for (i=children.length-1;i>=0;i--) {
+ child = children[i];
+ index = total++;
+ views[index] = child;
+ queue[length++] = index;
+ view = child;
+ }
+ }
+
+ index = queue[--length];
+ view = views[index];
+
+ while (parentIndex === index) {
+ level--;
+ view._elementCreated = true;
+ this.didCreateElement(view);
+ if (willInsert) {
+ this.willInsertElement(view);
+ }
+
+ if (level === 0) {
+ length--;
+ break;
+ }
+
+ parentIndex = parents[level];
+ parent = parentIndex === -1 ? _parentView : views[parentIndex];
+ this.insertElement(view, parent, element, -1);
+ index = queue[--length];
+ view = views[index];
+ element = elements[level];
+ elements[level] = null;
+ }
+ }
+
+ this.insertElement(view, _parentView, element, insertAt);
+
+ for (i=total-1;i>=0;i--) {
+ if (willInsert) {
+ views[i]._elementInserted = true;
+ this.didInsertElement(views[i]);
+ }
+ views[i] = null;
+ }
+
+ return element;
+ }
+
+ Renderer.prototype.uuid = function Renderer_uuid(view) {
+ if (view._uuid === undefined) {
+ view._uuid = ++this._uuid;
+ view._renderer = this;
+ } // else assert(view._renderer === this)
+ return view._uuid;
+ };
+
+ Renderer.prototype.scheduleInsert =
+ function Renderer_scheduleInsert(view, morph) {
+ if (view._morph || view._elementCreated) {
+ throw new Error("You cannot insert a View that has already been rendered");
+ }
+ Ember.assert("You cannot insert a View without a morph", morph);
+ view._morph = morph;
+ var viewId = this.uuid(view);
+ this._inserts[viewId] = this.scheduleRender(this, function() {
+ this._inserts[viewId] = null;
+ this.renderTree(view);
+ });
+ };
+
+ Renderer.prototype.appendTo =
+ function Renderer_appendTo(view, target) {
+ var morph = this._dom.appendMorph(target);
+ this.scheduleInsert(view, morph);
+ };
+
+ Renderer.prototype.replaceIn =
+ function Renderer_replaceIn(view, target) {
+ var morph = this._dom.createMorph(target, null, null);
+ this.scheduleInsert(view, morph);
+ };
+
+ function Renderer_remove(_view, shouldDestroy, reset) {
+ var viewId = this.uuid(_view);
+
+ if (this._inserts[viewId]) {
+ this.cancelRender(this._inserts[viewId]);
+ this._inserts[viewId] = undefined;
+ }
+
+ if (!_view._elementCreated) {
+ return;
+ }
+
+ var removeQueue = [];
+ var destroyQueue = [];
+ var morph = _view._morph;
+ var idx, len, view, staticChildren, queue,
+ childViews, i, l, parentView;
+
+ removeQueue.push(_view);
+
+ for (idx=0; idx
+ click me
+
```
- Example:
+ And application code
```javascript
- var promise1 = RSVP.Promise.resolve(1);
- var promise2 = RSVP.Promise.reject(new Error('2'));
- var promise3 = RSVP.Promise.reject(new Error('3'));
- var promises = [ promise1, promise2, promise3 ];
-
- RSVP.allSettled(promises).then(function(array){
- // array == [
- // { state: 'fulfilled', value: 1 },
- // { state: 'rejected', reason: Error },
- // { state: 'rejected', reason: Error }
- // ]
- // Note that for the second item, reason.message will be "2", and for the
- // third item, reason.message will be "3".
- }, function(error) {
- // Not run. (This block would only be called if allSettled had failed,
- // for instance if passed an incorrect argument type.)
+ App.ApplicationController = Ember.Controller.extend({
+ actions: {
+ anActionName: function() {
+ }
+ }
});
```
- @method allSettled
- @for RSVP
- @param {Array} promises
- @param {String} label - optional string that describes the promise.
- Useful for tooling.
- @return {Promise} promise that is fulfilled with an array of the settled
- states of the constituent promises.
- @static
+ Will result in the following rendered HTML
+
+ ```html
+
+
+ ```
+
+ Clicking "click me" will trigger the `anActionName` action of the
+ `App.ApplicationController`. In this case, no additional parameters will be passed.
+
+ If you provide additional parameters to the helper:
+
+ ```handlebars
+
+ ```
+
+ Those parameters will be passed along as arguments to the JavaScript
+ function implementing the action.
+
+ ### Event Propagation
+
+ Events triggered through the action helper will automatically have
+ `.preventDefault()` called on them. You do not need to do so in your event
+ handlers. If you need to allow event propagation (to handle file inputs for
+ example) you can supply the `preventDefault=false` option to the `{{action}}` helper:
+
+ ```handlebars
+
+ click me
+
+
+
+
+
+ ```
+
+ To disable bubbling, pass `bubbles=false` to the helper:
+
+ ```handlebars
+
+ ```
+
+ If you need the default handler to trigger you should either register your
+ own event handler, or use event methods on your view class. See [Ember.View](/api/classes/Ember.View.html)
+ 'Responding to Browser Events' for more information.
+
+ ### Specifying DOM event type
+
+ By default the `{{action}}` helper registers for DOM `click` events. You can
+ supply an `on` option to the helper to specify a different DOM event name:
+
+ ```handlebars
+
+ click me
+
+ ```
+
+ See `Ember.View` 'Responding to Browser Events' for a list of
+ acceptable DOM event names.
+
+ ### Specifying whitelisted modifier keys
+
+ By default the `{{action}}` helper will ignore click event with pressed modifier
+ keys. You can supply an `allowedKeys` option to specify which keys should not be ignored.
+
+ ```handlebars
+
+ click me
+
+ ```
+
+ This way the `{{action}}` will fire when clicking with the alt key pressed down.
+
+ Alternatively, supply "any" to the `allowedKeys` option to accept any combination of modifier keys.
+
+ ```handlebars
+
+ click me with any key pressed
+
+ ```
+
+ ### Specifying a Target
+
+ There are several possible target objects for `{{action}}` helpers:
+
+ In a typical Ember application, where templates are managed through use of the
+ `{{outlet}}` helper, actions will bubble to the current controller, then
+ to the current route, and then up the route hierarchy.
+
+ Alternatively, a `target` option can be provided to the helper to change
+ which object will receive the method call. This option must be a path
+ to an object, accessible in the current context:
+
+ ```handlebars
+ {{! the application template }}
+
+ click me
+
+ ```
+
+ ```javascript
+ App.ApplicationView = Ember.View.extend({
+ actions: {
+ anActionName: function(){}
+ }
+ });
+
+ ```
+
+ ### Additional Parameters
+
+ You may specify additional parameters to the `{{action}}` helper. These
+ parameters are passed along as the arguments to the JavaScript function
+ implementing the action.
+
+ ```handlebars
+ {{#each person in people}}
+
+ click me
+
+ {{/each}}
+ ```
+
+ Clicking "click me" will trigger the `edit` method on the current controller
+ with the value of `person` as a parameter.
+
+ @method action
+ @for Ember.Handlebars.helpers
+ @param {String} actionName
+ @param {Object} [context]*
+ @param {Hash} options
+ */
+ function actionHelper(actionName) {
+ var options = arguments[arguments.length - 1];
+ var contexts = a_slice.call(arguments, 1, -1);
+ var hash = options.hash;
+ var controller = options.data.keywords.controller;
+
+ // create a hash to pass along to registerAction
+ var action = {
+ eventName: hash.on || "click",
+ parameters: {
+ context: this,
+ options: options,
+ params: contexts
+ },
+ view: options.data.view,
+ bubbles: hash.bubbles,
+ preventDefault: hash.preventDefault,
+ target: { options: options },
+ withKeyCode: hash.withKeyCode,
+ boundProperty: options.types[0] === "ID"
+ };
+
+ if (hash.target) {
+ action.target.root = this;
+ action.target.target = hash.target;
+ } else if (controller) {
+ action.target.root = controller;
+ }
+
+ var actionId = ActionHelper.registerAction(actionName, action, hash.allowedKeys);
+ return new SafeString('data-ember-action="' + actionId + '"');
+ }
+
+ __exports__.actionHelper = actionHelper;
+ });
+enifed("ember-routing-handlebars/helpers/link_to",
+ ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/merge","ember-metal/run_loop","ember-metal/computed","ember-runtime/system/lazy_load","ember-runtime/system/string","ember-runtime/system/object","ember-metal/keys","ember-views/system/utils","ember-views/views/component","ember-handlebars","ember-handlebars/helpers/view","ember-routing/system/router","ember-routing-handlebars/helpers/shared","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __exports__) {
+ "use strict";
+ var Ember = __dependency1__["default"];
+ // FEATURES, Logger, Handlebars, warn, assert
+ var get = __dependency2__.get;
+ var set = __dependency3__.set;
+ var merge = __dependency4__["default"];
+ var run = __dependency5__["default"];
+ var computed = __dependency6__.computed;
+
+ var onLoad = __dependency7__.onLoad;
+ var fmt = __dependency8__.fmt;
+ var EmberObject = __dependency9__["default"];
+ var keys = __dependency10__["default"];
+ var isSimpleClick = __dependency11__.isSimpleClick;
+ var EmberComponent = __dependency12__["default"];
+ var EmberHandlebars = __dependency13__["default"];
+ var viewHelper = __dependency14__.viewHelper;
+ var EmberRouter = __dependency15__["default"];
+ var resolveParams = __dependency16__.resolveParams;
+ var resolvePaths = __dependency16__.resolvePaths;
+ var routeArgs = __dependency16__.routeArgs;
+
+ /**
+ @module ember
+ @submodule ember-routing
*/
- __exports__["default"] = function allSettled(entries, label) {
- return new Promise(function(resolve, reject) {
- if (!isArray(entries)) {
- throw new TypeError('You must pass an array to allSettled.');
+ var slice = [].slice;
+
+ requireModule('ember-handlebars');
+
+ var numberOfContextsAcceptedByHandler = function(handler, handlerInfos) {
+ var req = 0;
+ for (var i = 0, l = handlerInfos.length; i < l; i++) {
+ req = req + handlerInfos[i].names.length;
+ if (handlerInfos[i].handler === handler)
+ break;
+ }
+
+ return req;
+ };
+
+ var QueryParams = EmberObject.extend({
+ values: null
+ });
+
+ function getResolvedPaths(options) {
+
+ var types = options.options.types;
+ var data = options.options.data;
+
+ return resolvePaths(options.context, options.params, { types: types, data: data });
+ }
+
+ /**
+ `Ember.LinkView` renders an element whose `click` event triggers a
+ transition of the application's instance of `Ember.Router` to
+ a supplied route by name.
+
+ Instances of `LinkView` will most likely be created through
+ the `link-to` Handlebars helper, but properties of this class
+ can be overridden to customize application-wide behavior.
+
+ @class LinkView
+ @namespace Ember
+ @extends Ember.View
+ @see {Handlebars.helpers.link-to}
+ **/
+ var LinkView = Ember.LinkView = EmberComponent.extend({
+ tagName: 'a',
+
+ /**
+ @deprecated Use current-when instead.
+ @property currentWhen
+ */
+ currentWhen: null,
+
+ /**
+ Used to determine when this LinkView is active.
+
+ @property currentWhen
+ */
+ 'current-when': null,
+
+ /**
+ Sets the `title` attribute of the `LinkView`'s HTML element.
+
+ @property title
+ @default null
+ **/
+ title: null,
+
+ /**
+ Sets the `rel` attribute of the `LinkView`'s HTML element.
+
+ @property rel
+ @default null
+ **/
+ rel: null,
+
+ /**
+ The CSS class to apply to `LinkView`'s element when its `active`
+ property is `true`.
+
+ @property activeClass
+ @type String
+ @default active
+ **/
+ activeClass: 'active',
+
+ /**
+ The CSS class to apply to `LinkView`'s element when its `loading`
+ property is `true`.
+
+ @property loadingClass
+ @type String
+ @default loading
+ **/
+ loadingClass: 'loading',
+
+ /**
+ The CSS class to apply to a `LinkView`'s element when its `disabled`
+ property is `true`.
+
+ @property disabledClass
+ @type String
+ @default disabled
+ **/
+ disabledClass: 'disabled',
+ _isDisabled: false,
+
+ /**
+ Determines whether the `LinkView` will trigger routing via
+ the `replaceWith` routing strategy.
+
+ @property replace
+ @type Boolean
+ @default false
+ **/
+ replace: false,
+
+ /**
+ By default the `{{link-to}}` helper will bind to the `href` and
+ `title` attributes. It's discourage that you override these defaults,
+ however you can push onto the array if needed.
+
+ @property attributeBindings
+ @type Array | String
+ @default ['href', 'title', 'rel']
+ **/
+ attributeBindings: ['href', 'title', 'rel', 'tabindex'],
+
+ /**
+ By default the `{{link-to}}` helper will bind to the `active`, `loading`, and
+ `disabled` classes. It is discouraged to override these directly.
+
+ @property classNameBindings
+ @type Array
+ @default ['active', 'loading', 'disabled']
+ **/
+ classNameBindings: ['active', 'loading', 'disabled'],
+
+ /**
+ By default the `{{link-to}}` helper responds to the `click` event. You
+ can override this globally by setting this property to your custom
+ event name.
+
+ This is particularly useful on mobile when one wants to avoid the 300ms
+ click delay using some sort of custom `tap` event.
+
+ @property eventName
+ @type String
+ @default click
+ */
+ eventName: 'click',
+
+ // this is doc'ed here so it shows up in the events
+ // section of the API documentation, which is where
+ // people will likely go looking for it.
+ /**
+ Triggers the `LinkView`'s routing behavior. If
+ `eventName` is changed to a value other than `click`
+ the routing behavior will trigger on that custom event
+ instead.
+
+ @event click
+ **/
+
+ /**
+ An overridable method called when LinkView objects are instantiated.
+
+ Example:
+
+ ```javascript
+ App.MyLinkView = Ember.LinkView.extend({
+ init: function() {
+ this._super();
+ Ember.Logger.log('Event is ' + this.get('eventName'));
+ }
+ });
+ ```
+
+ NOTE: If you do override `init` for a framework class like `Ember.View` or
+ `Ember.ArrayController`, be sure to call `this._super()` in your
+ `init` declaration! If you don't, Ember may not have an opportunity to
+ do important setup work, and you'll see strange behavior in your
+ application.
+
+ @method init
+ */
+ init: function() {
+ this._super.apply(this, arguments);
+
+ Ember.deprecate('Using currentWhen with {{link-to}} is deprecated in favor of `current-when`.', !this.currentWhen);
+
+ // Map desired event name to invoke function
+ var eventName = get(this, 'eventName');
+ this.on(eventName, this, this._invoke);
+ },
+
+ /**
+ This method is invoked by observers installed during `init` that fire
+ whenever the params change
+
+ @private
+ @method _paramsChanged
+ @since 1.3.0
+ */
+ _paramsChanged: function() {
+ this.notifyPropertyChange('resolvedParams');
+ },
+
+ /**
+ This is called to setup observers that will trigger a rerender.
+
+ @private
+ @method _setupPathObservers
+ @since 1.3.0
+ **/
+ _setupPathObservers: function(){
+ var helperParameters = this.parameters;
+ var linkTextPath = helperParameters.options.linkTextPath;
+ var paths = getResolvedPaths(helperParameters);
+ var length = paths.length;
+ var path, i, normalizedPath;
+
+ if (linkTextPath) {
+ normalizedPath = getNormalizedPath(linkTextPath, helperParameters);
+ this.registerObserver(normalizedPath.root, normalizedPath.path, this, this.rerender);
}
- var remaining = entries.length;
- var entry;
+ for(i=0; i < length; i++) {
+ path = paths[i];
+ if (null === path) {
+ // A literal value was provided, not a path, so nothing to observe.
+ continue;
+ }
- if (remaining === 0) {
- resolve([]);
+ normalizedPath = getNormalizedPath(path, helperParameters);
+ this.registerObserver(normalizedPath.root, normalizedPath.path, this, this._paramsChanged);
+ }
+
+ var queryParamsObject = this.queryParamsObject;
+ if (queryParamsObject) {
+ var values = queryParamsObject.values;
+
+ // Install observers for all of the hash options
+ // provided in the (query-params) subexpression.
+ for (var k in values) {
+ if (!values.hasOwnProperty(k)) { continue; }
+
+ if (queryParamsObject.types[k] === 'ID') {
+ normalizedPath = getNormalizedPath(values[k], helperParameters);
+ this.registerObserver(normalizedPath.root, normalizedPath.path, this, this._paramsChanged);
+ }
+ }
+ }
+ },
+
+ afterRender: function(){
+ this._super.apply(this, arguments);
+ this._setupPathObservers();
+ },
+
+ /**
+
+ Accessed as a classname binding to apply the `LinkView`'s `disabledClass`
+ CSS `class` to the element when the link is disabled.
+
+ When `true` interactions with the element will not trigger route changes.
+ @property disabled
+ */
+ disabled: computed(function computeLinkViewDisabled(key, value) {
+ if (value !== undefined) { this.set('_isDisabled', value); }
+
+ return value ? get(this, 'disabledClass') : false;
+ }),
+
+ /**
+ Accessed as a classname binding to apply the `LinkView`'s `activeClass`
+ CSS `class` to the element when the link is active.
+
+ A `LinkView` is considered active when its `currentWhen` property is `true`
+ or the application's current route is the route the `LinkView` would trigger
+ transitions into.
+
+ The `currentWhen` property can match against multiple routes by separating
+ route names using the ` ` (space) character.
+
+ @property active
+ **/
+ active: computed('loadedParams', function computeLinkViewActive() {
+ if (get(this, 'loading')) { return false; }
+
+ var router = get(this, 'router');
+ var loadedParams = get(this, 'loadedParams');
+ var contexts = loadedParams.models;
+ var currentWhen = this['current-when'] || this.currentWhen;
+ var isCurrentWhenSpecified = Boolean(currentWhen);
+ currentWhen = currentWhen || loadedParams.targetRouteName;
+
+ function isActiveForRoute(routeName) {
+ var handlers = router.router.recognizer.handlersFor(routeName);
+ var leafName = handlers[handlers.length-1].handler;
+ var maximumContexts = numberOfContextsAcceptedByHandler(routeName, handlers);
+
+ // NOTE: any ugliness in the calculation of activeness is largely
+ // due to the fact that we support automatic normalizing of
+ // `resource` -> `resource.index`, even though there might be
+ // dynamic segments / query params defined on `resource.index`
+ // which complicates (and makes somewhat ambiguous) the calculation
+ // of activeness for links that link to `resource` instead of
+ // directly to `resource.index`.
+
+ // if we don't have enough contexts revert back to full route name
+ // this is because the leaf route will use one of the contexts
+ if (contexts.length > maximumContexts) {
+ routeName = leafName;
+ }
+
+ var args = routeArgs(routeName, contexts, null);
+ var isActive = router.isActive.apply(router, args);
+ if (!isActive) { return false; }
+
+ var emptyQueryParams = Ember.isEmpty(Ember.keys(loadedParams.queryParams));
+
+ if (!isCurrentWhenSpecified && !emptyQueryParams && isActive) {
+ var visibleQueryParams = {};
+ merge(visibleQueryParams, loadedParams.queryParams);
+ router._prepareQueryParams(loadedParams.targetRouteName, loadedParams.models, visibleQueryParams);
+ isActive = shallowEqual(visibleQueryParams, router.router.state.queryParams);
+ }
+
+ return isActive;
+ }
+
+
+ currentWhen = currentWhen.split(' ');
+ for (var i = 0, len = currentWhen.length; i < len; i++) {
+ if (isActiveForRoute(currentWhen[i])) {
+ return get(this, 'activeClass');
+ }
+ }
+ }),
+
+ /**
+ Accessed as a classname binding to apply the `LinkView`'s `loadingClass`
+ CSS `class` to the element when the link is loading.
+
+ A `LinkView` is considered loading when it has at least one
+ parameter whose value is currently null or undefined. During
+ this time, clicking the link will perform no transition and
+ emit a warning that the link is still in a loading state.
+
+ @property loading
+ **/
+ loading: computed('loadedParams', function computeLinkViewLoading() {
+ if (!get(this, 'loadedParams')) { return get(this, 'loadingClass'); }
+ }),
+
+ /**
+ Returns the application's main router from the container.
+
+ @private
+ @property router
+ **/
+ router: computed(function() {
+ var controller = get(this, 'controller');
+ if (controller && controller.container) {
+ return controller.container.lookup('router:main');
+ }
+ }),
+
+ /**
+ Event handler that invokes the link, activating the associated route.
+
+ @private
+ @method _invoke
+ @param {Event} event
+ */
+ _invoke: function(event) {
+ if (!isSimpleClick(event)) { return true; }
+
+ if (this.preventDefault !== false) {
+
+ var targetAttribute = get(this, 'target');
+ if (!targetAttribute || targetAttribute === '_self') {
+ event.preventDefault();
+ }
+ }
+
+ if (this.bubbles === false) { event.stopPropagation(); }
+
+ if (get(this, '_isDisabled')) { return false; }
+
+ if (get(this, 'loading')) {
+ Ember.Logger.warn("This link-to is in an inactive loading state because at least one of its parameters presently has a null/undefined value, or the provided route name is invalid.");
+ return false;
+ }
+
+
+ var targetAttribute2 = get(this, 'target');
+ if (targetAttribute2 && targetAttribute2 !== '_self') {
+ return false;
+ }
+
+
+ var router = get(this, 'router');
+ var loadedParams = get(this, 'loadedParams');
+
+ var transition = router._doTransition(loadedParams.targetRouteName, loadedParams.models, loadedParams.queryParams);
+ if (get(this, 'replace')) {
+ transition.method('replace');
+ }
+
+ // Schedule eager URL update, but after we've given the transition
+ // a chance to synchronously redirect.
+ // We need to always generate the URL instead of using the href because
+ // the href will include any rootURL set, but the router expects a URL
+ // without it! Note that we don't use the first level router because it
+ // calls location.formatURL(), which also would add the rootURL!
+ var args = routeArgs(loadedParams.targetRouteName, loadedParams.models, transition.state.queryParams);
+ var url = router.router.generate.apply(router.router, args);
+
+ run.scheduleOnce('routerTransitions', this, this._eagerUpdateUrl, transition, url);
+ },
+
+ /**
+ @private
+ @method _eagerUpdateUrl
+ @param transition
+ @param href
+ */
+ _eagerUpdateUrl: function(transition, href) {
+ if (!transition.isActive || !transition.urlMethod) {
+ // transition was aborted, already ran to completion,
+ // or it has a null url-updated method.
return;
}
- var results = new Array(remaining);
-
- function fulfilledResolver(index) {
- return function(value) {
- resolveAll(index, fulfilled(value));
- };
+ if (href.indexOf('#') === 0) {
+ href = href.slice(1);
}
- function rejectedResolver(index) {
- return function(reason) {
- resolveAll(index, rejected(reason));
- };
+ // Re-use the routerjs hooks set up by the Ember router.
+ var routerjs = get(this, 'router.router');
+ if (transition.urlMethod === 'update') {
+ routerjs.updateURL(href);
+ } else if (transition.urlMethod === 'replace') {
+ routerjs.replaceURL(href);
}
- function resolveAll(index, value) {
- results[index] = value;
- if (--remaining === 0) {
- resolve(results);
- }
+ // Prevent later update url refire.
+ transition.method(null);
+ },
+
+ /**
+ Computed property that returns an array of the
+ resolved parameters passed to the `link-to` helper,
+ e.g.:
+
+ ```hbs
+ {{link-to a b '123' c}}
+ ```
+
+ will generate a `resolvedParams` of:
+
+ ```js
+ [aObject, bObject, '123', cObject]
+ ```
+
+ @private
+ @property
+ @return {Array}
+ */
+ resolvedParams: computed('router.url', function() {
+ var parameters = this.parameters;
+ var options = parameters.options;
+ var types = options.types;
+ var data = options.data;
+ var targetRouteName, models;
+ var onlyQueryParamsSupplied = (parameters.params.length === 0);
+
+ if (onlyQueryParamsSupplied) {
+ var appController = this.container.lookup('controller:application');
+ targetRouteName = get(appController, 'currentRouteName');
+ models = [];
+ } else {
+ models = resolveParams(parameters.context, parameters.params, { types: types, data: data });
+ targetRouteName = models.shift();
}
- for (var index = 0; index < entries.length; index++) {
- entry = entries[index];
+ var suppliedQueryParams = getResolvedQueryParams(this, targetRouteName);
- if (isNonThenable(entry)) {
- resolveAll(index, fulfilled(entry));
- } else {
- Promise.cast(entry).then(fulfilledResolver(index), rejectedResolver(index));
- }
+ return {
+ targetRouteName: targetRouteName,
+ models: models,
+ queryParams: suppliedQueryParams
+ };
+ }),
+
+ /**
+ Computed property that returns the current route name,
+ dynamic segments, and query params. Returns falsy if
+ for null/undefined params to indicate that the link view
+ is still in a loading state.
+
+ @private
+ @property
+ @return {Array} An array with the route name and any dynamic segments
+ **/
+ loadedParams: computed('resolvedParams', function computeLinkViewRouteArgs() {
+ var router = get(this, 'router');
+ if (!router) { return; }
+
+ var resolvedParams = get(this, 'resolvedParams');
+ var namedRoute = resolvedParams.targetRouteName;
+
+ if (!namedRoute) { return; }
+
+ Ember.assert(fmt("The attempt to link-to route '%@' failed. " +
+ "The router did not find '%@' in its possible routes: '%@'",
+ [namedRoute, namedRoute, keys(router.router.recognizer.names).join("', '")]),
+ router.hasRoute(namedRoute));
+
+ if (!paramsAreLoaded(resolvedParams.models)) { return; }
+
+ return resolvedParams;
+ }),
+
+ queryParamsObject: null,
+
+ /**
+ Sets the element's `href` attribute to the url for
+ the `LinkView`'s targeted route.
+
+ If the `LinkView`'s `tagName` is changed to a value other
+ than `a`, this property will be ignored.
+
+ @property href
+ **/
+ href: computed('loadedParams', function computeLinkViewHref() {
+ if (get(this, 'tagName') !== 'a') { return; }
+
+ var router = get(this, 'router');
+ var loadedParams = get(this, 'loadedParams');
+
+ if (!loadedParams) {
+ return get(this, 'loadingHref');
}
- }, label);
- };
- function fulfilled(value) {
- return { state: 'fulfilled', value: value };
- }
+ var visibleQueryParams = {};
+ merge(visibleQueryParams, loadedParams.queryParams);
+ router._prepareQueryParams(loadedParams.targetRouteName, loadedParams.models, visibleQueryParams);
- function rejected(reason) {
- return { state: 'rejected', reason: reason };
- }
- });
-define("rsvp/config",
- ["./events","exports"],
- function(__dependency1__, __exports__) {
- "use strict";
- var EventTarget = __dependency1__["default"];
+ var args = routeArgs(loadedParams.targetRouteName, loadedParams.models, visibleQueryParams);
+ var result = router.generate.apply(router, args);
+ return result;
+ }),
- var config = {
- instrument: false
- };
+ /**
+ The default href value to use while a link-to is loading.
+ Only applies when tagName is 'a'
- EventTarget.mixin(config);
+ @property loadingHref
+ @type String
+ @default #
+ */
+ loadingHref: '#'
+ });
- function configure(name, value) {
- if (name === 'onerror') {
- // handle for legacy users that expect the actual
- // error to be passed to their function added via
- // `RSVP.configure('onerror', someFunctionHere);`
- config.on('error', value);
- return;
- }
+ LinkView.toString = function() { return "LinkView"; };
- if (arguments.length === 2) {
- config[name] = value;
- } else {
- return config[name];
- }
- }
+
+ LinkView.reopen({
+ attributeBindings: ['target'],
- __exports__.config = config;
- __exports__.configure = configure;
- });
-define("rsvp/defer",
- ["./promise","exports"],
- function(__dependency1__, __exports__) {
- "use strict";
- var Promise = __dependency1__["default"];
+ /**
+ Sets the `target` attribute of the `LinkView`'s anchor element.
+
+ @property target
+ @default null
+ **/
+ target: null
+ });
+
/**
- `RSVP.defer` returns an object similar to jQuery's `$.Deferred`.
- `RSVP.defer` should be used when porting over code reliant on `$.Deferred`'s
- interface. New code should use the `RSVP.Promise` constructor instead.
+ The `{{link-to}}` helper renders a link to the supplied
+ `routeName` passing an optionally supplied model to the
+ route as its `model` context of the route. The block
+ for `{{link-to}}` becomes the innerHTML of the rendered
+ element:
- The object returned from `RSVP.defer` is a plain object with three properties:
+ ```handlebars
+ {{#link-to 'photoGallery'}}
+ Great Hamster Photos
+ {{/link-to}}
+ ```
- * promise - an `RSVP.Promise`.
- * reject - a function that causes the `promise` property on this object to
- become rejected
- * resolve - a function that causes the `promise` property on this object to
- become fulfilled.
+ ```html
+
+ Great Hamster Photos
+
+ ```
+
+ ### Supplying a tagName
+ By default `{{link-to}}` renders an `` element. This can
+ be overridden for a single use of `{{link-to}}` by supplying
+ a `tagName` option:
+
+ ```handlebars
+ {{#link-to 'photoGallery' tagName="li"}}
+ Great Hamster Photos
+ {{/link-to}}
+ ```
+
+ ```html
+ My great app+ {{render "navigation"}} + ``` + + ```html +My great app+
+ Hello, world.
+
+ ```
+
+ Optionally you may provide a second argument: a property path
+ that will be bound to the `model` property of the controller.
+
+ If a `model` property path is specified, then a new instance of the
+ controller will be created and `{{render}}` can be used multiple times
+ with the same name.
+
+ For example if you had this `author` template.
+
+ ```handlebars
+
+ ```
+
+ You could render it inside the `post` template using the `render` helper.
+
+ ```handlebars
+
+
+ ```
+
+ @method render
+ @for Ember.Handlebars.helpers
+ @param {String} name
+ @param {Object?} contextString
+ @param {Hash} options
+ @return {String} HTML string
+ */
+ __exports__["default"] = function renderHelper(name, contextString, options) {
+ var length = arguments.length;
+ var contextProvided = length === 3;
+ var container, router, controller, view, context, lookupOptions;
+
+ container = (options || contextString).data.keywords.controller.container;
+ router = container.lookup('router:main');
+
+ if (length === 2) {
+ // use the singleton controller
+ options = contextString;
+ contextString = undefined;
+ Ember.assert("You can only use the {{render}} helper once without a model object as its second argument, as in {{render \"post\" post}}.", !router || !router._lookupActiveView(name));
+ } else if (length === 3) {
+ // create a new controller
+ context = handlebarsGet(options.contexts[1], contextString, options);
+ } else {
+ throw new EmberError("You must pass a templateName to render");
+ }
+
+ Ember.deprecate("Using a quoteless parameter with {{render}} is deprecated. Please update to quoted usage '{{render \"" + name + "\"}}.", options.types[0] !== 'ID');
+
+ // # legacy namespace
+ name = name.replace(/\//g, '.');
+ // \ legacy slash as namespace support
+
+
+ view = container.lookup('view:' + name) || container.lookup('view:default');
+
+ // provide controller override
+ var controllerName = options.hash.controller || name;
+ var controllerFullName = 'controller:' + controllerName;
+
+ if (options.hash.controller) {
+ Ember.assert("The controller name you supplied '" + controllerName + "' did not resolve to a controller.", container.has(controllerFullName));
+ }
+
+ var parentController = options.data.keywords.controller;
+
+ // choose name
+ if (length > 2) {
+ var factory = container.lookupFactory(controllerFullName) ||
+ generateControllerFactory(container, controllerName, context);
+
+ controller = factory.create({
+ model: context,
+ parentController: parentController,
+ target: parentController
+ });
+
+ view.one('willDestroyElement', function() {
+ controller.destroy();
+ });
+ } else {
+ controller = container.lookup(controllerFullName) ||
+ generateController(container, controllerName);
+
+ controller.setProperties({
+ target: parentController,
+ parentController: parentController
+ });
+ }
+
+ var root = options.contexts[1];
+
+ if (root) {
+ view.registerObserver(root, contextString, function() {
+ controller.set('model', handlebarsGet(root, contextString, options));
+ });
+ }
+
+ options.hash.viewName = camelize(name);
+
+ var templateName = 'template:' + name;
+ Ember.assert("You used `{{render '" + name + "'}}`, but '" + name + "' can not be found as either a template or a view.", container.has("view:" + name) || container.has(templateName) || options.fn);
+ options.hash.template = container.lookup(templateName);
+
+ options.hash.controller = controller;
+
+ if (router && !context) {
+ router._connectActiveView(name, view);
+ }
+
+ options.helperName = options.helperName || ('render "' + name + '"');
+
+ ViewHelper.instanceHelper(this, view, options);
+ }
+ });
+enifed("ember-routing-handlebars/helpers/shared",
+ ["ember-metal/property_get","ember-metal/array","ember-runtime/mixins/controller","ember-handlebars/ext","ember-metal/utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) {
+ "use strict";
+ var get = __dependency1__.get;
+ var map = __dependency2__.map;
+ var ControllerMixin = __dependency3__["default"];
+ var handlebarsResolve = __dependency4__.resolveParams;
+ var handlebarsGet = __dependency4__.handlebarsGet;
+ var typeOf = __dependency5__.typeOf;
+ var get = __dependency1__.get;
+
+ function routeArgs(targetRouteName, models, queryParams) {
+ var args = [];
+ if (typeOf(targetRouteName) === 'string') {
+ args.push('' + targetRouteName);
+ }
+ args.push.apply(args, models);
+ args.push({ queryParams: queryParams });
+ return args;
+ }
+
+ __exports__.routeArgs = routeArgs;function getActiveTargetName(router) {
+ var handlerInfos = router.activeTransition ?
+ router.activeTransition.state.handlerInfos :
+ router.state.handlerInfos;
+ return handlerInfos[handlerInfos.length - 1].name;
+ }
+
+ __exports__.getActiveTargetName = getActiveTargetName;function resolveParams(context, params, options) {
+ return map.call(resolvePaths(context, params, options), function(path, i) {
+ if (null === path) {
+ // Param was string/number, not a path, so just return raw string/number.
+ return params[i];
+ } else {
+ return handlebarsGet(context, path, options);
+ }
+ });
+ }
+
+ __exports__.resolveParams = resolveParams;function stashParamNames(router, handlerInfos) {
+ if (handlerInfos._namesStashed) { return; }
+
+ // This helper exists because router.js/route-recognizer.js awkwardly
+ // keeps separate a handlerInfo's list of parameter names depending
+ // on whether a URL transition or named transition is happening.
+ // Hopefully we can remove this in the future.
+ var targetRouteName = handlerInfos[handlerInfos.length-1].name;
+ var recogHandlers = router.router.recognizer.handlersFor(targetRouteName);
+ var dynamicParent = null;
+
+ for (var i = 0, len = handlerInfos.length; i < len; ++i) {
+ var handlerInfo = handlerInfos[i];
+ var names = recogHandlers[i].names;
+
+ if (names.length) {
+ dynamicParent = handlerInfo;
+ }
+
+ handlerInfo._names = names;
+
+ var route = handlerInfo.handler;
+ route._stashNames(handlerInfo, dynamicParent);
+ }
+
+ handlerInfos._namesStashed = true;
+ }
+
+ __exports__.stashParamNames = stashParamNames;function resolvePaths(context, params, options) {
+ var resolved = handlebarsResolve(context, params, options);
+ var types = options.types;
+
+ return map.call(resolved, function(object, i) {
+ if (types[i] === 'ID') {
+ return unwrap(object, params[i]);
+ } else {
+ return null;
+ }
+ });
+
+ function unwrap(object, path) {
+ if (path === 'controller') { return path; }
+
+ if (ControllerMixin.detect(object)) {
+ return unwrap(get(object, 'model'), path ? path + '.model' : 'model');
+ } else {
+ return path;
+ }
+ }
+ }
+
+ __exports__.resolvePaths = resolvePaths;
+ });
+enifed("ember-routing",
+ ["ember-handlebars","ember-metal/core","ember-routing/ext/run_loop","ember-routing/ext/controller","ember-routing/ext/view","ember-routing/location/api","ember-routing/location/none_location","ember-routing/location/hash_location","ember-routing/location/history_location","ember-routing/location/auto_location","ember-routing/system/generate_controller","ember-routing/system/controller_for","ember-routing/system/dsl","ember-routing/system/router","ember-routing/system/route","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __exports__) {
+ "use strict";
+ /**
+ Ember Routing
+
+ @module ember
+ @submodule ember-routing
+ @requires ember-views
+ */
+
+ var EmberHandlebars = __dependency1__["default"];
+ var Ember = __dependency2__["default"];
+
+ // ES6TODO: Cleanup modules with side-effects below
+
+ var EmberLocation = __dependency6__["default"];
+ var NoneLocation = __dependency7__["default"];
+ var HashLocation = __dependency8__["default"];
+ var HistoryLocation = __dependency9__["default"];
+ var AutoLocation = __dependency10__["default"];
+
+ var generateControllerFactory = __dependency11__.generateControllerFactory;
+ var generateController = __dependency11__["default"];
+ var controllerFor = __dependency12__["default"];
+ var RouterDSL = __dependency13__["default"];
+ var Router = __dependency14__["default"];
+ var Route = __dependency15__["default"];
+
+ Ember.Location = EmberLocation;
+ Ember.AutoLocation = AutoLocation;
+ Ember.HashLocation = HashLocation;
+ Ember.HistoryLocation = HistoryLocation;
+ Ember.NoneLocation = NoneLocation;
+
+ Ember.controllerFor = controllerFor;
+ Ember.generateControllerFactory = generateControllerFactory;
+ Ember.generateController = generateController;
+ Ember.RouterDSL = RouterDSL;
+ Ember.Router = Router;
+ Ember.Route = Route;
+
+ __exports__["default"] = Ember;
+ });
+enifed("ember-routing/ext/controller",
+ ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/computed","ember-metal/utils","ember-metal/merge","ember-metal/enumerable_utils","ember-runtime/mixins/controller","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) {
+ "use strict";
+ var Ember = __dependency1__["default"];
+ // FEATURES, deprecate
+ var get = __dependency2__.get;
+ var set = __dependency3__.set;
+ var computed = __dependency4__.computed;
+ var typeOf = __dependency5__.typeOf;
+ var meta = __dependency5__.meta;
+ var merge = __dependency6__["default"];
+ var map = __dependency7__.map;
+
+ var ControllerMixin = __dependency8__["default"];
+
+ /**
+ @module ember
+ @submodule ember-routing
+ */
+
+ ControllerMixin.reopen({
+ concatenatedProperties: ['queryParams', '_pCacheMeta'],
+
+ init: function() {
+ this._super.apply(this, arguments);
+ listenForQueryParamChanges(this);
+ },
+
+ /**
+ @property queryParams
+ @public
+ */
+ queryParams: null,
+
+ /**
+ @property _qpDelegate
+ @private
+ */
+ _qpDelegate: null,
+
+ /**
+ @property _normalizedQueryParams
+ @private
+ */
+ _normalizedQueryParams: computed(function() {
+ var m = meta(this);
+ if (m.proto !== this) {
+ return get(m.proto, '_normalizedQueryParams');
+ }
+
+ var queryParams = get(this, 'queryParams');
+ if (queryParams._qpMap) {
+ return queryParams._qpMap;
+ }
+
+ var qpMap = queryParams._qpMap = {};
+
+ for (var i = 0, len = queryParams.length; i < len; ++i) {
+ accumulateQueryParamDescriptors(queryParams[i], qpMap);
+ }
+
+ return qpMap;
+ }),
+
+ /**
+ @property _cacheMeta
+ @private
+ */
+ _cacheMeta: computed(function() {
+ var m = meta(this);
+ if (m.proto !== this) {
+ return get(m.proto, '_cacheMeta');
+ }
+
+ var cacheMeta = {};
+ var qpMap = get(this, '_normalizedQueryParams');
+ for (var prop in qpMap) {
+ if (!qpMap.hasOwnProperty(prop)) { continue; }
+
+ var qp = qpMap[prop];
+ var scope = qp.scope;
+ var parts;
+
+ if (scope === 'controller') {
+ parts = [];
+ }
+
+ cacheMeta[prop] = {
+ parts: parts, // provided by route if 'model' scope
+ values: null, // provided by route
+ scope: scope,
+ prefix: "",
+ def: get(this, prop)
+ };
+ }
+
+ return cacheMeta;
+ }),
+
+ /**
+ @method _updateCacheParams
+ @private
+ */
+ _updateCacheParams: function(params) {
+ var cacheMeta = get(this, '_cacheMeta');
+ for (var prop in cacheMeta) {
+ if (!cacheMeta.hasOwnProperty(prop)) { continue; }
+ var propMeta = cacheMeta[prop];
+ propMeta.values = params;
+
+ var cacheKey = this._calculateCacheKey(propMeta.prefix, propMeta.parts, propMeta.values);
+ var cache = this._bucketCache;
+
+ if (cache) {
+ var value = cache.lookup(cacheKey, prop, propMeta.def);
+ set(this, prop, value);
+ }
+ }
+ },
+
+ /**
+ @method _qpChanged
+ @private
+ */
+ _qpChanged: function(controller, _prop) {
+ var prop = _prop.substr(0, _prop.length-3);
+ var cacheMeta = get(controller, '_cacheMeta');
+ var propCache = cacheMeta[prop];
+ var cacheKey = controller._calculateCacheKey(propCache.prefix || "", propCache.parts, propCache.values);
+ var value = get(controller, prop);
+
+ // 1. Update model-dep cache
+ var cache = this._bucketCache;
+ if (cache) {
+ controller._bucketCache.stash(cacheKey, prop, value);
+ }
+
+ // 2. Notify a delegate (e.g. to fire a qp transition)
+ var delegate = controller._qpDelegate;
+ if (delegate) {
+ delegate(controller, prop);
+ }
+ },
+
+ /**
+ @method _calculateCacheKey
+ @private
+ */
+ _calculateCacheKey: function(prefix, _parts, values) {
+ var parts = _parts || [], suffixes = "";
+ for (var i = 0, len = parts.length; i < len; ++i) {
+ var part = parts[i];
+ var value = get(values, part);
+ suffixes += "::" + part + ":" + value;
+ }
+ return prefix + suffixes.replace(ALL_PERIODS_REGEX, '-');
+ },
+
+ /**
+ Transition the application into another route. The route may
+ be either a single route or route path:
+
+ ```javascript
+ aController.transitionToRoute('blogPosts');
+ aController.transitionToRoute('blogPosts.recentEntries');
+ ```
+
+ Optionally supply a model for the route in question. The model
+ will be serialized into the URL using the `serialize` hook of
+ the route:
+
+ ```javascript
+ aController.transitionToRoute('blogPost', aPost);
+ ```
+
+ If a literal is passed (such as a number or a string), it will
+ be treated as an identifier instead. In this case, the `model`
+ hook of the route will be triggered:
+
+ ```javascript
+ aController.transitionToRoute('blogPost', 1);
+ ```
+
+ Multiple models will be applied last to first recursively up the
+ resource tree.
+
+ ```javascript
+ App.Router.map(function() {
+ this.resource('blogPost', {path:':blogPostId'}, function(){
+ this.resource('blogComment', {path: ':blogCommentId'});
+ });
+ });
+
+ aController.transitionToRoute('blogComment', aPost, aComment);
+ aController.transitionToRoute('blogComment', 1, 13);
+ ```
+
+ It is also possible to pass a URL (a string that starts with a
+ `/`). This is intended for testing and debugging purposes and
+ should rarely be used in production code.
+
+ ```javascript
+ aController.transitionToRoute('/');
+ aController.transitionToRoute('/blog/post/1/comment/13');
+ ```
+
+ See also [replaceRoute](/api/classes/Ember.ControllerMixin.html#method_replaceRoute).
+
+ @param {String} name the name of the route or a URL
+ @param {...Object} models the model(s) or identifier(s) to be used
+ while transitioning to the route.
+ @for Ember.ControllerMixin
+ @method transitionToRoute
+ */
+ transitionToRoute: function() {
+ // target may be either another controller or a router
+ var target = get(this, 'target');
+ var method = target.transitionToRoute || target.transitionTo;
+ return method.apply(target, arguments);
+ },
+
+ /**
+ @deprecated
+ @for Ember.ControllerMixin
+ @method transitionTo
+ */
+ transitionTo: function() {
+ Ember.deprecate("transitionTo is deprecated. Please use transitionToRoute.");
+ return this.transitionToRoute.apply(this, arguments);
+ },
+
+ /**
+ Transition into another route while replacing the current URL, if possible.
+ This will replace the current history entry instead of adding a new one.
+ Beside that, it is identical to `transitionToRoute` in all other respects.
+
+ ```javascript
+ aController.replaceRoute('blogPosts');
+ aController.replaceRoute('blogPosts.recentEntries');
+ ```
+
+ Optionally supply a model for the route in question. The model
+ will be serialized into the URL using the `serialize` hook of
+ the route:
+
+ ```javascript
+ aController.replaceRoute('blogPost', aPost);
+ ```
+
+ If a literal is passed (such as a number or a string), it will
+ be treated as an identifier instead. In this case, the `model`
+ hook of the route will be triggered:
+
+ ```javascript
+ aController.replaceRoute('blogPost', 1);
+ ```
+
+ Multiple models will be applied last to first recursively up the
+ resource tree.
+
+ ```javascript
+ App.Router.map(function() {
+ this.resource('blogPost', {path:':blogPostId'}, function(){
+ this.resource('blogComment', {path: ':blogCommentId'});
+ });
+ });
+
+ aController.replaceRoute('blogComment', aPost, aComment);
+ aController.replaceRoute('blogComment', 1, 13);
+ ```
+
+ It is also possible to pass a URL (a string that starts with a
+ `/`). This is intended for testing and debugging purposes and
+ should rarely be used in production code.
+
+ ```javascript
+ aController.replaceRoute('/');
+ aController.replaceRoute('/blog/post/1/comment/13');
+ ```
+
+ @param {String} name the name of the route or a URL
+ @param {...Object} models the model(s) or identifier(s) to be used
+ while transitioning to the route.
+ @for Ember.ControllerMixin
+ @method replaceRoute
+ */
+ replaceRoute: function() {
+ // target may be either another controller or a router
+ var target = get(this, 'target');
+ var method = target.replaceRoute || target.replaceWith;
+ return method.apply(target, arguments);
+ },
+
+ /**
+ @deprecated
+ @for Ember.ControllerMixin
+ @method replaceWith
+ */
+ replaceWith: function() {
+ Ember.deprecate("replaceWith is deprecated. Please use replaceRoute.");
+ return this.replaceRoute.apply(this, arguments);
+ }
+ });
+
+ var ALL_PERIODS_REGEX = /\./g;
+
+ function accumulateQueryParamDescriptors(_desc, accum) {
+ var desc = _desc, tmp;
+ if (typeOf(desc) === 'string') {
+ tmp = {};
+ tmp[desc] = { as: null };
+ desc = tmp;
+ }
+
+ for (var key in desc) {
+ if (!desc.hasOwnProperty(key)) { return; }
+
+ var singleDesc = desc[key];
+ if (typeOf(singleDesc) === 'string') {
+ singleDesc = { as: singleDesc };
+ }
+
+ tmp = accum[key] || { as: null, scope: 'model' };
+ merge(tmp, singleDesc);
+
+ accum[key] = tmp;
+ }
+ }
+
+ function listenForQueryParamChanges(controller) {
+ var qpMap = get(controller, '_normalizedQueryParams');
+ for (var prop in qpMap) {
+ if (!qpMap.hasOwnProperty(prop)) { continue; }
+ controller.addObserver(prop + '.[]', controller, controller._qpChanged);
+ }
+ }
+
+
+ __exports__["default"] = ControllerMixin;
+ });
+enifed("ember-routing/ext/run_loop",
+ ["ember-metal/run_loop"],
+ function(__dependency1__) {
+ "use strict";
+ var run = __dependency1__["default"];
+
+ /**
+ @module ember
+ @submodule ember-views
+ */
+
+ // Add a new named queue after the 'actions' queue (where RSVP promises
+ // resolve), which is used in router transitions to prevent unnecessary
+ // loading state entry if all context promises resolve on the
+ // 'actions' queue first.
+
+ var queues = run.queues;
+ run._addQueue('routerTransitions', 'actions');
+ });
+enifed("ember-routing/ext/view",
+ ["ember-metal/property_get","ember-metal/property_set","ember-metal/run_loop","ember-views/views/view","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
+ "use strict";
+ var get = __dependency1__.get;
+ var set = __dependency2__.set;
+ var run = __dependency3__["default"];
+ var EmberView = __dependency4__["default"];
+
+ /**
+ @module ember
+ @submodule ember-routing
+ */
+
+ EmberView.reopen({
+
+ /**
+ Sets the private `_outlets` object on the view.
+
+ @method init
+ */
+ init: function() {
+ set(this, '_outlets', {});
+ this._super();
+ },
+
+ /**
+ Manually fill any of a view's `{{outlet}}` areas with the
+ supplied view.
+
+ Example
+
+ ```javascript
+ var MyView = Ember.View.extend({
+ template: Ember.Handlebars.compile('Child view: {{outlet "main"}} ')
+ });
+ var myView = MyView.create();
+ myView.appendTo('body');
+ // The html for myView now looks like:
+ // {{title}}+{{body}}
+ {{render "author" author}}
+ Child view:
+
+ var FooView = Ember.View.extend({
+ template: Ember.Handlebars.compile('Foo') + }); + var fooView = FooView.create(); + myView.connectOutlet('main', fooView); + // The html for myView now looks like: + //Child view:
+ //
+ ```
+ @method connectOutlet
+ @param {String} outletName A unique name for the outlet
+ @param {Object} view An Ember.View
+ */
+ connectOutlet: function(outletName, view) {
+ if (this._pendingDisconnections) {
+ delete this._pendingDisconnections[outletName];
+ }
+
+ if (this._hasEquivalentView(outletName, view)) {
+ view.destroy();
+ return;
+ }
+
+ var outlets = get(this, '_outlets');
+ var container = get(this, 'container');
+ var router = container && container.lookup('router:main');
+ var renderedName = get(view, 'renderedName');
+
+ set(outlets, outletName, view);
+
+ if (router && renderedName) {
+ router._connectActiveView(renderedName, view);
+ }
+ },
+
+ /**
+ Determines if the view has already been created by checking if
+ the view has the same constructor, template, and context as the
+ view in the `_outlets` object.
+
+ @private
+ @method _hasEquivalentView
+ @param {String} outletName The name of the outlet we are checking
+ @param {Object} view An Ember.View
+ @return {Boolean}
+ */
+ _hasEquivalentView: function(outletName, view) {
+ var existingView = get(this, '_outlets.'+outletName);
+ return existingView &&
+ existingView.constructor === view.constructor &&
+ existingView.get('template') === view.get('template') &&
+ existingView.get('context') === view.get('context');
+ },
+
+ /**
+ Removes an outlet from the view.
+
+ Example
+
+ ```javascript
+ var MyView = Ember.View.extend({
+ template: Ember.Handlebars.compile('Child view: {{outlet "main"}} ')
+ });
+ var myView = MyView.create();
+ myView.appendTo('body');
+ // myView's html:
+ // FooChild view:
+
+ var FooView = Ember.View.extend({
+ template: Ember.Handlebars.compile('Foo') + }); + var fooView = FooView.create(); + myView.connectOutlet('main', fooView); + // myView's html: + //Child view:
+ //
+
+ myView.disconnectOutlet('main');
+ // myView's html:
+ // FooChild view:
+ ```
+
+ @method disconnectOutlet
+ @param {String} outletName The name of the outlet to be removed
+ */
+ disconnectOutlet: function(outletName) {
+ if (!this._pendingDisconnections) {
+ this._pendingDisconnections = {};
+ }
+ this._pendingDisconnections[outletName] = true;
+ run.once(this, '_finishDisconnections');
+ },
+
+ /**
+ Gets an outlet that is pending disconnection and then
+ nullifys the object on the `_outlet` object.
+
+ @private
+ @method _finishDisconnections
+ */
+ _finishDisconnections: function() {
+ if (this.isDestroyed) return; // _outlets will be gone anyway
+ var outlets = get(this, '_outlets');
+ var pendingDisconnections = this._pendingDisconnections;
+ this._pendingDisconnections = null;
+
+ for (var outletName in pendingDisconnections) {
+ set(outlets, outletName, null);
+ }
+ }
+ });
+
+ __exports__["default"] = EmberView;
+ });
+enifed("ember-routing/location/api",
+ ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
+ "use strict";
+ var Ember = __dependency1__["default"];
+ // deprecate, assert
+ var get = __dependency2__.get;
+ var set = __dependency3__.set;
+
+ /**
+ @module ember
+ @submodule ember-routing
+ */
+
+ /**
+ Ember.Location returns an instance of the correct implementation of
+ the `location` API.
+
+ ## Implementations
+
+ You can pass an implementation name (`hash`, `history`, `none`) to force a
+ particular implementation to be used in your application.
+
+ ### HashLocation
+
+ Using `HashLocation` results in URLs with a `#` (hash sign) separating the
+ server side URL portion of the URL from the portion that is used by Ember.
+ This relies upon the `hashchange` event existing in the browser.
+
+ Example:
+
+ ```javascript
+ App.Router.map(function() {
+ this.resource('posts', function() {
+ this.route('new');
+ });
+ });
+
+ App.Router.reopen({
+ location: 'hash'
+ });
+ ```
+
+ This will result in a posts.new url of `/#/posts/new`.
+
+ ### HistoryLocation
+
+ Using `HistoryLocation` results in URLs that are indistinguishable from a
+ standard URL. This relies upon the browser's `history` API.
+
+ Example:
+
+ ```javascript
+ App.Router.map(function() {
+ this.resource('posts', function() {
+ this.route('new');
+ });
+ });
+
+ App.Router.reopen({
+ location: 'history'
+ });
+ ```
+
+ This will result in a posts.new url of `/posts/new`.
+
+ Keep in mind that your server must serve the Ember app at all the routes you
+ define.
+
+ ### AutoLocation
+
+ Using `AutoLocation`, the router will use the best Location class supported by
+ the browser it is running in.
+
+ Browsers that support the `history` API will use `HistoryLocation`, those that
+ do not, but still support the `hashchange` event will use `HashLocation`, and
+ in the rare case neither is supported will use `NoneLocation`.
+
+ Example:
+
+ ```javascript
+ App.Router.map(function() {
+ this.resource('posts', function() {
+ this.route('new');
+ });
+ });
+
+ App.Router.reopen({
+ location: 'auto'
+ });
+ ```
+
+ This will result in a posts.new url of `/posts/new` for modern browsers that
+ support the `history` api or `/#/posts/new` for older ones, like Internet
+ Explorer 9 and below.
+
+ When a user visits a link to your application, they will be automatically
+ upgraded or downgraded to the appropriate `Location` class, with the URL
+ transformed accordingly, if needed.
+
+ Keep in mind that since some of your users will use `HistoryLocation`, your
+ server must serve the Ember app at all the routes you define.
+
+ ### NoneLocation
+
+ Using `NoneLocation` causes Ember to not store the applications URL state
+ in the actual URL. This is generally used for testing purposes, and is one
+ of the changes made when calling `App.setupForTesting()`.
+
+ ## Location API
+
+ Each location implementation must provide the following methods:
+
+ * implementation: returns the string name used to reference the implementation.
+ * getURL: returns the current URL.
+ * setURL(path): sets the current URL.
+ * replaceURL(path): replace the current URL (optional).
+ * onUpdateURL(callback): triggers the callback when the URL changes.
+ * formatURL(url): formats `url` to be placed into `href` attribute.
+
+ Calling setURL or replaceURL will not trigger onUpdateURL callbacks.
+
+ @class Location
+ @namespace Ember
+ @static
+ */
+ __exports__["default"] = {
+ /**
+ This is deprecated in favor of using the container to lookup the location
+ implementation as desired.
+
+ For example:
+
```javascript
- var deferred = RSVP.defer();
+ // Given a location registered as follows:
+ container.register('location:history-test', HistoryTestLocation);
- deferred.resolve("Success!");
+ // You could create a new instance via:
+ container.lookup('location:history-test');
+ ```
- deferred.promise.then(function(value){
- // value here is "Success!"
+ @method create
+ @param {Object} options
+ @return {Object} an instance of an implementation of the `location` API
+ @deprecated Use the container to lookup the location implementation that you
+ need.
+ */
+ create: function(options) {
+ var implementation = options && options.implementation;
+ Ember.assert("Ember.Location.create: you must specify a 'implementation' option", !!implementation);
+
+ var implementationClass = this.implementations[implementation];
+ Ember.assert("Ember.Location.create: " + implementation + " is not a valid implementation", !!implementationClass);
+
+ return implementationClass.create.apply(implementationClass, arguments);
+ },
+
+ /**
+ This is deprecated in favor of using the container to register the
+ location implementation as desired.
+
+ Example:
+
+ ```javascript
+ Application.initializer({
+ name: "history-test-location",
+
+ initialize: function(container, application) {
+ application.register('location:history-test', HistoryTestLocation);
+ }
});
```
- @method defer
- @for RSVP
- @param {String} label optional string for labeling the promise.
- Useful for tooling.
- @return {Object}
- */
+ @method registerImplementation
+ @param {String} name
+ @param {Object} implementation of the `location` API
+ @deprecated Register your custom location implementation with the
+ container directly.
+ */
+ registerImplementation: function(name, implementation) {
+ Ember.deprecate('Using the Ember.Location.registerImplementation is no longer supported. Register your custom location implementation with the container instead.', false);
- __exports__["default"] = function defer(label) {
- var deferred = { };
+ this.implementations[name] = implementation;
+ },
- deferred.promise = new Promise(function(resolve, reject) {
- deferred.resolve = resolve;
- deferred.reject = reject;
- }, label);
+ implementations: {},
+ _location: window.location,
- return deferred;
+ /**
+ Returns the current `location.hash` by parsing location.href since browsers
+ inconsistently URL-decode `location.hash`.
+
+ https://bugzilla.mozilla.org/show_bug.cgi?id=483304
+
+ @private
+ @method getHash
+ @since 1.4.0
+ */
+ _getHash: function () {
+ // AutoLocation has it at _location, HashLocation at .location.
+ // Being nice and not changing
+ var href = (this._location || this.location).href;
+ var hashIndex = href.indexOf('#');
+
+ if (hashIndex === -1) {
+ return '';
+ } else {
+ return href.substr(hashIndex);
+ }
+ }
};
});
-define("rsvp/events",
- ["exports"],
- function(__exports__) {
+enifed("ember-routing/location/auto_location",
+ ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-routing/location/api","ember-routing/location/history_location","ember-routing/location/hash_location","ember-routing/location/none_location","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) {
"use strict";
- var indexOf = function(callbacks, callback) {
- for (var i=0, l=callbacks.length; i
+ {{outlet "anOutletName"}}
+
+ ```
+
+ ```handlebars
+
+ Photos+ ``` + + You can render `photos.hbs` into the `"anOutletName"` outlet of + `application.hbs` by calling `render`: + + ```js + // posts route + Ember.Route.extend({ + renderTemplate: function(){ + this.render('photos', { + into: 'application', + outlet: 'anOutletName' + }) + } + }); + ``` + + `render` additionally allows you to supply which `view`, `controller`, and + `model` objects should be loaded and associated with the rendered template. + + + ```js + // posts route + Ember.Route.extend({ + renderTemplate: function(controller, model){ + this.render('posts', { // the template to render, referenced by name + into: 'application', // the template to render into, referenced by name + outlet: 'anOutletName', // the outlet inside `options.template` to render into. + view: 'aViewName', // the view to use for this template, referenced by name + controller: 'someControllerName', // the controller to use for this template, referenced by name + model: model // the model to set on `options.controller`. + }) + } + }); + ``` + + The string values provided for the template name, view, and controller + will eventually pass through to the resolver for lookup. See + Ember.Resolver for how these are mapped to JavaScript objects in your + application. + + Not all options need to be passed to `render`. Default values will be used + based on the name of the route specified in the router or the Route's + `controllerName`, `viewName` and and `templateName` properties. + + For example: + + ```js + // router + Router.map(function() { + this.route('index'); + this.resource('post', {path: '/posts/:post_id'}); + }); + ``` + + ```js + // post route + PostRoute = App.Route.extend({ + renderTemplate: function() { + this.render(); // all defaults apply + } + }); + ``` + + The name of the `PostRoute`, defined by the router, is `post`. + + The following equivalent default options will be applied when + the Route calls `render`: + + ```js + // + this.render('post', { // the template name associated with 'post' Route + into: 'application', // the parent route to 'post' Route + outlet: 'main', // {{outlet}} and {{outlet 'main' are synonymous}}, + view: 'post', // the view associated with the 'post' Route + controller: 'post', // the controller associated with the 'post' Route + }) + ``` + + By default the controller's `model` will be the route's model, so it does not + need to be passed unless you wish to change which model is being used. + + @method render + @param {String} name the name of the template to render + @param {Object} [options] the options + @param {String} [options.into] the template to render into, + referenced by name. Defaults to the parent template + @param {String} [options.outlet] the outlet inside `options.template` to render into. + Defaults to 'main' + @param {String} [options.controller] the controller to use for this template, + referenced by name. Defaults to the Route's paired controller + @param {String} [options.model] the model object to set on `options.controller` + Defaults to the return value of the Route's model hook + */ + render: function(name, options) { + Ember.assert("The name in the given arguments is undefined", arguments.length > 0 ? !isNone(arguments[0]) : true); + + var namePassed = typeof name === 'string' && !!name; + + if (typeof name === 'object' && !options) { + options = name; + name = this.routeName; + } + + options = options || {}; + options.namePassed = namePassed; + + var templateName; + + if (name) { + name = name.replace(/\//g, '.'); + templateName = name; + } else { + name = this.routeName; + templateName = this.templateName || name; + } + + var viewName = options.view || namePassed && name || this.viewName || name; + + var container = this.container; + var view = container.lookup('view:' + viewName); + var template = view ? view.get('template') : null; + + if (!template) { + template = container.lookup('template:' + templateName); + } + + if (!view && !template) { + Ember.assert("Could not find \"" + name + "\" template or view.", Ember.isEmpty(arguments[0])); + if (get(this.router, 'namespace.LOG_VIEW_LOOKUPS')) { + Ember.Logger.info("Could not find \"" + name + "\" template or view. Nothing will be rendered", { fullName: 'template:' + name }); + } + return; + } + + options = normalizeOptions(this, name, template, options); + view = setupView(view, container, options); + + if (options.outlet === 'main') { this.lastRenderedTemplate = name; } + + appendView(this, view, options); + }, + + /** + Disconnects a view that has been rendered into an outlet. + + You may pass any or all of the following options to `disconnectOutlet`: + + * `outlet`: the name of the outlet to clear (default: 'main') + * `parentView`: the name of the view containing the outlet to clear + (default: the view rendered by the parent route) + + Example: + + ```js + App.ApplicationRoute = App.Route.extend({ + actions: { + showModal: function(evt) { + this.render(evt.modalName, { + outlet: 'modal', + into: 'application' + }); + }, + hideModal: function(evt) { + this.disconnectOutlet({ + outlet: 'modal', + parentView: 'application' + }); + } + } + }); + ``` + + Alternatively, you can pass the `outlet` name directly as a string. + + Example: + + ```js + hideModal: function(evt) { + this.disconnectOutlet('modal'); + } + ``` + + @method disconnectOutlet + @param {Object|String} options the options hash or outlet name + */ + disconnectOutlet: function(options) { + if (!options || typeof options === "string") { + var outletName = options; + options = {}; + options.outlet = outletName; + } + options.parentView = options.parentView ? options.parentView.replace(/\//g, '.') : parentTemplate(this); + options.outlet = options.outlet || 'main'; + + var parentView = this.router._lookupActiveView(options.parentView); + if (parentView) { parentView.disconnectOutlet(options.outlet); } + }, + + willDestroy: function() { + this.teardownViews(); + }, + + /** + @private + + @method teardownViews + */ + teardownViews: function() { + // Tear down the top level view + if (this.teardownTopLevelView) { this.teardownTopLevelView(); } + + // Tear down any outlets rendered with 'into' + var teardownOutletViews = this.teardownOutletViews || []; + forEach(teardownOutletViews, function(teardownOutletView) { + teardownOutletView(); + }); + + delete this.teardownTopLevelView; + delete this.teardownOutletViews; + delete this.lastRenderedTemplate; + } + }); + + var defaultQPMeta = { + qps: [], + map: {}, + states: {} + }; + + function parentRoute(route) { + var handlerInfo = handlerInfoFor(route, route.router.router.state.handlerInfos, -1); + return handlerInfo && handlerInfo.handler; + } + + function handlerInfoFor(route, handlerInfos, _offset) { + if (!handlerInfos) { return; } + + var offset = _offset || 0, current; + for (var i=0, l=handlerInfos.length; i
- {{outputWrittenWords}}
-
- ```
-
- Would result in the following HTML:
-
- ```html
-
- Lots of text that IS bound
-
- ```
-
- Finally, this example really shows the power and ease of Ember when two
- properties are bound to eachother via `Ember.computed.alias`. Type into
- either text area box and they'll both stay in sync. Note that
- `Ember.computed.alias` costs more in terms of performance, so only use it when
- your really binding in both directions:
-
- ```javascript
- App.ApplicationController = Ember.Controller.extend({
- writtenWords: "Lots of text that IS bound",
- twoWayWrittenWords: Ember.computed.alias("writtenWords")
- });
- ```
-
- ```handlebars
- {{textarea value=writtenWords}}
- {{textarea value=twoWayWrittenWords}}
- ```
-
- ```html
-
-
- ```
-
- `{{with}}` can be our best friend in these cases,
- instead of writing `user.role.*` over and over, we use `{{#with user.role}}`.
- Now the context within the `{{#with}} .. {{/with}}` block is `user.role` so you can do the following:
-
- ```handlebars
- {{user.role.label}}- {{user.role.id}} - -{{user.role.description}} -{{user.name}}- -
- {{#with user.role}}
-
- ```
-
- ### `as` operator
-
- This operator aliases the scope to a new name. It's helpful for semantic clarity and to retain
- default scope or to reference from another `{{with}}` block.
-
- ```handlebars
- // posts might not be
- {{#with user.posts as blogPosts}}
- {{label}}- {{id}} - -{{description}} - {{/with}} -
- There are {{blogPosts.length}} blog posts written by {{user.name}}.
-
-
- {{#each post in blogPosts}}
- ![]()
-
- ```
-
- ### Blockless use in a collection
-
- If you provide an `itemViewClass` option that has its own `template` you can
- omit the block.
-
- The following template:
-
- ```handlebars
- {{collection contentBinding="App.items" itemViewClass="App.AnItemView"}}
- ```
-
- And application code
-
- ```javascript
- App = Ember.Application.create();
- App.items = [
- Ember.Object.create({name: 'Dave'}),
- Ember.Object.create({name: 'Mary'}),
- Ember.Object.create({name: 'Sara'})
- ];
-
- App.AnItemView = Ember.View.extend({
- template: Ember.Handlebars.compile("Greetings {{view.content.name}}")
- });
- ```
-
- Will result in the HTML structure below
-
- ```html
- Hi Dave
- Hi Mary
- Hi Sara
-
-
- ```
-
- ### Specifying a CollectionView subclass
-
- By default the `{{collection}}` helper will create an instance of
- `Ember.CollectionView`. You can supply a `Ember.CollectionView` subclass to
- the helper by passing it as the first argument:
-
- ```handlebars
- {{#collection App.MyCustomCollectionClass contentBinding="App.items"}}
- Hi {{view.content.name}}
- {{/collection}}
- ```
-
- ### Forwarded `item.*`-named Options
-
- As with the `{{view}}`, helper options passed to the `{{collection}}` will be
- set on the resulting `Ember.CollectionView` as properties. Additionally,
- options prefixed with `item` will be applied to the views rendered for each
- item (note the camelcasing):
-
- ```handlebars
- {{#collection contentBinding="App.items"
- itemTagName="p"
- itemClassNames="greeting"}}
- Howdy {{view.content.name}}
- {{/collection}}
- ```
-
- Will result in the following HTML structure:
-
- ```html
- Greetings Dave
- Greetings Mary
- Greetings Sara
-
-
- ```
-
- @method collection
- @for Ember.Handlebars.helpers
- @param {String} path
- @param {Hash} options
- @return {String} HTML string
- @deprecated Use `{{each}}` helper instead.
- */
- function collectionHelper(path, options) {
-
- // If no path is provided, treat path param as options.
- if (path && path.data && path.data.isRenderData) {
- options = path;
- path = undefined;
- } else {
- }
-
- var fn = options.fn;
- var data = options.data;
- var inverse = options.inverse;
- var view = options.data.view;
-
-
- var controller, container;
- // If passed a path string, convert that into an object.
- // Otherwise, just default to the standard class.
- var collectionClass;
- if (path) {
- controller = data.keywords.controller;
- container = controller && controller.container;
- collectionClass = handlebarsGet(this, path, options) || container.lookupFactory('view:' + path);
- }
- else {
- collectionClass = CollectionView;
- }
-
- var hash = options.hash, itemHash = {}, match;
-
- // Extract item view class if provided else default to the standard class
- var collectionPrototype = collectionClass.proto(), itemViewClass;
-
- if (hash.itemView) {
- controller = data.keywords.controller;
- container = controller.container;
- itemViewClass = container.lookupFactory('view:' + hash.itemView);
- } else if (hash.itemViewClass) {
- itemViewClass = handlebarsGet(collectionPrototype, hash.itemViewClass, options);
- } else {
- itemViewClass = collectionPrototype.itemViewClass;
- }
-
-
- delete hash.itemViewClass;
- delete hash.itemView;
-
- // Go through options passed to the {{collection}} helper and extract options
- // that configure item views instead of the collection itself.
- for (var prop in hash) {
- if (hash.hasOwnProperty(prop)) {
- match = prop.match(/^item(.)(.*)$/);
-
- if (match && prop !== 'itemController') {
- // Convert itemShouldFoo -> shouldFoo
- itemHash[match[1].toLowerCase() + match[2]] = hash[prop];
- // Delete from hash as this will end up getting passed to the
- // {{view}} helper method.
- delete hash[prop];
- }
- }
- }
-
- if (fn) {
- itemHash.template = fn;
- delete options.fn;
- }
-
- var emptyViewClass;
- if (inverse && inverse !== EmberHandlebars.VM.noop) {
- emptyViewClass = get(collectionPrototype, 'emptyViewClass');
- emptyViewClass = emptyViewClass.extend({
- template: inverse,
- tagName: itemHash.tagName
- });
- } else if (hash.emptyViewClass) {
- emptyViewClass = handlebarsGet(this, hash.emptyViewClass, options);
- }
- if (emptyViewClass) { hash.emptyView = emptyViewClass; }
-
- if (hash.keyword) {
- itemHash._context = this;
- } else {
- itemHash._context = alias('content');
- }
-
- var viewOptions = ViewHelper.propertiesFromHTMLOptions({ data: data, hash: itemHash }, this);
- hash.itemViewClass = itemViewClass.extend(viewOptions);
-
- options.helperName = options.helperName || 'collection';
-
- return helpers.view.call(this, collectionClass, options);
- }
-
- __exports__["default"] = collectionHelper;
- });
-define("ember-handlebars/helpers/debug",
- ["ember-metal/core","ember-metal/utils","ember-metal/logger","ember-metal/property_get","ember-handlebars/ext","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) {
- "use strict";
- /*jshint debug:true*/
-
- /**
- @module ember
- @submodule ember-handlebars
- */
- var Ember = __dependency1__["default"];
- // Ember.FEATURES,
- var inspect = __dependency2__.inspect;
- var Logger = __dependency3__["default"];
-
- var get = __dependency4__.get;
- var normalizePath = __dependency5__.normalizePath;
- var handlebarsGet = __dependency5__.handlebarsGet;
-
- var a_slice = [].slice;
-
- /**
- `log` allows you to output the value of variables in the current rendering
- context. `log` also accepts primitive types such as strings or numbers.
-
- ```handlebars
- {{log "myVariable:" myVariable }}
- ```
-
- @method log
- @for Ember.Handlebars.helpers
- @param {String} property
- */
- function logHelper() {
- var params = a_slice.call(arguments, 0, -1),
- options = arguments[arguments.length - 1],
- logger = Logger.log,
- values = [],
- allowPrimitives = true;
-
- for (var i = 0; i < params.length; i++) {
- var type = options.types[i];
-
- if (type === 'ID' || !allowPrimitives) {
- var context = (options.contexts && options.contexts[i]) || this,
- normalized = normalizePath(context, params[i], options.data);
-
- if (normalized.path === 'this') {
- values.push(normalized.root);
- } else {
- values.push(handlebarsGet(normalized.root, normalized.path, options));
- }
- } else {
- values.push(params[i]);
- }
- }
-
- logger.apply(logger, values);
- };
-
- /**
- Execute the `debugger` statement in the current context.
-
- ```handlebars
- {{debugger}}
- ```
-
- Before invoking the `debugger` statement, there
- are a few helpful variables defined in the
- body of this helper that you can inspect while
- debugging that describe how and where this
- helper was invoked:
-
- - templateContext: this is most likely a controller
- from which this template looks up / displays properties
- - typeOfTemplateContext: a string description of
- what the templateContext is
-
- For example, if you're wondering why a value `{{foo}}`
- isn't rendering as expected within a template, you
- could place a `{{debugger}}` statement, and when
- the `debugger;` breakpoint is hit, you can inspect
- `templateContext`, determine if it's the object you
- expect, and/or evaluate expressions in the console
- to perform property lookups on the `templateContext`:
-
- ```
- > templateContext.get('foo') // -> "Howdy Dave -Howdy Mary -Howdy Sara -Sorry, nobody is available for this task. - {{/each}} - ``` - ### Specifying a View class for items - If you provide an `itemViewClass` option that references a view class - with its own `template` you can omit the block. - - The following template: - - ```handlebars - {{#view App.MyView }} - {{each view.items itemViewClass="App.AnItemView"}} - {{/view}} - ``` - - And application code - - ```javascript - App = Ember.Application.create({ - MyView: Ember.View.extend({ - items: [ - Ember.Object.create({name: 'Dave'}), - Ember.Object.create({name: 'Mary'}), - Ember.Object.create({name: 'Sara'}) - ] - }) - }); - - App.AnItemView = Ember.View.extend({ - template: Ember.Handlebars.compile("Greetings {{name}}") - }); - ``` - - Will result in the HTML structure below - - ```html -
-
- ```
-
- If an `itemViewClass` is defined on the helper, and therefore the helper is not
- being used as a block, an `emptyViewClass` can also be provided optionally.
- The `emptyViewClass` will match the behavior of the `{{else}}` condition
- described above. That is, the `emptyViewClass` will render if the collection
- is empty.
-
- ### Representing each item with a Controller.
- By default the controller lookup within an `{{#each}}` block will be
- the controller of the template where the `{{#each}}` was used. If each
- item needs to be presented by a custom controller you can provide a
- `itemController` option which references a controller by lookup name.
- Each item in the loop will be wrapped in an instance of this controller
- and the item itself will be set to the `content` property of that controller.
-
- This is useful in cases where properties of model objects need transformation
- or synthesis for display:
-
- ```javascript
- App.DeveloperController = Ember.ObjectController.extend({
- isAvailableForHire: function() {
- return !this.get('content.isEmployed') && this.get('content.isSeekingWork');
- }.property('isEmployed', 'isSeekingWork')
- })
- ```
-
- ```handlebars
- {{#each person in developers itemController="developer"}}
- {{person.name}} {{#if person.isAvailableForHire}}Hire me!{{/if}}
- {{/each}}
- ```
-
- Each itemController will receive a reference to the current controller as
- a `parentController` property.
-
- ### (Experimental) Grouped Each
-
- When used in conjunction with the experimental [group helper](https://github.com/emberjs/group-helper),
- you can inform Handlebars to re-render an entire group of items instead of
- re-rendering them one at a time (in the event that they are changed en masse
- or an item is added/removed).
-
- ```handlebars
- {{#group}}
- {{#each people}}
- {{firstName}} {{lastName}}
- {{/each}}
- {{/group}}
- ```
-
- This can be faster than the normal way that Handlebars re-renders items
- in some cases.
-
- If for some reason you have a group with more than one `#each`, you can make
- one of the collections be updated in normal (non-grouped) fashion by setting
- the option `groupedRows=true` (counter-intuitive, I know).
-
- For example,
-
- ```handlebars
- {{dealershipName}}
-
- {{#group}}
- {{#each dealers}}
- {{firstName}} {{lastName}}
- {{/each}}
-
- {{#each car in cars groupedRows=true}}
- {{car.make}} {{car.model}} {{car.color}}
- {{/each}}
- {{/group}}
- ```
- Any change to `dealershipName` or the `dealers` collection will cause the
- entire group to be re-rendered. However, changes to the `cars` collection
- will be re-rendered individually (as normal).
-
- Note that `group` behavior is also disabled by specifying an `itemViewClass`.
-
- @method each
- @for Ember.Handlebars.helpers
- @param [name] {String} name for item (used with `in`)
- @param [path] {String} path
- @param [options] {Object} Handlebars key/value pairs of options
- @param [options.itemViewClass] {String} a path to a view class used for each item
- @param [options.itemController] {String} name of a controller to be created for each item
- @param [options.groupedRows] {boolean} enable normal item-by-item rendering when inside a `#group` helper
- */
- function eachHelper(path, options) {
- var ctx, helperName = 'each';
-
- if (arguments.length === 4) {
-
- var keywordName = arguments[0];
-
-
- options = arguments[3];
- path = arguments[2];
-
- helperName += ' ' + keywordName + ' in ' + path;
-
- if (path === '') { path = "this"; }
-
- options.hash.keyword = keywordName;
-
- } else if (arguments.length === 1) {
- options = path;
- path = 'this';
- } else {
- helperName += ' ' + path;
- }
-
- options.hash.dataSourceBinding = path;
- // Set up emptyView as a metamorph with no tag
- //options.hash.emptyViewClass = Ember._MetamorphView;
-
- // can't rely on this default behavior when use strict
- ctx = this || window;
-
- options.helperName = options.helperName || helperName;
-
- if (options.data.insideGroup && !options.hash.groupedRows && !options.hash.itemViewClass) {
- new GroupedEach(ctx, path, options).render();
- } else {
- // ES6TODO: figure out how to do this without global lookup.
- return helpers.collection.call(ctx, 'Ember.Handlebars.EachView', options);
- }
- }
-
- __exports__.EachView = EachView;
- __exports__.GroupedEach = GroupedEach;
- __exports__.eachHelper = eachHelper;
- });
-define("ember-handlebars/helpers/loc",
- ["ember-runtime/system/string","exports"],
- function(__dependency1__, __exports__) {
- "use strict";
- var loc = __dependency1__.loc;
-
- /**
- @module ember
- @submodule ember-handlebars
- */
-
- // ES6TODO:
- // Pretty sure this can be expressed as
- // var locHelper EmberStringUtils.loc ?
-
- /**
- Calls [Ember.String.loc](/api/classes/Ember.String.html#method_loc) with the
- provided string.
-
- This is a convenient way to localize text. For example:
-
- ```html
-
- ```
-
- Take note that `"welcome"` is a string and not an object
- reference.
-
- See [Ember.String.loc](/api/classes/Ember.String.html#method_loc) for how to
- set up localized string references.
-
- @method loc
- @for Ember.Handlebars.helpers
- @param {String} str The string to format
- @see {Ember.String#loc}
- */
- function locHelper(str) {
- return loc(str);
- }
-
- __exports__["default"] = locHelper;
- });
-define("ember-handlebars/helpers/partial",
- ["ember-metal/core","ember-metal/is_none","ember-handlebars/ext","ember-handlebars/helpers/binding","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
- "use strict";
- var Ember = __dependency1__["default"];
- // Ember.assert
- // var emberAssert = Ember.assert;
-
- var isNone = __dependency2__.isNone;
- var handlebarsGet = __dependency3__.handlebarsGet;
- var bind = __dependency4__.bind;
-
- /**
- @module ember
- @submodule ember-handlebars
- */
-
- /**
- The `partial` helper renders another template without
- changing the template context:
-
- ```handlebars
- {{foo}}
- {{partial "nav"}}
- ```
-
- The above example template will render a template named
- "_nav", which has the same context as the parent template
- it's rendered into, so if the "_nav" template also referenced
- `{{foo}}`, it would print the same thing as the `{{foo}}`
- in the above example.
-
- If a "_nav" template isn't found, the `partial` helper will
- fall back to a template named "nav".
-
- ## Bound template names
-
- The parameter supplied to `partial` can also be a path
- to a property containing a template name, e.g.:
-
- ```handlebars
- {{partial someTemplateName}}
- ```
-
- The above example will look up the value of `someTemplateName`
- on the template context (e.g. a controller) and use that
- value as the name of the template to render. If the resolved
- value is falsy, nothing will be rendered. If `someTemplateName`
- changes, the partial will be re-rendered using the new template
- name.
-
- ## Setting the partial's context with `with`
-
- The `partial` helper can be used in conjunction with the `with`
- helper to set a context that will be used by the partial:
-
- ```handlebars
- {{#with currentUser}}
- {{partial "user_info"}}
- {{/with}}
- ```
-
- @method partial
- @for Ember.Handlebars.helpers
- @param {String} partialName the name of the template to render minus the leading underscore
- */
-
- function partialHelper(name, options) {
-
- var context = (options.contexts && options.contexts.length) ? options.contexts[0] : this;
-
- options.helperName = options.helperName || 'partial';
-
- if (options.types[0] === "ID") {
- // Helper was passed a property path; we need to
- // create a binding that will re-render whenever
- // this property changes.
- options.fn = function(context, fnOptions) {
- var partialName = handlebarsGet(context, name, fnOptions);
- renderPartial(context, partialName, fnOptions);
- };
-
- return bind.call(context, name, options, true, exists);
- } else {
- // Render the partial right into parent template.
- renderPartial(context, name, options);
- }
- }
-
- function exists(value) {
- return !isNone(value);
- }
-
- function renderPartial(context, name, options) {
- var nameParts = name.split("/");
- var lastPart = nameParts[nameParts.length - 1];
-
- nameParts[nameParts.length - 1] = "_" + lastPart;
-
- var view = options.data.view;
- var underscoredName = nameParts.join("/");
- var template = view.templateForName(underscoredName);
- var deprecatedTemplate = !template && view.templateForName(name);
-
-
- template = template || deprecatedTemplate;
-
- template(context, { data: options.data });
- }
-
- __exports__["default"] = partialHelper;
- });
-define("ember-handlebars/helpers/shared",
- ["ember-handlebars/ext","exports"],
- function(__dependency1__, __exports__) {
- "use strict";
- var handlebarsGet = __dependency1__.handlebarsGet;
-
- function resolvePaths(options) {
- var ret = [],
- contexts = options.contexts,
- roots = options.roots,
- data = options.data;
-
- for (var i=0, l=contexts.length; iGreetings Dave
- Greetings Mary
- Greetings Sara
- |