diff --git a/.travis.yml b/.travis.yml index d4bdd2a7..c407e03b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,19 @@ language: ruby rvm: - 2.1.2 - + sudo: false cache: bundler: true +matrix: + allow_failures: + - rvm: 2.1.2 + env: 'TEST_SUITE=phantomjs' + - rvm: 2.1.2 + env: "TEST_SUITE=saucelabs BROWSER='firefox::Windows XP'" + env: global: - secure: "RFuCOppyjWHC4XWKtQlgS4zO4B6KVxytdX8+G5jRY3XM+OEGte8VDD88gZLM\nKDpkqMFDbNJAVTsh1kMANCTct2ONi30RTxuJWLtRyK7RE5zCcaGbAkTNZgXo\nOR5OWLEPJZbNfbh17H6J7izTy6yiLR+CsVP1wMgeVusP0eoDhCA=" diff --git a/assets/images/ui/activated.png b/assets/images/ui/activated.png new file mode 100644 index 00000000..450c6099 Binary files /dev/null and b/assets/images/ui/activated.png differ diff --git a/assets/images/ui/charmtab.png b/assets/images/ui/charmtab.png new file mode 100644 index 00000000..f30e92fb Binary files /dev/null and b/assets/images/ui/charmtab.png differ diff --git a/assets/images/ui/slider-closed.png b/assets/images/ui/slider-closed.png new file mode 100644 index 00000000..4f2ca670 Binary files /dev/null and b/assets/images/ui/slider-closed.png differ diff --git a/assets/images/ui/slider-open.png b/assets/images/ui/slider-open.png new file mode 100644 index 00000000..6a037b7b Binary files /dev/null and b/assets/images/ui/slider-open.png differ diff --git a/assets/scripts/app/app.coffee b/assets/scripts/app/app.coffee index d79fbb61..4fbe783c 100644 --- a/assets/scripts/app/app.coffee +++ b/assets/scripts/app/app.coffee @@ -145,3 +145,45 @@ unless window.TravisApplication currentDate: -> new Date() ) + + onUserUpdate: (user) -> + if Travis.config.pro + @identifyCustomer(user) + @subscribePusher(user) + @setupCharm(user) + + subscribePusher: (user) -> + channels = user.channels + channels = channels.map (channel) -> + if channel.match /^private-/ + channel + else + "private-#{channel}" + Travis.pusher.subscribeAll(channels) + + setupCharm: (user) -> + $.extend window.__CHARM, + customer: user.login, + customer_id: user.id, + email: user.email + + displayCharm: -> + __CHARM.show() + + identifyCustomer: (user) -> + if _cio && _cio.identify + _cio.identify + id: user.id + email: user.email + name: user.name + created_at: (Date.parse(user.created_at) / 1000) || null + login: user.login + + @on 'user:signed_in', (user) -> + Travis.onUserUpdate(user) + + @on 'user:synced', (user) -> + Travis.onUserUpdate(user) + + @_super.apply this, arguments + diff --git a/assets/scripts/app/auth.coffee b/assets/scripts/app/auth.coffee index 613c44d7..ba6f6cef 100644 --- a/assets/scripts/app/auth.coffee +++ b/assets/scripts/app/auth.coffee @@ -46,7 +46,11 @@ window.Auth = Ember.Object.extend null validateUser: (user) -> - @validateHas('id', user) && @validateHas('login', user) && @validateHas('token', user) && @validateHas('correct_scopes', user) && user.correct_scopes + fieldsToValidate = ['id', 'login', 'token', 'correct_scopes'] + if Travis.config.pro + fieldsToValidate.push 'channels' + + fieldsToValidate.every( (field) => @validateHas(field, user) ) && user.correct_scopes validateHas: (field, user) -> if user[field] diff --git a/assets/scripts/app/controllers.coffee b/assets/scripts/app/controllers.coffee index a8c9be07..c9653c51 100644 --- a/assets/scripts/app/controllers.coffee +++ b/assets/scripts/app/controllers.coffee @@ -25,20 +25,12 @@ Travis.TopController = Em.Controller.extend Travis.get('authState') == 'signing-in' ).property('Travis.authState') -Travis.ApplicationController = Em.Controller.extend - templateName: 'layouts/home' - - connectLayout: (name) -> - name = "layouts/#{name}" - if @get('templateName') != name - @set('templateName', name) - Travis.MainController = Em.Controller.extend() Travis.StatsLayoutController = Em.Controller.extend() Travis.ProfileLayoutController = Em.Controller.extend() Travis.AuthLayoutController = Em.Controller.extend() -Travis.ProfileInfoController = Em.Controller.extend +Travis.AccountsInfoController = Em.Controller.extend needs: ['currentUser', 'repos'] userBinding: 'controllers.currentUser' @@ -49,6 +41,33 @@ Travis.FirstSyncController = Em.Controller.extend isSyncing: Ember.computed.alias('user.isSyncing') Travis.IndexErrorController = Em.Controller.extend() +Travis.BuildsItemController = Em.ObjectController.extend(Travis.GithubUrlProperties) + +Travis.QueuesController = Em.ArrayController.extend + content: (-> + Travis.Job.queued() + ).property() + +Travis.RunningJobsController = Em.ArrayController.extend + content: (-> + Travis.Job.running() + ).property() + +Travis.SidebarController = Em.ArrayController.extend + init: -> + @_super.apply this, arguments + @tickables = [] + + tips: [ + "Did you know that you can parallelize tests on Travis CI? Learn more" + "Did you know that you can split a build into several smaller pieces? Learn more" + "Did you know that you can skip a build? Learn more" + ] + + tip: (-> + if tips = @get('tips') + tips[Math.floor(Math.random()*tips.length)] + ).property().volatile() require 'controllers/accounts' require 'controllers/auth' diff --git a/assets/scripts/app/controllers/account.coffee b/assets/scripts/app/controllers/account.coffee index 36fe2bae..182a2ab6 100644 --- a/assets/scripts/app/controllers/account.coffee +++ b/assets/scripts/app/controllers/account.coffee @@ -11,8 +11,12 @@ Travis.AccountController = Ember.ObjectController.extend self.reloadHooks() )) - toggle: (hook) -> - hook.toggle() + actions: + sync: -> + @get('user').sync() + + toggle: (hook) -> + hook.toggle() reloadHooks: -> if login = @get('login') @@ -35,8 +39,3 @@ Travis.AccountController = Ember.ObjectController.extend showPublicReposHint: (-> Travis.config.show_repos_hint == 'public' ) .property() - - actions: - sync: -> - @get('user').sync() - diff --git a/assets/scripts/app/controllers/build.coffee b/assets/scripts/app/controllers/build.coffee index 6e61ab6f..c8763418 100644 --- a/assets/scripts/app/controllers/build.coffee +++ b/assets/scripts/app/controllers/build.coffee @@ -1,4 +1,4 @@ -Travis.BuildController = Ember.Controller.extend +Travis.BuildController = Ember.Controller.extend Travis.GithubUrlProperties, needs: ['repo'] repoBinding: 'controllers.repo.repo' commitBinding: 'build.commit' @@ -10,7 +10,3 @@ Travis.BuildController = Ember.Controller.extend loading: (-> @get('build.isLoading') ).property('build.isLoading') - - urlGithubCommit: (-> - Travis.Urls.githubCommit(@get('repo.slug'), @get('commit.sha')) - ).property('repo.slug', 'commit.sha') diff --git a/assets/scripts/app/controllers/builds.coffee b/assets/scripts/app/controllers/builds.coffee index 7339c3f5..1ebd215e 100644 --- a/assets/scripts/app/controllers/builds.coffee +++ b/assets/scripts/app/controllers/builds.coffee @@ -1,4 +1,6 @@ Travis.BuildsController = Em.ArrayController.extend + isPullRequestsList: false + sortAscending: false sortProperties: ['number'] 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..8a10eaae 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: (-> @@ -30,10 +30,11 @@ Travis.FlashController = Ember.ArrayController.extend @get('flashes').unshiftObject(msg) Ember.run.later(this, (-> @get('flashes.content').removeObject(msg)), 15000) - close: (msg) -> - if msg instanceof Travis.Broadcast - msg.setSeen() - @notifyPropertyChange('unseenBroadcasts') - else - @get('flashes').removeObject(msg) + actions: + close: (msg) -> + if msg instanceof Travis.Broadcast + msg.setSeen() + @notifyPropertyChange('unseenBroadcasts') + else + @get('flashes').removeObject(msg) diff --git a/assets/scripts/app/controllers/job.coffee b/assets/scripts/app/controllers/job.coffee index 193aab42..5705e0d6 100644 --- a/assets/scripts/app/controllers/job.coffee +++ b/assets/scripts/app/controllers/job.coffee @@ -1,7 +1,6 @@ Travis.JobController = Em.Controller.extend needs: ['repo'] - jobBinding: 'controllers.repo.job' repoBinding: 'controllers.repo.repo' commitBinding: 'job.commit' annotationsBinding: 'job.annotations' diff --git a/assets/scripts/app/controllers/profile.coffee b/assets/scripts/app/controllers/profile.coffee index a2498206..298f3700 100644 --- a/assets/scripts/app/controllers/profile.coffee +++ b/assets/scripts/app/controllers/profile.coffee @@ -16,5 +16,14 @@ Travis.ProfileController = Travis.Controller.extend @connectTab('user') connectTab: (tab) -> - viewClass = Travis["#{$.camelize(tab)}View"] + if tab == 'user' + view = 'AccountsInfoView' + else + view = "#{$.camelize(tab)}View" + viewClass = Travis[view] @set('tab', tab) + + billingUrl: (-> + id = if @get('account.type') == 'user' then 'user' else @get('account.login') + "#{Travis.config.billing_endpoint}/subscriptions/#{id}" + ).property('account.login', 'account.type') diff --git a/assets/scripts/app/controllers/repo.coffee b/assets/scripts/app/controllers/repo.coffee index 4144b681..a2d73bf5 100644 --- a/assets/scripts/app/controllers/repo.coffee +++ b/assets/scripts/app/controllers/repo.coffee @@ -1,8 +1,9 @@ Travis.RepoController = Travis.Controller.extend - needs: ['repos', 'currentUser', 'build', 'request'] + needs: ['repos', 'currentUser', 'build', 'request', 'job'] currentUserBinding: 'controllers.currentUser' build: Ember.computed.alias('controllers.build.build') + job: Ember.computed.alias('controllers.job.job') request: Ember.computed.alias('controllers.request.model') slug: (-> @get('repo.slug') ).property('repo.slug') @@ -10,7 +11,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, -> @@ -23,6 +25,9 @@ Travis.RepoController = Travis.Controller.extend if build && jobs = build.get('jobs') jobs.forEach (j) -> j.updateTimes() + deactivate: -> + @stopObservingLastBuild() + activate: (action) -> @stopObservingLastBuild() this["view#{$.camelize(action)}"]() diff --git a/assets/scripts/app/controllers/repos.coffee b/assets/scripts/app/controllers/repos.coffee index 8bb5a377..7df3d16b 100644 --- a/assets/scripts/app/controllers/repos.coffee +++ b/assets/scripts/app/controllers/repos.coffee @@ -1,19 +1,9 @@ require 'travis/limited_array' Travis.ReposController = Ember.ArrayController.extend - defaultTab: ( -> - if @get('currentUser.id') - 'owned' - else - 'recent' - ).property('currentUser.id') - - currentUserIdDidChange: (-> - if @get('currentUser.id') - @activate('owned') - else if @get('tab') == 'owned' - @activate('recent') - ).observes('currentUser.id') + actions: + activate: (name) -> + @activate(name) tabOrIsLoadedDidChange: (-> @possiblyRedirectToGettingStartedPage() @@ -36,7 +26,8 @@ Travis.ReposController = Ember.ArrayController.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) recentRepos: (-> Ember.ArrayProxy.extend( @@ -53,13 +44,8 @@ Travis.ReposController = Ember.ArrayController.extend if content = @get('content') content.forEach (r) -> r.updateTimes() - transitionToRoot: -> - @container.lookup('router:main').send('renderDefaultTemplate') - @container.lookup('router:main').transitionTo('index.current') - activate: (tab, params) -> @set('sortProperties', ['sortOrder']) - tab ||= @get('defaultTab') @set('tab', tab) this["view#{$.camelize(tab)}"](params) @@ -76,22 +62,20 @@ Travis.ReposController = Ember.ArrayController.extend [] ).property('currentUser.login') - viewSearch: (params) -> - @set('content', Travis.Repo.search(params.search)) + viewSearch: (phrase) -> + @set('search', phrase) + @set('content', Travis.Repo.search(phrase)) searchObserver: (-> search = @get('search') if search @searchFor search - else - @activate 'recent' - 'recent' ).observes('search') searchFor: (phrase) -> Ember.run.cancel(@searchLater) if @searchLater @searchLater = Ember.run.later(this, (-> - @activate 'search', search: phrase + @transitionTo('index.search', phrase) ), 500) noReposMessage: (-> diff --git a/assets/scripts/app/controllers/request.coffee b/assets/scripts/app/controllers/request.coffee index e825c410..57d1d279 100644 --- a/assets/scripts/app/controllers/request.coffee +++ b/assets/scripts/app/controllers/request.coffee @@ -19,3 +19,13 @@ Travis.RequestController = Ember.ObjectController.extend else 'Rejected' ).property('isAccepted') + + message: (-> + message = @get('model.message') + if Travis.features.pro && message == "private repository" + '' + else + message + ).property('model.message') + + diff --git a/assets/scripts/app/controllers/settings.coffee b/assets/scripts/app/controllers/settings.coffee index e4e0e0bb..d090beea 100644 --- a/assets/scripts/app/controllers/settings.coffee +++ b/assets/scripts/app/controllers/settings.coffee @@ -12,6 +12,7 @@ Travis.SettingsIndexController = Em.ObjectController.extend @set('settings.maximum_number_of_builds_valid', 'invalid') ).observes('settings.maximum_number_of_builds') - save: -> - @get('model').saveSettings(@get('settings')).then null, -> - Travis.flash(error: 'There was an error while saving settings. Please try again.') + actions: + save: -> + @get('model').saveSettings(@get('settings')).then null, -> + Travis.flash(error: 'There was an error while saving settings. Please try again.') diff --git a/assets/scripts/app/helpers.coffee b/assets/scripts/app/helpers.coffee index 98608e18..5c5b4f46 100644 --- a/assets/scripts/app/helpers.coffee +++ b/assets/scripts/app/helpers.coffee @@ -2,3 +2,4 @@ require 'helpers/handlebars' require 'helpers/helpers' require 'helpers/urls' require 'helpers/status_image_formatter' +require 'helpers/github_url_properties' diff --git a/assets/scripts/app/helpers/github_url_properties.coffee b/assets/scripts/app/helpers/github_url_properties.coffee new file mode 100644 index 00000000..6a19e21c --- /dev/null +++ b/assets/scripts/app/helpers/github_url_properties.coffee @@ -0,0 +1,8 @@ +Travis.GithubUrlProperties = Ember.Mixin.create + urlGithubCommit: (-> + Travis.Urls.githubCommit(@get('repo.slug'), @get('commit.sha')) + ).property('repo.slug', 'commit.sha') + + urlGithubPullRequest: (-> + Travis.Urls.githubPullRequest(@get('repo.slug'), @get('build.pullRequestNumber')) + ).property('repo.slug', 'build.pullRequestNumber') diff --git a/assets/scripts/app/helpers/handlebars.coffee b/assets/scripts/app/helpers/handlebars.coffee index bc555d96..98901e6a 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) @@ -50,6 +48,7 @@ Ember.Handlebars.registerHelper('label', (options) -> options.hash.for = id options.hashTypes.for = 'STRING' options.hashContexts.for = this + options.fn = Ember.Handlebars.compile("{{view.content}}") Ember.Handlebars.helpers.view.call(this, view, options) ) @@ -112,7 +111,7 @@ Travis.ErrorsView = Ember.View.extend ).property('@errors') show: Ember.computed.notEmpty('errors.[]') -Ember.Handlebars.helper('travis-errors', (name, options) -> +Ember.Handlebars.registerHelper('travis-errors', (name, options) -> errors = @get('errors').for(name) view = Travis.ErrorsView.create( controller: this @@ -125,7 +124,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 +139,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 +152,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 +169,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/helpers/urls.coffee b/assets/scripts/app/helpers/urls.coffee index cda9bbc5..9e7d5fbc 100644 --- a/assets/scripts/app/helpers/urls.coffee +++ b/assets/scripts/app/helpers/urls.coffee @@ -21,10 +21,18 @@ "#{Travis.config.source_endpoint}/#{slug}/settings/hooks#travis_minibucket" statusImage: (slug, branch) -> - "#{location.protocol}//#{location.host}/#{slug}.svg" + if branch then "?branch=#{encodeURIComponent(branch)}" else '' + if Travis.config.pro + token = Travis.__container__.lookup('controller:currentUser').get('token') + "#{location.protocol}//#{location.host}/#{slug}.svg?token=#{token}" + if branch then "&branch=#{branch}" else '' + else + "#{location.protocol}//#{location.host}/#{slug}.svg" + if branch then "?branch=#{encodeURIComponent(branch)}" else '' ccXml: (slug) -> - "#{Travis.config.api_endpoint}/repos/#{slug}/cc.xml" + if Travis.config.pro + token = Travis.__container__.lookup('controller:currentUser').get('token') + "##{Travis.config.api_endpoint}/repos/#{slug}/cc.xml?token=#{token}" + else + "#{Travis.config.api_endpoint}/repos/#{slug}/cc.xml" email: (email) -> "mailto:#{email}" diff --git a/assets/scripts/app/models/account.coffee b/assets/scripts/app/models/account.coffee index 513782db..f936df1d 100644 --- a/assets/scripts/app/models/account.coffee +++ b/assets/scripts/app/models/account.coffee @@ -5,6 +5,8 @@ require 'travis/model' name: Ember.attr('string') type: Ember.attr('string') _reposCount: Ember.attr(Number, key: 'repos_count') + subscribed: Ember.attr(Boolean) + education: Ember.attr(Boolean) urlGithub: (-> "#{Travis.config.source_endpoint}/#{@get('login')}" diff --git a/assets/scripts/app/models/build.coffee b/assets/scripts/app/models/build.coffee index a5fb1809..a83fcf91 100644 --- a/assets/scripts/app/models/build.coffee +++ b/assets/scripts/app/models/build.coffee @@ -23,7 +23,14 @@ require 'travis/model' jobs: Ember.hasMany('Travis.Job') config: (-> - Travis.Helpers.compact(@get('_config')) + console.log('config') + if config = @get('_config') + Travis.Helpers.compact(config) + else + return if @get('isFetchingConfig') + @set 'isFetchingConfig', true + + @reload() ).property('_config') # TODO add eventType to the api for api build requests @@ -92,12 +99,6 @@ require 'travis/model' requeue: -> Travis.ajax.post "/builds/#{@get('id')}/restart" - isPropertyLoaded: (key) -> - if ['_duration', '_finishedAt'].contains(key) && !@get('isFinished') - return true - else - @_super(key) - formattedFinishedAt: (-> if finishedAt = @get('finishedAt') moment(finishedAt).format('lll') diff --git a/assets/scripts/app/models/env_var.coffee b/assets/scripts/app/models/env_var.coffee index 5ddd444c..168df98f 100644 --- a/assets/scripts/app/models/env_var.coffee +++ b/assets/scripts/app/models/env_var.coffee @@ -6,11 +6,3 @@ Travis.EnvVar = Travis.Model.extend public: Ember.attr('boolean') repo: Ember.belongsTo('Travis.Repo', key: 'repository_id') - - isPropertyLoaded: (key) -> - if key == 'value' - return true - else - @_super(key) - - diff --git a/assets/scripts/app/models/job.coffee b/assets/scripts/app/models/job.coffee index 92a711fb..ec8d3b00 100644 --- a/assets/scripts/app/models/job.coffee +++ b/assets/scripts/app/models/job.coffee @@ -42,7 +42,13 @@ require 'travis/model' ).property('repositorySlug') config: (-> - Travis.Helpers.compact(@get('_config')) + if config = @get('_config') + Travis.Helpers.compact(config) + else + return if @get('isFetchingConfig') + @set 'isFetchingConfig', true + + @reload() ).property('_config') isFinished: (-> @@ -113,14 +119,6 @@ require 'travis/model' @unsubscribe() if @get('state') == 'finished' && Travis.pusher ).observes('state') - isPropertyLoaded: (key) -> - if ['_finishedAt'].contains(key) && !@get('isFinished') - return true - else if key == '_startedAt' && @get('state') == 'created' - return true - else - @_super(key) - isFinished: (-> @get('state') in ['passed', 'failed', 'errored', 'canceled'] ).property('state') @@ -139,6 +137,10 @@ require 'travis/model' true ).property() + slug: (-> + "#{@get('repo.slug')} ##{@get('number')}" + ).property() + @Travis.Job.reopenClass queued: -> filtered = Ember.FilteredRecordArray.create( diff --git a/assets/scripts/app/models/log.coffee b/assets/scripts/app/models/log.coffee index ef9b396e..6056731f 100644 --- a/assets/scripts/app/models/log.coffee +++ b/assets/scripts/app/models/log.coffee @@ -6,9 +6,6 @@ require 'travis/log_chunks' isLoaded: false length: 0 - init: -> - @setParts() - fetchMissingParts: (partNumbers, after) -> return if @get('notStarted') @@ -27,31 +24,31 @@ require 'travis/log_chunks' for part in parts @append part - setParts: -> - if parts = @get('parts') - parts.destroy() + parts: (-> + #if Travis.config.pusher_log_fallback + # Travis.LogChunks.create(content: [], missingPartsCallback: => @fetchMissingParts.apply(this, arguments)) + #else + Ember.ArrayProxy.create(content: []) + ).property() - if Travis.config.pusher_log_fallback - parts = Travis.LogChunks.create(content: [], missingPartsCallback: => @fetchMissingParts.apply(this, arguments)) - else - parts = Ember.ArrayProxy.create(content: []) - - @set 'parts', parts - # @set 'parts', Travis.ChunkBuffer.create(content: []) + clearParts: -> + parts = @get('parts') + parts.set('content', []) fetch: -> console.log 'log model: fetching log' if Log.DEBUG - @setParts() + @clearParts() handlers = json: (json) => @loadParts(json['log']['parts']) text: (text) => @loadText(text) Travis.Log.Request.create(id: id, handlers: handlers).run() if id = @get('job.id') clear: -> - @setParts() + @clearParts() @incrementProperty('version') append: (part) -> + return if @get('parts').isDestroying || @get('parts').isDestroyed @get('parts').pushObject(part) loadParts: (parts) -> @@ -75,6 +72,9 @@ Travis.Log.Request = Em.Object.extend success: (body, status, xhr) => Ember.run(this, -> @handle(body, status, xhr)) handle: (body, status, xhr) -> + if Travis.config.pro + Travis.Job.find(@get('id')).get('log').set('token', xhr.getResponseHeader('X-Log-Access-Token')) + if xhr.status == 204 $.ajax(url: @redirectTo(xhr), type: 'GET', success: @handlers.text) else if @isJson(xhr, body) diff --git a/assets/scripts/app/models/repo.coffee b/assets/scripts/app/models/repo.coffee index aa8c1e99..2e9a1cd0 100644 --- a/assets/scripts/app/models/repo.coffee +++ b/assets/scripts/app/models/repo.coffee @@ -24,6 +24,9 @@ require 'travis/model' } ).property('lastBuildId', 'lastBuildNumber') + withLastBuild: -> + @filter( (repo) -> repo.get('lastBuildId') ) + sshKey: (-> Travis.SshKey.find(@get('id')) ) diff --git a/assets/scripts/app/models/ssh_key.coffee b/assets/scripts/app/models/ssh_key.coffee index 59f05b19..40d83fb0 100644 --- a/assets/scripts/app/models/ssh_key.coffee +++ b/assets/scripts/app/models/ssh_key.coffee @@ -3,11 +3,3 @@ Travis.SshKey = Travis.Model.extend value: Ember.attr('string') description: Ember.attr('string') fingerprint: Ember.attr('string') - - isPropertyLoaded: (key) -> - if key == 'value' - return true - else - @_super(key) - - diff --git a/assets/scripts/app/pusher.coffee b/assets/scripts/app/pusher.coffee index 5cc394b9..74802a85 100644 --- a/assets/scripts/app/pusher.coffee +++ b/assets/scripts/app/pusher.coffee @@ -2,10 +2,17 @@ Travis.Pusher = (config) -> @init(config) this -$.extend Travis.Pusher, - CHANNELS: ['common'] - CHANNEL_PREFIX: '' - ENCRYPTED: false +if Travis.config.pro + $.extend Travis.Pusher, + CHANNELS: [] + CHANNEL_PREFIX: 'private-' + ENCRYPTED: true + KEY: '' +else + $.extend Travis.Pusher, + CHANNELS: ['common'] + CHANNEL_PREFIX: '' + ENCRYPTED: false $.extend Travis.Pusher.prototype, active_channels: [] @@ -101,3 +108,73 @@ $.extend Travis.Pusher.prototype, ignoreMessage: (message) -> message.indexOf('Existing subscription') == 0 or message.indexOf('No current subscription') == 0 +pusher_host = $('meta[name="travis.pusher_host"]').attr('value') +pusher_path = $('meta[name="travis.pusher_path"]').attr('value') + +Pusher.SockJSTransport.isSupported = -> false if pusher_host != 'ws.pusherapp.com' + +if Travis.config.pro + Pusher.channel_auth_transport = 'bulk_ajax' + + Pusher.authorizers.bulk_ajax = (socketId, _callback) -> + channels = Travis.pusher.pusher.channels + channels.callbacks ||= [] + + name = this.channel.name + names = $.keys(channels.channels) + + channels.callbacks.push (auths) -> + _callback(false, auth: auths[name]) + + unless channels.fetching + channels.fetching = true + Travis.ajax.post Pusher.channel_auth_endpoint, { socket_id: socketId, channels: names }, (data) -> + channels.fetching = false + callback(data.channels) for callback in channels.callbacks + + + Pusher.getDefaultStrategy = (config) -> + [ + [":def", "ws_options", { + hostUnencrypted: config.wsHost + ":" + config.wsPort + (pusher_path && "/#{pusher_path}" || ''), + hostEncrypted: config.wsHost + ":" + config.wssPort + (pusher_path && "/#{pusher_path}" || '') + path: config.path + }], + [":def", "sockjs_options", { + hostUnencrypted: config.httpHost + ":" + config.httpPort, + hostEncrypted: config.httpHost + ":" + config.httpsPort + }], + [":def", "timeouts", { + loop: true, + timeout: 15000, + timeoutLimit: 60000 + }], + + [":def", "ws_manager", [":transport_manager", { + lives: 2, + minPingDelay: 10000, + maxPingDelay: config.activity_timeout + }]], + + [":def_transport", "ws", "ws", 3, ":ws_options", ":ws_manager"], + [":def_transport", "flash", "flash", 2, ":ws_options", ":ws_manager"], + [":def_transport", "sockjs", "sockjs", 1, ":sockjs_options"], + [":def", "ws_loop", [":sequential", ":timeouts", ":ws"]], + [":def", "flash_loop", [":sequential", ":timeouts", ":flash"]], + [":def", "sockjs_loop", [":sequential", ":timeouts", ":sockjs"]], + + [":def", "strategy", + [":cached", 1800000, + [":first_connected", + [":if", [":is_supported", ":ws"], [ + ":best_connected_ever", ":ws_loop", [":delayed", 2000, [":sockjs_loop"]] + ], [":if", [":is_supported", ":flash"], [ + ":best_connected_ever", ":flash_loop", [":delayed", 2000, [":sockjs_loop"]] + ], [ + ":sockjs_loop" + ] + ]] + ] + ] + ] + ] diff --git a/assets/scripts/app/routes.coffee b/assets/scripts/app/routes.coffee index 2ce50a2e..838b0252 100644 --- a/assets/scripts/app/routes.coffee +++ b/assets/scripts/app/routes.coffee @@ -18,9 +18,22 @@ Travis.Route = Ember.Route.extend @_super.apply(this, arguments) signedIn: -> - @controllerFor('currentUser').get('content') + @controllerFor('currentUser').get('model') + + needsAuth: (-> + # on pro, we need to auth on every route + Travis.config.pro + ).property() Travis.ApplicationRoute = Travis.Route.extend + needsAuth: false + + renderTemplate: -> + if Travis.config.pro + $('body').addClass('pro') + + @_super.apply(this, arguments) + actions: redirectToGettingStarted: -> # do nothing, we handle it only in index path @@ -43,14 +56,21 @@ Travis.ApplicationRoute = Travis.Route.extend if transition = @auth.get('afterSignInTransition') @auth.set('afterSignInTransition', null) transition.retry() + else + @transitionTo('index') afterSignOut: -> - @transitionTo('index.current') + if Travis.config.pro + @transitionTo('auth') + else + @transitionTo('index') Travis.Router.map -> @resource 'index', path: '/', -> @resource 'getting_started' - @route 'current', path: '/' + @route 'recent' + @route 'my_repositories' + @route 'search', path: '/search/:phrase' @resource 'repo', path: '/:owner/:name', -> @route 'index', path: '/' @resource 'build', path: '/builds/:build_id' @@ -74,8 +94,9 @@ Travis.Router.map -> @route 'auth', path: '/auth' @resource 'profile', path: '/profile', -> - @resource 'account', path: '/:login' - @route 'info', path: '/info' + @resource 'accounts', path: '/', -> + @resource 'account', path: '/:login' + @route 'info', path: '/info' @route 'notFound', path: "/*path" @@ -129,8 +150,8 @@ Travis.GettingStartedRoute = Travis.Route.extend Travis.SimpleLayoutRoute = Travis.Route.extend setupController: -> $('body').attr('id', 'home') - @container.lookup('controller:repos').activate() - @container.lookup('controller:application').connectLayout 'simple' + toActivate = if @signedIn() then 'owned' else 'recent' + @container.lookup('controller:repos').activate(toActivate) @_super.apply(this, arguments) renderTemplate: -> @@ -148,31 +169,60 @@ Travis.InsufficientOauthPermissionsRoute = Travis.SimpleLayoutRoute.extend existingUser = document.location.hash.match(/#existing[_-]user/) controller.set('existingUser', existingUser) -Travis.IndexCurrentRoute = Travis.Route.extend +Travis.IndexTabRoute = Travis.Route.extend renderTemplate: -> @render 'repo' @render 'build', into: 'repo' setupController: -> @_super.apply this, arguments - @currentRepoDidChange() @controllerFor('repo').activate('index') - @controllerFor('repos').addObserver('firstObject', this, 'currentRepoDidChange') + @controllerFor('repos').activate(@get('reposTabName')) - afterModel: -> - @controllerFor('repos').possiblyRedirectToGettingStartedPage() + @currentRepoDidChange() + @controllerFor('repos').addObserver('firstObject', this, 'currentRepoDidChange') deactivate: -> @controllerFor('repos').removeObserver('firstObject', this, 'currentRepoDidChange') currentRepoDidChange: -> - @controllerFor('repo').set('repo', @controllerFor('repos').get('firstObject')) + if repo = @controllerFor('repos').get('firstObject') + @controllerFor('repo').set('repo', repo) actions: redirectToGettingStarted: -> @transitionTo('getting_started') +Travis.IndexMyRepositoriesRoute = Travis.IndexTabRoute.extend + reposTabName: 'owned' + afterModel: -> + @controllerFor('repos').possiblyRedirectToGettingStartedPage() + +Travis.IndexRecentRoute = Travis.IndexTabRoute.extend + reposTabName: 'recent' + +Travis.IndexSearchRoute = Travis.IndexTabRoute.extend + renderTemplate: -> + @render 'repo' + @render 'build', into: 'repo' + + setupController: (controller, searchPhrase) -> + # TODO: this method is almost the same as _super, refactor this + @controllerFor('repo').activate('index') + @controllerFor('repos').activate('search', searchPhrase) + + @currentRepoDidChange() + @controllerFor('repos').addObserver('firstObject', this, 'currentRepoDidChange') + + model: (params) -> + params.phrase + + deactivate: -> + @_super.apply(this, arguments) + + @controllerFor('repos').set('search', undefined) + Travis.AbstractBuildsRoute = Travis.Route.extend renderTemplate: -> @render 'builds' @@ -187,7 +237,7 @@ Travis.AbstractBuildsRoute = Travis.Route.extend contentDidChange: -> path = @get('path') - @controllerFor('builds').set('content', @controllerFor('repo').get(path)) + @controllerFor('builds').set('model', @controllerFor('repo').get(path)) path: (-> type = @get('contentType') @@ -195,8 +245,19 @@ Travis.AbstractBuildsRoute = Travis.Route.extend ).property('contentType') Travis.BuildsRoute = Travis.AbstractBuildsRoute.extend(contentType: 'builds') -Travis.PullRequestsRoute = Travis.AbstractBuildsRoute.extend(contentType: 'pull_requests') Travis.BranchesRoute = Travis.AbstractBuildsRoute.extend(contentType: 'branches') +Travis.PullRequestsRoute = Travis.AbstractBuildsRoute.extend( + contentType: 'pull_requests' + + # TODO: it would be better to have separate controller for branches and PRs list + setupController: (controller, model) -> + @_super(controller, model) + + this.controllerFor('builds').set('isPullRequestsList', true) + + deactivate: -> + this.controllerFor('builds').set('isPullRequestsList', false) +) Travis.BuildRoute = Travis.Route.extend serialize: (model, params) -> @@ -208,18 +269,17 @@ Travis.BuildRoute = Travis.Route.extend model = Travis.Build.find(model) if model && !model.get repo = @controllerFor('repo') - repo.set('build', model) - repo.activate('build') + #repo.set('build', model) @controllerFor('build').set('build', model) - repo.set('build', model) + repo.activate('build') + #repo.set('build', model) model: (params) -> Travis.Build.fetch(params.build_id) deactivate: -> - repo = @controllerFor('repo') - repo.set('build', null) - repo.set('job', null) + @controllerFor('job').set('job', null) + @controllerFor('build').set('build', null) Travis.JobRoute = Travis.Route.extend serialize: (model, params) -> @@ -231,19 +291,19 @@ Travis.JobRoute = Travis.Route.extend model = Travis.Job.find(model) if model && !model.get repo = @controllerFor('repo') - repo.set('job', model) + @controllerFor('job').set('job', model) repo.activate('job') if build = model.get('build') @controllerFor('build').set('build', build) - repo.set('build', build) model: (params) -> Travis.Job.fetch(params.job_id) deactivate: -> - repo = @controllerFor('repo') - repo.set('job', null) + @controllerFor('build').set('build', null) + @controllerFor('job').set('job', null) + Travis.RepoIndexRoute = Travis.Route.extend setupController: (controller, model) -> @@ -258,8 +318,8 @@ Travis.RepoIndexRoute = Travis.Route.extend deactivate: -> repo = @controllerFor('repo') - repo.set('build', null) - repo.set('job', null) + @controllerFor('build').set('build', null) + @controllerFor('job').set('job', null) Travis.RepoRoute = Travis.Route.extend renderTemplate: -> @@ -280,6 +340,9 @@ Travis.RepoRoute = Travis.Route.extend slug = "#{params.owner}/#{params.name}" Travis.Repo.fetchBySlug(slug) + resetController: -> + @controllerFor('repo').deactivate() + actions: error: (error) -> # if error throwed has a slug (ie. it was probably repo not found) @@ -291,15 +354,25 @@ Travis.RepoRoute = Travis.Route.extend # bubble to the top return true +# Obviously Index route should be renamed to something +# like "main" or "home" +Travis.IndexIndexRoute = Travis.Route.extend + redirect: -> + target = if @signedIn() then 'my_repositories' else 'recent' + @transitionTo("index.#{target}") + Travis.IndexRoute = Travis.Route.extend renderTemplate: -> $('body').attr('id', 'home') - @render 'repos', outlet: 'left' + @_super.apply this, arguments + + @render 'repos', outlet: 'left', into: 'index' setupController: (controller)-> - @container.lookup('controller:repos').activate() - @container.lookup('controller:application').connectLayout 'home' + # TODO: this is redundant with my_repositories and recent routes + toActivate = if @signedIn() then 'owned' else 'recent' + @container.lookup('controller:repos').activate(toActivate) Travis.StatsRoute = Travis.Route.extend renderTemplate: -> @@ -307,42 +380,39 @@ Travis.StatsRoute = Travis.Route.extend @render 'stats' - setupController: -> - @container.lookup('controller:application').connectLayout('simple') - Travis.NotFoundRoute = Travis.Route.extend renderTemplate: -> $('body').attr('id', 'not-found') @render 'not_found' - setupController: -> - @container.lookup('controller:application').connectLayout('simple') - Travis.ProfileRoute = Travis.Route.extend needsAuth: true setupController: (controller, model) -> - @container.lookup('controller:application').connectLayout('profile') @controllerFor('accounts').set('model', model) + renderTemplate: -> + $('body').attr('id', 'profile') + @_super.apply(this, arguments) + @render 'loading', outlet: 'left', into: 'profile' + +Travis.AccountsRoute = Travis.Route.extend model: -> Travis.Account.fetch(all: true) renderTemplate: -> - $('body').attr('id', 'profile') - @render 'accounts', outlet: 'left' - @_super.apply(this, arguments) + @render 'profile_accounts', outlet: 'left', into: 'profile' -Travis.ProfileIndexRoute = Travis.Route.extend +Travis.AccountsIndexRoute = Travis.Route.extend redirect: -> # TODO: setting accounts model in ProfileRoute is wrong, but # at this stage it's better than what we had before - accounts = @modelFor('profile') + accounts = @modelFor('accounts') login = @controllerFor('currentUser').get('login') account = accounts.find (account) -> account.get('login') == login - @transitionTo 'account', account + @replaceWith 'account', account Travis.AccountRoute = Travis.Route.extend setupController: (controller, account) -> @@ -351,7 +421,7 @@ Travis.AccountRoute = Travis.Route.extend @controllerFor('profile').activate 'hooks' model: (params) -> - @modelFor('profile').find (account) -> account.get('login') == params.login + @modelFor('accounts').find (account) -> account.get('login') == params.login serialize: (account) -> if account && account.get @@ -359,30 +429,35 @@ Travis.AccountRoute = Travis.Route.extend else {} -Travis.ProfileInfoRoute = Travis.Route.extend +Travis.AccountsInfoRoute = Travis.Route.extend setupController: -> - @container.lookup('controller:profile').activate 'user' + user = @controllerFor('currentUser').get('model') + @controllerFor('account').set('model', user) + @controllerFor('profile').activate 'user' renderTemplate: -> - @render 'user' + @render 'accounts_info' Travis.AuthRoute = Travis.Route.extend + needsAuth: false + renderTemplate: -> $('body').attr('id', 'auth') @render 'auth.signin' - setupController: -> - @container.lookup('controller:application').connectLayout('simple') - deactivate: -> @controllerFor('auth').set('redirected', false) actions: afterSignIn: -> - @transitionTo('index.current') + @transitionTo('index') return true + redirect: -> + if @signedIn() + @transitionTo('index') + Travis.SettingsRoute = Travis.Route.extend needsAuth: true setupController: (controller, model) -> @@ -403,7 +478,7 @@ Travis.SshKeyRoute = Travis.Route.extend model: (params) -> repo = @modelFor('repo') self = this - Travis.SshKey.fetch(repo.get('id')).then ( (result) -> result ), (xhr) -> + Travis.SshKey.fetch(repo.get('id')).then ( (result) -> result unless result.get('isNew') ), (xhr) -> if xhr.status == 404 # if there is no model, just return null. I'm not sure if this is the # best answer, maybe we should just redirect to different route, like @@ -422,3 +497,8 @@ Travis.SshKeyRoute = Travis.Route.extend if @defaultKey controller.set('defaultKey', @defaultKey) @defaultKey = null + + deactivate: -> + @_super.apply(this, arguments) + + @controllerFor('ssh_key').send('cancel') diff --git a/assets/scripts/app/templates/accounts.hbs b/assets/scripts/app/templates/accounts.hbs new file mode 100644 index 00000000..c24cd689 --- /dev/null +++ b/assets/scripts/app/templates/accounts.hbs @@ -0,0 +1 @@ +{{outlet}} diff --git a/assets/scripts/app/templates/application.hbs b/assets/scripts/app/templates/application.hbs new file mode 100644 index 00000000..c24cd689 --- /dev/null +++ b/assets/scripts/app/templates/application.hbs @@ -0,0 +1 @@ +{{outlet}} diff --git a/assets/scripts/app/templates/builds/list.hbs b/assets/scripts/app/templates/builds/list.hbs index ea2a408a..221b16eb 100644 --- a/assets/scripts/app/templates/builds/list.hbs +++ b/assets/scripts/app/templates/builds/list.hbs @@ -10,7 +10,7 @@ Committer - {{#if view.isPullRequestsList}} + {{#if isPullRequestsList}} PR @@ -21,7 +21,7 @@ - {{#each build in controller}} + {{#each build in controller itemController="buildsItem"}} {{#view Travis.BuildsItemView contextBinding="build"}} @@ -35,16 +35,16 @@ {{{formatMessage commit.message short="true" repoBinding=build.repo}}} - + {{formatCommit commit}} {{commit.committerName}} - {{#if view.isPullRequestsList}} + {{#if isPullRequestsList}} - + #{{pullRequestNumber}} 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/index.hbs b/assets/scripts/app/templates/index.hbs new file mode 100644 index 00000000..c24cd689 --- /dev/null +++ b/assets/scripts/app/templates/index.hbs @@ -0,0 +1 @@ +{{outlet}} diff --git a/assets/scripts/app/templates/jobs.hbs b/assets/scripts/app/templates/jobs.hbs new file mode 100644 index 00000000..04e19178 --- /dev/null +++ b/assets/scripts/app/templates/jobs.hbs @@ -0,0 +1,17 @@ +

Running Jobs ({{controller.length}})

+ + diff --git a/assets/scripts/app/templates/jobs/list.hbs b/assets/scripts/app/templates/jobs/list.hbs index 046d2981..c122243f 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 view.required}} -
- + {{else}} -
- Build Matrix - + Build Matrix +
- + {{/if}} @@ -20,7 +19,7 @@ {{#each job in view.jobs}} - {{#view Travis.JobsItemView contextBinding="job"}} + {{#view 'jobs-item' context=job}} . Other browsers create it - * before the first node, no matter what. - * - * This means the the following code: - * - * div = document.createElement("div"); - * div.innerHTML = "
- Allowed Failures - - + Allowed Failures + +
{{#if job.id}} @@ -45,7 +44,7 @@ {{#unless view.required}} {{else}}
diff --git a/assets/scripts/app/templates/layouts/flash.hbs b/assets/scripts/app/templates/layouts/flash.hbs index ccbabee6..193d0d8c 100644 --- a/assets/scripts/app/templates/layouts/flash.hbs +++ b/assets/scripts/app/templates/layouts/flash.hbs @@ -1,6 +1,6 @@ {{#each flash in controller}} {{#view Travis.FlashItemView flashBinding="flash"}}

{{{flash.message}}}

- + {{/view}} {{/each}} diff --git a/assets/scripts/app/templates/layouts/home.hbs b/assets/scripts/app/templates/layouts/home.hbs index 4e6fc0fe..ff2aa177 100644 --- a/assets/scripts/app/templates/layouts/home.hbs +++ b/assets/scripts/app/templates/layouts/home.hbs @@ -3,10 +3,16 @@
- {{outlet left}} + {{outlet "left"}}
{{render "flash"}} - {{outlet}} + {{yield}}
+ +{{#if config.pro}} + +{{/if}} diff --git a/assets/scripts/app/templates/layouts/profile.hbs b/assets/scripts/app/templates/layouts/profile.hbs index 2a908308..736b3899 100644 --- a/assets/scripts/app/templates/layouts/profile.hbs +++ b/assets/scripts/app/templates/layouts/profile.hbs @@ -3,16 +3,16 @@
- {{outlet left}} + {{outlet "left"}}
{{render "flash"}} - {{outlet main}} + {{yield}}
hi
- * - * Generates the following DOM in IE: - * - * + div - * + table - * - script id='first' - * + tbody - * + tr - * + td - * - "hi" - * - script id='last' - * - * Which means that the two script tags, even though they were - * inserted at the same point in the hierarchy in the original - * HTML, now have different parents. - * - * This code reparents the first script tag by making it the tbody's - * first child. - * - */ - var fixParentage = function(start, end) { - if (start.parentNode !== end.parentNode) { - end.parentNode.insertBefore(start, end.parentNode.firstChild); - } - }; - - htmlFunc = function(html, outerToo) { - // get the real starting node. see realNode for details. - var start = realNode(document.getElementById(this.start)); - var end = document.getElementById(this.end); - var parentNode = end.parentNode; - var node, nextSibling, last; - - // make sure that the start and end nodes share the same - // parent. If not, fix it. - fixParentage(start, end); - - // remove all of the nodes after the starting placeholder and - // before the ending placeholder. - node = start.nextSibling; - while (node) { - nextSibling = node.nextSibling; - last = node === end; - - // if this is the last node, and we want to remove it as well, - // set the `end` node to the next sibling. This is because - // for the rest of the function, we insert the new nodes - // before the end (note that insertBefore(node, null) is - // the same as appendChild(node)). - // - // if we do not want to remove it, just break. - if (last) { - if (outerToo) { end = node.nextSibling; } else { break; } - } - - node.parentNode.removeChild(node); - - // if this is the last node and we didn't break before - // (because we wanted to remove the outer nodes), break - // now. - if (last) { break; } - - node = nextSibling; - } - - // get the first node for the HTML string, even in cases like - // tables and lists where a simple innerHTML on a div would - // swallow some of the content. - node = firstNodeFor(start.parentNode, html); - - if (outerToo) { - start.parentNode.removeChild(start); - } - - // copy the nodes for the HTML between the starting and ending - // placeholder. - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, end); - node = nextSibling; - } - }; - - // remove the nodes in the DOM representing this metamorph. - // - // this includes the starting and ending placeholders. - removeFunc = function() { - var start = realNode(document.getElementById(this.start)); - var end = document.getElementById(this.end); - - this.html(''); - start.parentNode.removeChild(start); - end.parentNode.removeChild(end); - }; - - appendToFunc = function(parentNode) { - var node = firstNodeFor(parentNode, this.outerHTML()); - var nextSibling; - - while (node) { - nextSibling = node.nextSibling; - parentNode.appendChild(node); - node = nextSibling; - } - }; - - afterFunc = function(html) { - // get the real starting node. see realNode for details. - var end = document.getElementById(this.end); - var insertBefore = end.nextSibling; - var parentNode = end.parentNode; - var nextSibling; - var node; - - // get the first node for the HTML string, even in cases like - // tables and lists where a simple innerHTML on a div would - // swallow some of the content. - node = firstNodeFor(parentNode, html); - - // copy the nodes for the HTML between the starting and ending - // placeholder. - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, insertBefore); - node = nextSibling; - } - }; - - prependFunc = function(html) { - var start = document.getElementById(this.start); - var parentNode = start.parentNode; - var nextSibling; - var node; - - node = firstNodeFor(parentNode, html); - var insertBefore = start.nextSibling; - - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, insertBefore); - node = nextSibling; - } - }; + /* global navigator */ + // require the main entry points for each of these packages + // this is so that the global exports occur properly + + // do this to ensure that Ember.Test is defined properly on the global + // if it is present. + if (Ember.__loader.registry['ember-testing']) { + requireModule('ember-testing'); } - Metamorph.prototype.html = function(html) { - this.checkRemoved(); - if (html === undefined) { return this.innerHTML; } - - htmlFunc.call(this, html); - - this.innerHTML = html; - }; - - Metamorph.prototype.replaceWith = function(html) { - this.checkRemoved(); - htmlFunc.call(this, html, true); - }; - - Metamorph.prototype.remove = removeFunc; - Metamorph.prototype.outerHTML = outerHTMLFunc; - Metamorph.prototype.appendTo = appendToFunc; - Metamorph.prototype.after = afterFunc; - Metamorph.prototype.prepend = prependFunc; - Metamorph.prototype.startTag = startTagFunc; - Metamorph.prototype.endTag = endTagFunc; - - Metamorph.prototype.isRemoved = function() { - var before = document.getElementById(this.start); - var after = document.getElementById(this.end); - - return !before || !after; - }; - - Metamorph.prototype.checkRemoved = function() { - if (this.isRemoved()) { - throw new Error("Cannot perform operations on a Metamorph that is not in the DOM."); - } - }; - - return Metamorph; - }); - -})(); - -(function() { -define("ember-handlebars-compiler", - ["ember-metal/core","exports"], - function(__dependency1__, __exports__) { - "use strict"; /** + Ember + @module ember - @submodule ember-handlebars-compiler */ - var Ember = __dependency1__["default"]; - - // ES6Todo: you'll need to import debugger once debugger is es6'd. - if (typeof Ember.assert === 'undefined') { Ember.assert = function(){}; }; - if (typeof Ember.FEATURES === 'undefined') { Ember.FEATURES = { isEnabled: function(){} }; }; - - var objectCreate = Object.create || function(parent) { - function F() {} - F.prototype = parent; - return new F(); - }; - - // set up for circular references later - var View, Component; - - // ES6Todo: when ember-debug is es6'ed import this. - // var emberAssert = Ember.assert; - var Handlebars = (Ember.imports && Ember.imports.Handlebars) || (this && this.Handlebars); - if (!Handlebars && typeof require === 'function') { - Handlebars = require('handlebars'); - } - - Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1. Include " + - "a SCRIPT tag in the HTML HEAD linking to the Handlebars file " + - "before you link to Ember.", Handlebars); - - Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1, " + - "COMPILER_REVISION expected: 4, got: " + Handlebars.COMPILER_REVISION + - " - Please note: Builds of master may have other COMPILER_REVISION values.", - Handlebars.COMPILER_REVISION === 4); - - /** - Prepares the Handlebars templating library for use inside Ember's view - system. - - The `Ember.Handlebars` object is the standard Handlebars library, extended to - use Ember's `get()` method instead of direct property access, which allows - computed properties to be used inside templates. - - To create an `Ember.Handlebars` template, call `Ember.Handlebars.compile()`. - This will return a function that can be used by `Ember.View` for rendering. - - @class Handlebars - @namespace Ember - */ - var EmberHandlebars = Ember.Handlebars = objectCreate(Handlebars); - - /** - Register a bound helper or custom view helper. - - ## Simple bound helper example - - ```javascript - Ember.Handlebars.helper('capitalize', function(value) { - return value.toUpperCase(); - }); - ``` - - The above bound helper can be used inside of templates as follows: - - ```handlebars - {{capitalize name}} - ``` - - In this case, when the `name` property of the template's context changes, - the rendered value of the helper will update to reflect this change. - - For more examples of bound helpers, see documentation for - `Ember.Handlebars.registerBoundHelper`. - - ## Custom view helper example - - Assuming a view subclass named `App.CalendarView` were defined, a helper - for rendering instances of this view could be registered as follows: - - ```javascript - Ember.Handlebars.helper('calendar', App.CalendarView): - ``` - - The above bound helper can be used inside of templates as follows: - - ```handlebars - {{calendar}} - ``` - - Which is functionally equivalent to: - - ```handlebars - {{view App.CalendarView}} - ``` - - Options in the helper will be passed to the view in exactly the same - manner as with the `view` helper. - - @method helper - @for Ember.Handlebars - @param {String} name - @param {Function|Ember.View} function or view class constructor - @param {String} dependentKeys* - */ - EmberHandlebars.helper = function(name, value) { - if (!View) { View = requireModule('ember-views/views/view')['View']; } // ES6TODO: stupid circular dep - if (!Component) { Component = requireModule('ember-views/views/component')['default']; } // ES6TODO: stupid circular dep - - Ember.assert("You tried to register a component named '" + name + "', but component names must include a '-'", !Component.detect(value) || name.match(/-/)); - - if (View.detect(value)) { - EmberHandlebars.registerHelper(name, EmberHandlebars.makeViewHelper(value)); - } else { - EmberHandlebars.registerBoundHelper.apply(null, arguments); - } - }; - - /** - Returns a helper function that renders the provided ViewClass. - - Used internally by Ember.Handlebars.helper and other methods - involving helper/component registration. - - @private - @method makeViewHelper - @for Ember.Handlebars - @param {Function} ViewClass view class constructor - @since 1.2.0 - */ - EmberHandlebars.makeViewHelper = function(ViewClass) { - return function(options) { - Ember.assert("You can only pass attributes (such as name=value) not bare values to a helper for a View found in '" + ViewClass.toString() + "'", arguments.length < 2); - return EmberHandlebars.helpers.view.call(this, ViewClass, options); - }; - }; - - /** - @class helpers - @namespace Ember.Handlebars - */ - EmberHandlebars.helpers = objectCreate(Handlebars.helpers); - - /** - Override the the opcode compiler and JavaScript compiler for Handlebars. - - @class Compiler - @namespace Ember.Handlebars - @private - @constructor - */ - EmberHandlebars.Compiler = function() {}; - - // Handlebars.Compiler doesn't exist in runtime-only - if (Handlebars.Compiler) { - EmberHandlebars.Compiler.prototype = objectCreate(Handlebars.Compiler.prototype); - } - - EmberHandlebars.Compiler.prototype.compiler = EmberHandlebars.Compiler; - - /** - @class JavaScriptCompiler - @namespace Ember.Handlebars - @private - @constructor - */ - EmberHandlebars.JavaScriptCompiler = function() {}; - - // Handlebars.JavaScriptCompiler doesn't exist in runtime-only - if (Handlebars.JavaScriptCompiler) { - EmberHandlebars.JavaScriptCompiler.prototype = objectCreate(Handlebars.JavaScriptCompiler.prototype); - EmberHandlebars.JavaScriptCompiler.prototype.compiler = EmberHandlebars.JavaScriptCompiler; - } - - - EmberHandlebars.JavaScriptCompiler.prototype.namespace = "Ember.Handlebars"; - - EmberHandlebars.JavaScriptCompiler.prototype.initializeBuffer = function() { - return "''"; - }; - - /** - Override the default buffer for Ember Handlebars. By default, Handlebars - creates an empty String at the beginning of each invocation and appends to - it. Ember's Handlebars overrides this to append to a single shared buffer. - - @private - @method appendToBuffer - @param string {String} - */ - EmberHandlebars.JavaScriptCompiler.prototype.appendToBuffer = function(string) { - return "data.buffer.push("+string+");"; - }; - - // Hacks ahead: - // Handlebars presently has a bug where the `blockHelperMissing` hook - // doesn't get passed the name of the missing helper name, but rather - // gets passed the value of that missing helper evaluated on the current - // context, which is most likely `undefined` and totally useless. - // - // So we alter the compiled template function to pass the name of the helper - // instead, as expected. - // - // This can go away once the following is closed: - // https://github.com/wycats/handlebars.js/issues/634 - - var DOT_LOOKUP_REGEX = /helpers\.(.*?)\)/, - BRACKET_STRING_LOOKUP_REGEX = /helpers\['(.*?)'/, - INVOCATION_SPLITTING_REGEX = /(.*blockHelperMissing\.call\(.*)(stack[0-9]+)(,.*)/; - - EmberHandlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation = function(source) { - var helperInvocation = source[source.length - 1], - helperName = (DOT_LOOKUP_REGEX.exec(helperInvocation) || BRACKET_STRING_LOOKUP_REGEX.exec(helperInvocation))[1], - matches = INVOCATION_SPLITTING_REGEX.exec(helperInvocation); - - source[source.length - 1] = matches[1] + "'" + helperName + "'" + matches[3]; - }; - - var stringifyBlockHelperMissing = EmberHandlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation; - - var originalBlockValue = EmberHandlebars.JavaScriptCompiler.prototype.blockValue; - EmberHandlebars.JavaScriptCompiler.prototype.blockValue = function() { - originalBlockValue.apply(this, arguments); - stringifyBlockHelperMissing(this.source); - }; - - var originalAmbiguousBlockValue = EmberHandlebars.JavaScriptCompiler.prototype.ambiguousBlockValue; - EmberHandlebars.JavaScriptCompiler.prototype.ambiguousBlockValue = function() { - originalAmbiguousBlockValue.apply(this, arguments); - stringifyBlockHelperMissing(this.source); - }; - - /** - Rewrite simple mustaches from `{{foo}}` to `{{bind "foo"}}`. This means that - all simple mustaches in Ember's Handlebars will also set up an observer to - keep the DOM up to date when the underlying property changes. - - @private - @method mustache - @for Ember.Handlebars.Compiler - @param mustache - */ - EmberHandlebars.Compiler.prototype.mustache = function(mustache) { - if (!(mustache.params.length || mustache.hash)) { - var id = new Handlebars.AST.IdNode([{ part: '_triageMustache' }]); - - // Update the mustache node to include a hash value indicating whether the original node - // was escaped. This will allow us to properly escape values when the underlying value - // changes and we need to re-render the value. - if (!mustache.escaped) { - mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]); - mustache.hash.pairs.push(["unescaped", new Handlebars.AST.StringNode("true")]); - } - mustache = new Handlebars.AST.MustacheNode([id].concat([mustache.id]), mustache.hash, !mustache.escaped); - } - - return Handlebars.Compiler.prototype.mustache.call(this, mustache); - }; - - /** - Used for precompilation of Ember Handlebars templates. This will not be used - during normal app execution. - - @method precompile - @for Ember.Handlebars - @static - @param {String} string The template to precompile - @param {Boolean} asObject optional parameter, defaulting to true, of whether or not the - compiled template should be returned as an Object or a String - */ - EmberHandlebars.precompile = function(string, asObject) { - var ast = Handlebars.parse(string); - - var options = { - knownHelpers: { - action: true, - unbound: true, - 'bind-attr': true, - template: true, - view: true, - _triageMustache: true - }, - data: true, - stringParams: true - }; - - asObject = asObject === undefined ? true : asObject; - - var environment = new EmberHandlebars.Compiler().compile(ast, options); - return new EmberHandlebars.JavaScriptCompiler().compile(environment, options, undefined, asObject); - }; - - // We don't support this for Handlebars runtime-only - if (Handlebars.compile) { - /** - The entry point for Ember Handlebars. This replaces the default - `Handlebars.compile` and turns on template-local data and String - parameters. - - @method compile - @for Ember.Handlebars - @static - @param {String} string The template to compile - @return {Function} - */ - EmberHandlebars.compile = function(string) { - var ast = Handlebars.parse(string); - var options = { data: true, stringParams: true }; - var environment = new EmberHandlebars.Compiler().compile(ast, options); - var templateSpec = new EmberHandlebars.JavaScriptCompiler().compile(environment, options, undefined, true); - - var template = EmberHandlebars.template(templateSpec); - template.isMethod = false; //Make sure we don't wrap templates with ._super - - return template; - }; - } - - __exports__["default"] = EmberHandlebars; + Ember.deprecate('Usage of Ember is deprecated for Internet Explorer 6 and 7, support will be removed in the next major version.', !navigator.userAgent.match(/MSIE [67]/)); }); -})(); - -(function() { -define("ember-handlebars/component_lookup", - ["ember-runtime/system/object","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var EmberObject = __dependency1__["default"]; - - var ComponentLookup = EmberObject.extend({ - lookupFactory: function(name, container) { - - container = container || this.container; - - var fullName = 'component:' + name, - templateFullName = 'template:components/' + name, - templateRegistered = container && container.has(templateFullName); - - if (templateRegistered) { - container.injection(fullName, 'layout', templateFullName); - } - - var Component = container.lookupFactory(fullName); - - // Only treat as a component if either the component - // or a template has been registered. - if (templateRegistered || Component) { - if (!Component) { - container.register(fullName, Ember.Component); - Component = container.lookupFactory(fullName); - } - return Component; - } - } - }); - - __exports__["default"] = ComponentLookup; - }); -define("ember-handlebars/controls", - ["ember-handlebars/controls/checkbox","ember-handlebars/controls/text_field","ember-handlebars/controls/text_area","ember-metal/core","ember-handlebars-compiler","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { - "use strict"; - var Checkbox = __dependency1__["default"]; - var TextField = __dependency2__["default"]; - var TextArea = __dependency3__["default"]; - - var Ember = __dependency4__["default"]; - // Ember.assert - // var emberAssert = Ember.assert; - - var EmberHandlebars = __dependency5__["default"]; - var helpers = EmberHandlebars.helpers; - /** - @module ember - @submodule ember-handlebars-compiler - */ - - /** - - The `{{input}}` helper inserts an HTML `` tag into the template, - with a `type` value of either `text` or `checkbox`. If no `type` is provided, - `text` will be the default value applied. The attributes of `{{input}}` - match those of the native HTML tag as closely as possible for these two types. - - ## Use as text field - An `{{input}}` with no `type` or a `type` of `text` will render an HTML text input. - The following HTML attributes can be set via the helper: - - - - - - - - - - - - -
`readonly``required``autofocus`
`value``placeholder``disabled`
`size``tabindex``maxlength`
`name``min``max`
`pattern``accept``autocomplete`
`autosave``formaction``formenctype`
`formmethod``formnovalidate``formtarget`
`height``inputmode``multiple`
`step``width``form`
`selectionDirection``spellcheck` 
- - - When set to a quoted string, these values will be directly applied to the HTML - element. When left unquoted, these values will be bound to a property on the - template's current rendering context (most typically a controller instance). - - ## Unbound: - - ```handlebars - {{input value="http://www.facebook.com"}} - ``` - - - ```html - - ``` - - ## Bound: - - ```javascript - App.ApplicationController = Ember.Controller.extend({ - firstName: "Stanley", - entryNotAllowed: true - }); - ``` - - - ```handlebars - {{input type="text" value=firstName disabled=entryNotAllowed size="50"}} - ``` - - - ```html - - ``` - - ## Extension - - Internally, `{{input type="text"}}` creates an instance of `Ember.TextField`, passing - arguments from the helper to `Ember.TextField`'s `create` method. You can extend the - capabilities of text inputs in your applications by reopening this class. For example, - if you are building a Bootstrap project where `data-*` attributes are used, you - can add one to the `TextField`'s `attributeBindings` property: - - - ```javascript - Ember.TextField.reopen({ - attributeBindings: ['data-error'] - }); - ``` - - Keep in mind when writing `Ember.TextField` subclasses that `Ember.TextField` - itself extends `Ember.Component`, meaning that it does NOT inherit - the `controller` of the parent view. - - See more about [Ember components](api/classes/Ember.Component.html) - - - ## Use as checkbox - - An `{{input}}` with a `type` of `checkbox` will render an HTML checkbox input. - The following HTML attributes can be set via the helper: - - * `checked` - * `disabled` - * `tabindex` - * `indeterminate` - * `name` - * `autofocus` - * `form` - - - When set to a quoted string, these values will be directly applied to the HTML - element. When left unquoted, these values will be bound to a property on the - template's current rendering context (most typically a controller instance). - - ## Unbound: - - ```handlebars - {{input type="checkbox" name="isAdmin"}} - ``` - - ```html - - ``` - - ## Bound: - - ```javascript - App.ApplicationController = Ember.Controller.extend({ - isAdmin: true - }); - ``` - - - ```handlebars - {{input type="checkbox" checked=isAdmin }} - ``` - - - ```html - - ``` - - ## Extension - - Internally, `{{input type="checkbox"}}` creates an instance of `Ember.Checkbox`, passing - arguments from the helper to `Ember.Checkbox`'s `create` method. You can extend the - capablilties of checkbox inputs in your applications by reopening this class. For example, - if you wanted to add a css class to all checkboxes in your application: - - - ```javascript - Ember.Checkbox.reopen({ - classNames: ['my-app-checkbox'] - }); - ``` - - - @method input - @for Ember.Handlebars.helpers - @param {Hash} options - */ - function inputHelper(options) { - Ember.assert('You can only pass attributes to the `input` helper, not arguments', arguments.length < 2); - - var hash = options.hash, - types = options.hashTypes, - inputType = hash.type, - onEvent = hash.on; - - delete hash.type; - delete hash.on; - - if (inputType === 'checkbox') { - Ember.assert("{{input type='checkbox'}} does not support setting `value=someBooleanValue`; you must use `checked=someBooleanValue` instead.", options.hashTypes.value !== 'ID'); - return helpers.view.call(this, Checkbox, options); - } else { - if (inputType) { hash.type = inputType; } - hash.onEvent = onEvent || 'enter'; - return helpers.view.call(this, TextField, options); - } - } - - /** - `{{textarea}}` inserts a new instance of ` - ``` - - Bound: - - In the following example, the `writtenWords` property on `App.ApplicationController` - will be updated live as the user types 'Lots of text that IS bound' into - the text area of their browser's window. - - ```javascript - App.ApplicationController = Ember.Controller.extend({ - writtenWords: "Lots of text that IS bound" - }); - ``` - - ```handlebars - {{textarea value=writtenWords}} - ``` - - Would result in the following HTML: - - ```html - - ``` - - If you wanted a one way binding between the text area and a div tag - somewhere else on your screen, you could use `Ember.computed.oneWay`: - - ```javascript - App.ApplicationController = Ember.Controller.extend({ - writtenWords: "Lots of text that IS bound", - outputWrittenWords: Ember.computed.oneWay("writtenWords") - }); - ``` - - ```handlebars - {{textarea value=writtenWords}} - -
- {{outputWrittenWords}} -
- ``` - - Would result in the following HTML: - - ```html - - - <-- the following div will be updated in real time as you type --> - -
- 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 - - - <-- both updated in real time --> - - - ``` - - ## Extension - - Internally, `{{textarea}}` creates an instance of `Ember.TextArea`, passing - arguments from the helper to `Ember.TextArea`'s `create` method. You can - extend the capabilities of text areas in your application by reopening this - class. For example, if you are building a Bootstrap project where `data-*` - attributes are used, you can globally add support for a `data-*` attribute - on all `{{textarea}}`s' in your app by reopening `Ember.TextArea` or - `Ember.TextSupport` and adding it to the `attributeBindings` concatenated - property: - - ```javascript - Ember.TextArea.reopen({ - attributeBindings: ['data-error'] - }); - ``` - - Keep in mind when writing `Ember.TextArea` subclasses that `Ember.TextArea` - itself extends `Ember.Component`, meaning that it does NOT inherit - the `controller` of the parent view. - - See more about [Ember components](api/classes/Ember.Component.html) - - @method textarea - @for Ember.Handlebars.helpers - @param {Hash} options - */ - function textareaHelper(options) { - Ember.assert('You can only pass attributes to the `textarea` helper, not arguments', arguments.length < 2); - - var hash = options.hash, - types = options.hashTypes; - - return helpers.view.call(this, TextArea, options); - } - - __exports__.inputHelper = inputHelper; - __exports__.textareaHelper = textareaHelper; - }); -define("ember-handlebars/controls/checkbox", - ["ember-metal/property_get","ember-metal/property_set","ember-views/views/view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var get = __dependency1__.get; - var set = __dependency2__.set; - var View = __dependency3__.View; - - /** - @module ember - @submodule ember-handlebars - */ - - /** - The internal class used to create text inputs when the `{{input}}` - helper is used with `type` of `checkbox`. - - See [handlebars.helpers.input](/api/classes/Ember.Handlebars.helpers.html#method_input) for usage details. - - ## Direct manipulation of `checked` - - The `checked` attribute of an `Ember.Checkbox` object should always be set - through the Ember object or by interacting with its rendered element - representation via the mouse, keyboard, or touch. Updating the value of the - checkbox via jQuery will result in the checked value of the object and its - element losing synchronization. - - ## Layout and LayoutName properties - - Because HTML `input` elements are self closing `layout` and `layoutName` - properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s - layout section for more information. - - @class Checkbox - @namespace Ember - @extends Ember.View - */ - var Checkbox = View.extend({ - instrumentDisplay: '{{input type="checkbox"}}', - - classNames: ['ember-checkbox'], - - tagName: 'input', - - attributeBindings: ['type', 'checked', 'indeterminate', 'disabled', 'tabindex', 'name', - 'autofocus', 'required', 'form'], - - type: "checkbox", - checked: false, - disabled: false, - indeterminate: false, - - init: function() { - this._super(); - this.on("change", this, this._updateElementValue); - }, - - didInsertElement: function() { - this._super(); - get(this, 'element').indeterminate = !!get(this, 'indeterminate'); - }, - - _updateElementValue: function() { - set(this, 'checked', this.$().prop('checked')); - } - }); - - __exports__["default"] = Checkbox; - }); -define("ember-handlebars/controls/select", - ["ember-handlebars-compiler","ember-metal/enumerable_utils","ember-metal/property_get","ember-metal/property_set","ember-views/views/view","ember-views/views/collection_view","ember-metal/utils","ember-metal/is_none","ember-metal/computed","ember-runtime/system/native_array","ember-metal/mixin","ember-metal/properties","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { - "use strict"; - /*jshint eqeqeq:false newcap:false */ - - /** - @module ember - @submodule ember-handlebars - */ - - var EmberHandlebars = __dependency1__["default"]; - var EnumerableUtils = __dependency2__["default"]; - var get = __dependency3__.get; - var set = __dependency4__.set; - var View = __dependency5__.View; - var CollectionView = __dependency6__["default"]; - var isArray = __dependency7__.isArray; - var isNone = __dependency8__["default"]; - var computed = __dependency9__.computed; - var A = __dependency10__.A; - var observer = __dependency11__.observer; - var defineProperty = __dependency12__.defineProperty; - - var indexOf = EnumerableUtils.indexOf, - indexesOf = EnumerableUtils.indexesOf, - forEach = EnumerableUtils.forEach, - replace = EnumerableUtils.replace, - precompileTemplate = EmberHandlebars.compile; - - var SelectOption = View.extend({ - instrumentDisplay: 'Ember.SelectOption', - - tagName: 'option', - attributeBindings: ['value', 'selected'], - - defaultTemplate: function(context, options) { - options = { data: options.data, hash: {} }; - EmberHandlebars.helpers.bind.call(context, "view.label", options); - }, - - init: function() { - this.labelPathDidChange(); - this.valuePathDidChange(); - - this._super(); - }, - - selected: computed(function() { - var content = get(this, 'content'), - selection = get(this, 'parentView.selection'); - if (get(this, 'parentView.multiple')) { - return selection && indexOf(selection, content.valueOf()) > -1; - } else { - // Primitives get passed through bindings as objects... since - // `new Number(4) !== 4`, we use `==` below - return content == selection; - } - }).property('content', 'parentView.selection'), - - labelPathDidChange: observer('parentView.optionLabelPath', function() { - var labelPath = get(this, 'parentView.optionLabelPath'); - - if (!labelPath) { return; } - - defineProperty(this, 'label', computed(function() { - return get(this, labelPath); - }).property(labelPath)); - }), - - valuePathDidChange: observer('parentView.optionValuePath', function() { - var valuePath = get(this, 'parentView.optionValuePath'); - - if (!valuePath) { return; } - - defineProperty(this, 'value', computed(function() { - return get(this, valuePath); - }).property(valuePath)); - }) - }); - - var SelectOptgroup = CollectionView.extend({ - instrumentDisplay: 'Ember.SelectOptgroup', - - tagName: 'optgroup', - attributeBindings: ['label'], - - selectionBinding: 'parentView.selection', - multipleBinding: 'parentView.multiple', - optionLabelPathBinding: 'parentView.optionLabelPath', - optionValuePathBinding: 'parentView.optionValuePath', - - itemViewClassBinding: 'parentView.optionView' - }); - - /** - The `Ember.Select` view class renders a - [select](https://developer.mozilla.org/en/HTML/Element/select) HTML element, - allowing the user to choose from a list of options. - - The text and `value` property of each ` - - - ``` - - The `value` attribute of the selected `"); - return buffer; - } - -function program3(depth0,data) { - - var stack1; - stack1 = helpers.each.call(depth0, "view.groupedContent", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(4, program4, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - else { data.buffer.push(''); } - } -function program4(depth0,data) { - - - data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.groupView", {hash:{ - 'content': ("content"), - 'label': ("label") - },hashTypes:{'content': "ID",'label': "ID"},hashContexts:{'content': depth0,'label': depth0},contexts:[depth0],types:["ID"],data:data}))); - } - -function program6(depth0,data) { - - var stack1; - stack1 = helpers.each.call(depth0, "view.content", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(7, program7, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - else { data.buffer.push(''); } - } -function program7(depth0,data) { - - - data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.optionView", {hash:{ - 'content': ("") - },hashTypes:{'content': "ID"},hashContexts:{'content': depth0},contexts:[depth0],types:["ID"],data:data}))); - } - - stack1 = helpers['if'].call(depth0, "view.prompt", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - stack1 = helpers['if'].call(depth0, "view.optionGroupPath", {hash:{},hashTypes:{},hashContexts:{},inverse:self.program(6, program6, data),fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - return buffer; - -}), - attributeBindings: ['multiple', 'disabled', 'tabindex', 'name', 'required', 'autofocus', - 'form', 'size'], - - /** - The `multiple` attribute of the select element. Indicates whether multiple - options can be selected. - - @property multiple - @type Boolean - @default false - */ - multiple: false, - - /** - The `disabled` attribute of the select element. Indicates whether - the element is disabled from interactions. - - @property disabled - @type Boolean - @default false - */ - disabled: false, - - /** - The `required` attribute of the select element. Indicates whether - a selected option is required for form validation. - - @property required - @type Boolean - @default false - @since 1.5.0 - */ - required: false, - - /** - The list of options. - - If `optionLabelPath` and `optionValuePath` are not overridden, this should - be a list of strings, which will serve simultaneously as labels and values. - - Otherwise, this should be a list of objects. For instance: - - ```javascript - Ember.Select.create({ - content: A([ - { id: 1, firstName: 'Yehuda' }, - { id: 2, firstName: 'Tom' } - ]), - optionLabelPath: 'content.firstName', - optionValuePath: 'content.id' - }); - ``` - - @property content - @type Array - @default null - */ - content: null, - - /** - When `multiple` is `false`, the element of `content` that is currently - selected, if any. - - When `multiple` is `true`, an array of such elements. - - @property selection - @type Object or Array - @default null - */ - selection: null, - - /** - In single selection mode (when `multiple` is `false`), value can be used to - get the current selection's value or set the selection by it's value. - - It is not currently supported in multiple selection mode. - - @property value - @type String - @default null - */ - value: computed(function(key, value) { - if (arguments.length === 2) { return value; } - var valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''); - return valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection'); - }).property('selection'), - - /** - If given, a top-most dummy option will be rendered to serve as a user - prompt. - - @property prompt - @type String - @default null - */ - prompt: null, - - /** - The path of the option labels. See [content](/api/classes/Ember.Select.html#property_content). - - @property optionLabelPath - @type String - @default 'content' - */ - optionLabelPath: 'content', - - /** - The path of the option values. See [content](/api/classes/Ember.Select.html#property_content). - - @property optionValuePath - @type String - @default 'content' - */ - optionValuePath: 'content', - - /** - The path of the option group. - When this property is used, `content` should be sorted by `optionGroupPath`. - - @property optionGroupPath - @type String - @default null - */ - optionGroupPath: null, - - /** - The view class for optgroup. - - @property groupView - @type Ember.View - @default Ember.SelectOptgroup - */ - groupView: SelectOptgroup, - - groupedContent: computed(function() { - var groupPath = get(this, 'optionGroupPath'); - var groupedContent = A(); - var content = get(this, 'content') || []; - - forEach(content, function(item) { - var label = get(item, groupPath); - - if (get(groupedContent, 'lastObject.label') !== label) { - groupedContent.pushObject({ - label: label, - content: A() - }); - } - - get(groupedContent, 'lastObject.content').push(item); - }); - - return groupedContent; - }).property('optionGroupPath', 'content.@each'), - - /** - The view class for option. - - @property optionView - @type Ember.View - @default Ember.SelectOption - */ - optionView: SelectOption, - - _change: function() { - if (get(this, 'multiple')) { - this._changeMultiple(); - } else { - this._changeSingle(); - } - }, - - selectionDidChange: observer('selection.@each', function() { - var selection = get(this, 'selection'); - if (get(this, 'multiple')) { - if (!isArray(selection)) { - set(this, 'selection', A([selection])); - return; - } - this._selectionDidChangeMultiple(); - } else { - this._selectionDidChangeSingle(); - } - }), - - valueDidChange: observer('value', function() { - var content = get(this, 'content'), - value = get(this, 'value'), - valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''), - selectedValue = (valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection')), - selection; - - if (value !== selectedValue) { - selection = content ? content.find(function(obj) { - return value === (valuePath ? get(obj, valuePath) : obj); - }) : null; - - this.set('selection', selection); - } - }), - - - _triggerChange: function() { - var selection = get(this, 'selection'); - var value = get(this, 'value'); - - if (!isNone(selection)) { this.selectionDidChange(); } - if (!isNone(value)) { this.valueDidChange(); } - - this._change(); - }, - - _changeSingle: function() { - var selectedIndex = this.$()[0].selectedIndex, - content = get(this, 'content'), - prompt = get(this, 'prompt'); - - if (!content || !get(content, 'length')) { return; } - if (prompt && selectedIndex === 0) { set(this, 'selection', null); return; } - - if (prompt) { selectedIndex -= 1; } - set(this, 'selection', content.objectAt(selectedIndex)); - }, - - - _changeMultiple: function() { - var options = this.$('option:selected'), - prompt = get(this, 'prompt'), - offset = prompt ? 1 : 0, - content = get(this, 'content'), - selection = get(this, 'selection'); - - if (!content) { return; } - if (options) { - var selectedIndexes = options.map(function() { - return this.index - offset; - }).toArray(); - var newSelection = content.objectsAt(selectedIndexes); - - if (isArray(selection)) { - replace(selection, 0, get(selection, 'length'), newSelection); - } else { - set(this, 'selection', newSelection); - } - } - }, - - _selectionDidChangeSingle: function() { - var el = this.get('element'); - if (!el) { return; } - - var content = get(this, 'content'), - selection = get(this, 'selection'), - selectionIndex = content ? indexOf(content, selection) : -1, - prompt = get(this, 'prompt'); - - if (prompt) { selectionIndex += 1; } - if (el) { el.selectedIndex = selectionIndex; } - }, - - _selectionDidChangeMultiple: function() { - var content = get(this, 'content'), - selection = get(this, 'selection'), - selectedIndexes = content ? indexesOf(content, selection) : [-1], - prompt = get(this, 'prompt'), - offset = prompt ? 1 : 0, - options = this.$('option'), - adjusted; - - if (options) { - options.each(function() { - adjusted = this.index > -1 ? this.index - offset : -1; - this.selected = indexOf(selectedIndexes, adjusted) > -1; - }); - } - }, - - init: function() { - this._super(); - this.on("didInsertElement", this, this._triggerChange); - this.on("change", this, this._change); - } - }); - - __exports__["default"] = Select - __exports__.Select = Select; - __exports__.SelectOption = SelectOption; - __exports__.SelectOptgroup = SelectOptgroup; - }); -define("ember-handlebars/controls/text_area", - ["ember-metal/property_get","ember-views/views/component","ember-handlebars/controls/text_support","ember-metal/mixin","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - - /** - @module ember - @submodule ember-handlebars - */ - var get = __dependency1__.get; - var Component = __dependency2__["default"]; - var TextSupport = __dependency3__["default"]; - var observer = __dependency4__.observer; - - /** - The internal class used to create textarea element when the `{{textarea}}` - helper is used. - - See [handlebars.helpers.textarea](/api/classes/Ember.Handlebars.helpers.html#method_textarea) for usage details. - - ## Layout and LayoutName properties - - Because HTML `textarea` elements do not contain inner HTML the `layout` and - `layoutName` properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s - layout section for more information. - - @class TextArea - @namespace Ember - @extends Ember.Component - @uses Ember.TextSupport - */ - var TextArea = Component.extend(TextSupport, { - instrumentDisplay: '{{textarea}}', - - classNames: ['ember-text-area'], - - tagName: "textarea", - attributeBindings: ['rows', 'cols', 'name', 'selectionEnd', 'selectionStart', 'wrap'], - rows: null, - cols: null, - - _updateElementValue: observer('value', function() { - // We do this check so cursor position doesn't get affected in IE - var value = get(this, 'value'), - $el = this.$(); - if ($el && value !== $el.val()) { - $el.val(value); - } - }), - - init: function() { - this._super(); - this.on("didInsertElement", this, this._updateElementValue); - } - - }); - - __exports__["default"] = TextArea; - }); -define("ember-handlebars/controls/text_field", - ["ember-metal/property_get","ember-metal/property_set","ember-views/views/component","ember-handlebars/controls/text_support","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-handlebars - */ - - var get = __dependency1__.get; - var set = __dependency2__.set; - var Component = __dependency3__["default"]; - var TextSupport = __dependency4__["default"]; - - /** - - The internal class used to create text inputs when the `{{input}}` - helper is used with `type` of `text`. - - See [Handlebars.helpers.input](/api/classes/Ember.Handlebars.helpers.html#method_input) for usage details. - - ## Layout and LayoutName properties - - Because HTML `input` elements are self closing `layout` and `layoutName` - properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s - layout section for more information. - - @class TextField - @namespace Ember - @extends Ember.Component - @uses Ember.TextSupport - */ - var TextField = Component.extend(TextSupport, { - instrumentDisplay: '{{input type="text"}}', - - classNames: ['ember-text-field'], - tagName: "input", - attributeBindings: ['type', 'value', 'size', 'pattern', 'name', 'min', 'max', - 'accept', 'autocomplete', 'autosave', 'formaction', - 'formenctype', 'formmethod', 'formnovalidate', 'formtarget', - 'height', 'inputmode', 'list', 'multiple', 'pattern', 'step', - 'width'], - - /** - The `value` attribute of the input element. As the user inputs text, this - property is updated live. - - @property value - @type String - @default "" - */ - value: "", - - /** - The `type` attribute of the input element. - - @property type - @type String - @default "text" - */ - type: "text", - - /** - The `size` of the text field in characters. - - @property size - @type String - @default null - */ - size: null, - - /** - The `pattern` attribute of input element. - - @property pattern - @type String - @default null - */ - pattern: null, - - /** - The `min` attribute of input element used with `type="number"` or `type="range"`. - - @property min - @type String - @default null - @since 1.4.0 - */ - min: null, - - /** - The `max` attribute of input element used with `type="number"` or `type="range"`. - - @property max - @type String - @default null - @since 1.4.0 - */ - max: null - }); - - __exports__["default"] = TextField; - }); -define("ember-handlebars/controls/text_support", - ["ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-runtime/mixins/target_action_support","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-handlebars - */ - - var get = __dependency1__.get; - var set = __dependency2__.set; - var Mixin = __dependency3__.Mixin; - var TargetActionSupport = __dependency4__["default"]; - - /** - Shared mixin used by `Ember.TextField` and `Ember.TextArea`. - - @class TextSupport - @namespace Ember - @uses Ember.TargetActionSupport - @extends Ember.Mixin - @private - */ - var TextSupport = Mixin.create(TargetActionSupport, { - value: "", - - attributeBindings: ['placeholder', 'disabled', 'maxlength', 'tabindex', 'readonly', - 'autofocus', 'form', 'selectionDirection', 'spellcheck', 'required', - 'title', 'autocapitalize', 'autocorrect'], - placeholder: null, - disabled: false, - maxlength: null, - - init: function() { - this._super(); - this.on("focusOut", this, this._elementValueDidChange); - this.on("change", this, this._elementValueDidChange); - this.on("paste", this, this._elementValueDidChange); - this.on("cut", this, this._elementValueDidChange); - this.on("input", this, this._elementValueDidChange); - this.on("keyUp", this, this.interpretKeyEvents); - }, - - /** - The action to be sent when the user presses the return key. - - This is similar to the `{{action}}` helper, but is fired when - the user presses the return key when editing a text field, and sends - the value of the field as the context. - - @property action - @type String - @default null - */ - action: null, - - /** - The event that should send the action. - - Options are: - - * `enter`: the user pressed enter - * `keyPress`: the user pressed a key - - @property onEvent - @type String - @default enter - */ - onEvent: 'enter', - - /** - Whether they `keyUp` event that triggers an `action` to be sent continues - propagating to other views. - - By default, when the user presses the return key on their keyboard and - the text field has an `action` set, the action will be sent to the view's - controller and the key event will stop propagating. - - If you would like parent views to receive the `keyUp` event even after an - action has been dispatched, set `bubbles` to true. - - @property bubbles - @type Boolean - @default false - */ - bubbles: false, - - interpretKeyEvents: function(event) { - var map = TextSupport.KEY_EVENTS; - var method = map[event.keyCode]; - - this._elementValueDidChange(); - if (method) { return this[method](event); } - }, - - _elementValueDidChange: function() { - set(this, 'value', this.$().val()); - }, - - /** - The action to be sent when the user inserts a new line. - - Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 13. - Uses sendAction to send the `enter` action to the controller. - - @method insertNewline - @param {Event} event - */ - insertNewline: function(event) { - sendAction('enter', this, event); - sendAction('insert-newline', this, event); - }, - - /** - Called when the user hits escape. - - Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 27. - Uses sendAction to send the `escape-press` action to the controller. - - @method cancel - @param {Event} event - */ - cancel: function(event) { - sendAction('escape-press', this, event); - }, - - /** - Called when the text area is focused. - - @method focusIn - @param {Event} event - */ - focusIn: function(event) { - sendAction('focus-in', this, event); - }, - - /** - Called when the text area is blurred. - - @method focusOut - @param {Event} event - */ - focusOut: function(event) { - sendAction('focus-out', this, event); - }, - - /** - The action to be sent when the user presses a key. Enabled by setting - the `onEvent` property to `keyPress`. - - Uses sendAction to send the `keyPress` action to the controller. - - @method keyPress - @param {Event} event - */ - keyPress: function(event) { - sendAction('key-press', this, event); - } - - }); - - TextSupport.KEY_EVENTS = { - 13: 'insertNewline', - 27: 'cancel' - }; - - // In principle, this shouldn't be necessary, but the legacy - // sendAction semantics for TextField are different from - // the component semantics so this method normalizes them. - function sendAction(eventName, view, event) { - var action = get(view, eventName), - on = get(view, 'onEvent'), - value = get(view, 'value'); - - // back-compat support for keyPress as an event name even though - // it's also a method name that consumes the event (and therefore - // incompatible with sendAction semantics). - if (on === eventName || (on === 'keyPress' && eventName === 'key-press')) { - view.sendAction('action', value); - } - - view.sendAction(eventName, value); - - if (action || on === eventName) { - if(!get(view, 'bubbles')) { - event.stopPropagation(); - } - } - } - - __exports__["default"] = TextSupport; - }); -define("ember-handlebars/ext", - ["ember-metal/core","ember-runtime/system/string","ember-handlebars-compiler","ember-metal/property_get","ember-metal/binding","ember-metal/error","ember-metal/mixin","ember-metal/is_empty","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // Ember.FEATURES, Ember.assert, Ember.Handlebars, Ember.lookup - // var emberAssert = Ember.assert; - - var fmt = __dependency2__.fmt; - - var EmberHandlebars = __dependency3__["default"]; - var helpers = EmberHandlebars.helpers; - - var get = __dependency4__.get; - var isGlobalPath = __dependency5__.isGlobalPath; - var EmberError = __dependency6__["default"]; - var IS_BINDING = __dependency7__.IS_BINDING; - - // late bound via requireModule because of circular dependencies. - var resolveHelper, - SimpleHandlebarsView; - - var isEmpty = __dependency8__["default"]; - - var slice = [].slice, originalTemplate = EmberHandlebars.template; - - /** - If a path starts with a reserved keyword, returns the root - that should be used. - - @private - @method normalizePath - @for Ember - @param root {Object} - @param path {String} - @param data {Hash} - */ - function normalizePath(root, path, data) { - var keywords = (data && data.keywords) || {}, - keyword, isKeyword; - - // Get the first segment of the path. For example, if the - // path is "foo.bar.baz", returns "foo". - keyword = path.split('.', 1)[0]; - - // Test to see if the first path is a keyword that has been - // passed along in the view's data hash. If so, we will treat - // that object as the new root. - if (keywords.hasOwnProperty(keyword)) { - // Look up the value in the template's data hash. - root = keywords[keyword]; - isKeyword = true; - - // Handle cases where the entire path is the reserved - // word. In that case, return the object itself. - if (path === keyword) { - path = ''; - } else { - // Strip the keyword from the path and look up - // the remainder from the newly found root. - path = path.substr(keyword.length+1); - } - } - - return { root: root, path: path, isKeyword: isKeyword }; - }; - - - /** - Lookup both on root and on window. If the path starts with - a keyword, the corresponding object will be looked up in the - template's data hash and used to resolve the path. - - @method get - @for Ember.Handlebars - @param {Object} root The object to look up the property on - @param {String} path The path to be lookedup - @param {Object} options The template's option hash - */ - function handlebarsGet(root, path, options) { - var data = options && options.data, - normalizedPath = normalizePath(root, path, data), - value; - - - root = normalizedPath.root; - path = normalizedPath.path; - - value = get(root, path); - - if (value === undefined && root !== Ember.lookup && isGlobalPath(path)) { - value = get(Ember.lookup, path); - } - - - return value; - } - - /** - This method uses `Ember.Handlebars.get` to lookup a value, then ensures - that the value is escaped properly. - - If `unescaped` is a truthy value then the escaping will not be performed. - - @method getEscaped - @for Ember.Handlebars - @param {Object} root The object to look up the property on - @param {String} path The path to be lookedup - @param {Object} options The template's option hash - @since 1.4.0 - */ - function getEscaped(root, path, options) { - var result = handlebarsGet(root, path, options); - - if (result === null || result === undefined) { - result = ""; - } else if (!(result instanceof Handlebars.SafeString)) { - result = String(result); - } - if (!options.hash.unescaped){ - result = Handlebars.Utils.escapeExpression(result); - } - - return result; - }; - - function resolveParams(context, params, options) { - var resolvedParams = [], types = options.types, param, type; - - for (var i=0, l=params.length; i{{user.name}} - -
-
{{user.role.label}}
- {{user.role.id}} - -

{{user.role.description}}

-
- ``` - - `{{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.name}}
- -
- {{#with user.role}} -
{{label}}
- {{id}} - -

{{description}}

- {{/with}} -
- ``` - - ### `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}} -
- There are {{blogPosts.length}} blog posts written by {{user.name}}. -
- - {{#each post in blogPosts}} -
  • {{post.title}}
  • - {{/each}} - {{/with}} - ``` - - Without the `as` operator, it would be impossible to reference `user.name` in the example above. - - NOTE: The alias should not reuse a name from the bound property path. - For example: `{{#with foo.bar as foo}}` is not supported because it attempts to alias using - the first part of the property path, `foo`. Instead, use `{{#with foo.bar as baz}}`. - - ### `controller` option - - Adding `controller='something'` instructs the `{{with}}` helper to create and use an instance of - the specified controller with the new context as its content. - - This is very similar to using an `itemController` option with the `{{each}}` helper. - - ```handlebars - {{#with users.posts controller='userBlogPosts'}} - {{!- The current context is wrapped in our controller instance }} - {{/with}} - ``` - - In the above example, the template provided to the `{{with}}` block is now wrapped in the - `userBlogPost` controller, which provides a very elegant way to decorate the context with custom - functions/properties. - - @method with - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string - */ - function withHelper(context, options) { - var bindContext, preserveContext, controller, helperName = 'with'; - - if (arguments.length === 4) { - var keywordName, path, rootPath, normalized, contextPath; - - Ember.assert("If you pass more than one argument to the with helper, it must be in the form #with foo as bar", arguments[1] === "as"); - options = arguments[3]; - keywordName = arguments[2]; - path = arguments[0]; - - if (path) { - helperName += ' ' + path + ' as ' + keywordName; - } - - Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); - - var localizedOptions = o_create(options); - localizedOptions.data = o_create(options.data); - localizedOptions.data.keywords = o_create(options.data.keywords || {}); - - if (isGlobalPath(path)) { - contextPath = path; - } else { - normalized = normalizePath(this, path, options.data); - path = normalized.path; - rootPath = normalized.root; - - // This is a workaround for the fact that you cannot bind separate objects - // together. When we implement that functionality, we should use it here. - var contextKey = jQuery.expando + guidFor(rootPath); - localizedOptions.data.keywords[contextKey] = rootPath; - // if the path is '' ("this"), just bind directly to the current context - contextPath = path ? contextKey + '.' + path : contextKey; - } - - localizedOptions.hash.keywordName = keywordName; - localizedOptions.hash.keywordPath = contextPath; - - bindContext = this; - context = path; - options = localizedOptions; - preserveContext = true; - } else { - Ember.assert("You must pass exactly one argument to the with helper", arguments.length === 2); - Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); - - helperName += ' ' + context; - bindContext = options.contexts[0]; - preserveContext = false; - } - - options.helperName = helperName; - options.isWithHelper = true; - - return bind.call(bindContext, context, options, preserveContext, exists); - } - /** - See [boundIf](/api/classes/Ember.Handlebars.helpers.html#method_boundIf) - and [unboundIf](/api/classes/Ember.Handlebars.helpers.html#method_unboundIf) - - @method if - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string - */ - function ifHelper(context, options) { - Ember.assert("You must pass exactly one argument to the if helper", arguments.length === 2); - Ember.assert("You must pass a block to the if helper", options.fn && options.fn !== Handlebars.VM.noop); - - options.helperName = options.helperName || ('if ' + context); - - if (options.data.isUnbound) { - return helpers.unboundIf.call(options.contexts[0], context, options); - } else { - return helpers.boundIf.call(options.contexts[0], context, options); - } - } - - /** - @method unless - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string - */ - function unlessHelper(context, options) { - Ember.assert("You must pass exactly one argument to the unless helper", arguments.length === 2); - Ember.assert("You must pass a block to the unless helper", options.fn && options.fn !== Handlebars.VM.noop); - - var fn = options.fn, inverse = options.inverse, helperName = 'unless'; - - if (context) { - helperName += ' ' + context; - } - - options.fn = inverse; - options.inverse = fn; - - options.helperName = options.helperName || helperName; - - if (options.data.isUnbound) { - return helpers.unboundIf.call(options.contexts[0], context, options); - } else { - return helpers.boundIf.call(options.contexts[0], context, options); - } - } - - /** - `bind-attr` allows you to create a binding between DOM element attributes and - Ember objects. For example: - - ```handlebars - imageTitle - ``` - - The above handlebars template will fill the ``'s `src` attribute will - the value of the property referenced with `"imageUrl"` and its `alt` - attribute with the value of the property referenced with `"imageTitle"`. - - If the rendering context of this template is the following object: - - ```javascript - { - imageUrl: 'http://lolcats.info/haz-a-funny', - imageTitle: 'A humorous image of a cat' - } - ``` - - The resulting HTML output will be: - - ```html - A humorous image of a cat - ``` - - `bind-attr` cannot redeclare existing DOM element attributes. The use of `src` - in the following `bind-attr` example will be ignored and the hard coded value - of `src="/failwhale.gif"` will take precedence: - - ```handlebars - imageTitle - ``` - - ### `bind-attr` and the `class` attribute - - `bind-attr` supports a special syntax for handling a number of cases unique - to the `class` DOM element attribute. The `class` attribute combines - multiple discrete values into a single attribute as a space-delimited - list of strings. Each string can be: - - * a string return value of an object's property. - * a boolean return value of an object's property - * a hard-coded value - - A string return value works identically to other uses of `bind-attr`. The - return value of the property will become the value of the attribute. For - example, the following view and template: - - ```javascript - AView = View.extend({ - someProperty: function() { - return "aValue"; - }.property() - }) - ``` - - ```handlebars - - ``` - - A boolean return value will insert a specified class name if the property - returns `true` and remove the class name if the property returns `false`. - - A class name is provided via the syntax - `somePropertyName:class-name-if-true`. - - ```javascript - AView = View.extend({ - someBool: true - }) - ``` - - ```handlebars - - ``` - - Result in the following rendered output: - - ```html - - ``` - - An additional section of the binding can be provided if you want to - replace the existing class instead of removing it when the boolean - value changes: - - ```handlebars - - ``` - - A hard-coded value can be used by prepending `:` to the desired - class name: `:class-name-to-always-apply`. - - ```handlebars - - ``` - - Results in the following rendered output: - - ```html - - ``` - - All three strategies - string return value, boolean return value, and - hard-coded value – can be combined in a single declaration: - - ```handlebars - - ``` - - @method bind-attr - @for Ember.Handlebars.helpers - @param {Hash} options - @return {String} HTML string - */ - function bindAttrHelper(options) { - var attrs = options.hash; - - Ember.assert("You must specify at least one hash argument to bind-attr", !!keys(attrs).length); - - var view = options.data.view; - var ret = []; - - // we relied on the behavior of calling without - // context to mean this === window, but when running - // "use strict", it's possible for this to === undefined; - var ctx = this || window; - - // Generate a unique id for this element. This will be added as a - // data attribute to the element so it can be looked up when - // the bound property changes. - var dataId = ++Ember.uuid; - - // Handle classes differently, as we can bind multiple classes - var classBindings = attrs['class']; - if (classBindings != null) { - var classResults = bindClasses(ctx, classBindings, view, dataId, options); - - ret.push('class="' + Handlebars.Utils.escapeExpression(classResults.join(' ')) + '"'); - delete attrs['class']; - } - - var attrKeys = keys(attrs); - - // For each attribute passed, create an observer and emit the - // current value of the property as an attribute. - forEach.call(attrKeys, function(attr) { - var path = attrs[attr], - normalized; - - Ember.assert(fmt("You must provide an expression as the value of bound attribute. You specified: %@=%@", [attr, path]), typeof path === 'string'); - - normalized = normalizePath(ctx, path, options.data); - - var value = (path === 'this') ? normalized.root : handlebarsGet(ctx, path, options), - type = typeOf(value); - - Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [value]), value === null || value === undefined || type === 'number' || type === 'string' || type === 'boolean'); - - var observer, invoker; - - observer = function observer() { - var result = handlebarsGet(ctx, path, options); - - Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [result]), - result === null || result === undefined || typeof result === 'number' || - typeof result === 'string' || typeof result === 'boolean'); - - var elem = view.$("[data-bindattr-" + dataId + "='" + dataId + "']"); - - // If we aren't able to find the element, it means the element - // to which we were bound has been removed from the view. - // In that case, we can assume the template has been re-rendered - // and we need to clean up the observer. - if (!elem || elem.length === 0) { - removeObserver(normalized.root, normalized.path, invoker); - return; - } - - View.applyAttributeBindings(elem, attr, result); - }; - - // Add an observer to the view for when the property changes. - // When the observer fires, find the element using the - // unique data id and update the attribute to the new value. - // Note: don't add observer when path is 'this' or path - // is whole keyword e.g. {{#each x in list}} ... {{bind-attr attr="x"}} - if (path !== 'this' && !(normalized.isKeyword && normalized.path === '' )) { - view.registerObserver(normalized.root, normalized.path, observer); - } - - // if this changes, also change the logic in ember-views/lib/views/view.js - if ((type === 'string' || (type === 'number' && !isNaN(value)))) { - ret.push(attr + '="' + Handlebars.Utils.escapeExpression(value) + '"'); - } else if (value && type === 'boolean') { - // The developer controls the attr name, so it should always be safe - ret.push(attr + '="' + attr + '"'); - } - }, this); - - // Add the unique identifier - // NOTE: We use all lower-case since Firefox has problems with mixed case in SVG - ret.push('data-bindattr-' + dataId + '="' + dataId + '"'); - return new SafeString(ret.join(' ')); - } - - /** - See `bind-attr` - - @method bindAttr - @for Ember.Handlebars.helpers - @deprecated - @param {Function} context - @param {Hash} options - @return {String} HTML string - */ - function bindAttrHelperDeprecated() { - Ember.warn("The 'bindAttr' view helper is deprecated in favor of 'bind-attr'"); - return helpers['bind-attr'].apply(this, arguments); - } - - /** - Helper that, given a space-separated string of property paths and a context, - returns an array of class names. Calling this method also has the side - effect of setting up observers at those property paths, such that if they - change, the correct class name will be reapplied to the DOM element. - - For example, if you pass the string "fooBar", it will first look up the - "fooBar" value of the context. If that value is true, it will add the - "foo-bar" class to the current element (i.e., the dasherized form of - "fooBar"). If the value is a string, it will add that string as the class. - Otherwise, it will not add any new class name. - - @private - @method bindClasses - @for Ember.Handlebars - @param {Ember.Object} context The context from which to lookup properties - @param {String} classBindings A string, space-separated, of class bindings - to use - @param {View} view The view in which observers should look for the - element to update - @param {Srting} bindAttrId Optional bindAttr id used to lookup elements - @return {Array} An array of class names to add - */ - function bindClasses(context, classBindings, view, bindAttrId, options) { - var ret = [], newClass, value, elem; - - // Helper method to retrieve the property from the context and - // determine which class string to return, based on whether it is - // a Boolean or not. - var classStringForPath = function(root, parsedPath, options) { - var val, - path = parsedPath.path; - - if (path === 'this') { - val = root; - } else if (path === '') { - val = true; - } else { - val = handlebarsGet(root, path, options); - } - - return View._classStringForValue(path, val, parsedPath.className, parsedPath.falsyClassName); - }; - - // For each property passed, loop through and setup - // an observer. - forEach.call(classBindings.split(' '), function(binding) { - - // Variable in which the old class value is saved. The observer function - // closes over this variable, so it knows which string to remove when - // the property changes. - var oldClass; - - var observer, invoker; - - var parsedPath = View._parsePropertyPath(binding), - path = parsedPath.path, - pathRoot = context, - normalized; - - if (path !== '' && path !== 'this') { - normalized = normalizePath(context, path, options.data); - - pathRoot = normalized.root; - path = normalized.path; - } - - // Set up an observer on the context. If the property changes, toggle the - // class name. - observer = function() { - // Get the current value of the property - newClass = classStringForPath(context, parsedPath, options); - elem = bindAttrId ? view.$("[data-bindattr-" + bindAttrId + "='" + bindAttrId + "']") : view.$(); - - // If we can't find the element anymore, a parent template has been - // re-rendered and we've been nuked. Remove the observer. - if (!elem || elem.length === 0) { - removeObserver(pathRoot, path, invoker); - } else { - // If we had previously added a class to the element, remove it. - if (oldClass) { - elem.removeClass(oldClass); - } - - // If necessary, add a new class. Make sure we keep track of it so - // it can be removed in the future. - if (newClass) { - elem.addClass(newClass); - oldClass = newClass; - } else { - oldClass = null; - } - } - }; - - if (path !== '' && path !== 'this') { - view.registerObserver(pathRoot, path, observer); - } - - // We've already setup the observer; now we just need to figure out the - // correct behavior right now on the first pass through. - value = classStringForPath(context, parsedPath, options); - - if (value) { - ret.push(value); - - // Make sure we save the current value so that it can be removed if the - // observer fires. - oldClass = value; - } - }); - - return ret; - }; - - __exports__.bind = bind; - __exports__._triageMustacheHelper = _triageMustacheHelper; - __exports__.resolveHelper = resolveHelper; - __exports__.bindHelper = bindHelper; - __exports__.boundIfHelper = boundIfHelper; - __exports__.unboundIfHelper = unboundIfHelper; - __exports__.withHelper = withHelper; - __exports__.ifHelper = ifHelper; - __exports__.unlessHelper = unlessHelper; - __exports__.bindAttrHelper = bindAttrHelper; - __exports__.bindAttrHelperDeprecated = bindAttrHelperDeprecated; - __exports__.bindClasses = bindClasses; - }); -define("ember-handlebars/helpers/collection", - ["ember-metal/core","ember-metal/utils","ember-handlebars-compiler","ember-runtime/system/string","ember-metal/property_get","ember-handlebars/ext","ember-handlebars/helpers/view","ember-metal/computed","ember-views/views/collection_view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-handlebars - */ - - var Ember = __dependency1__["default"]; - // Ember.assert, Ember.deprecate - var inspect = __dependency2__.inspect; - - // var emberAssert = Ember.assert; - // emberDeprecate = Ember.deprecate; - - var EmberHandlebars = __dependency3__["default"]; - var helpers = EmberHandlebars.helpers; - - var fmt = __dependency4__.fmt; - var get = __dependency5__.get; - var handlebarsGet = __dependency6__.handlebarsGet; - var ViewHelper = __dependency7__.ViewHelper; - var computed = __dependency8__.computed; - var CollectionView = __dependency9__["default"]; - - var alias = computed.alias; - /** - `{{collection}}` is a `Ember.Handlebars` helper for adding instances of - `Ember.CollectionView` to a template. See [Ember.CollectionView](/api/classes/Ember.CollectionView.html) - for additional information on how a `CollectionView` functions. - - `{{collection}}`'s primary use is as a block helper with a `contentBinding` - option pointing towards an `Ember.Array`-compatible object. An `Ember.View` - instance will be created for each item in its `content` property. Each view - will have its own `content` property set to the appropriate item in the - collection. - - The provided block will be applied as the template for each item's view. - - Given an empty `` the following template: - - ```handlebars - {{#collection contentBinding="App.items"}} - Hi {{view.content.name}} - {{/collection}} - ``` - - And the following application code - - ```javascript - App = Ember.Application.create() - App.items = [ - Ember.Object.create({name: 'Dave'}), - Ember.Object.create({name: 'Mary'}), - Ember.Object.create({name: 'Sara'}) - ] - ``` - - Will result in the HTML structure below - - ```html -
    -
    Hi Dave
    -
    Hi Mary
    -
    Hi Sara
    -
    - ``` - - ### 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 -
    -
    Greetings Dave
    -
    Greetings Mary
    -
    Greetings 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 -
    -

    Howdy Dave

    -

    Howdy Mary

    -

    Howdy 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) { - Ember.deprecate("Using the {{collection}} helper without specifying a class has been deprecated as the {{each}} helper now supports the same functionality.", path !== 'collection'); - - // If no path is provided, treat path param as options. - if (path && path.data && path.data.isRenderData) { - options = path; - path = undefined; - Ember.assert("You cannot pass more than one argument to the collection helper", arguments.length === 1); - } else { - Ember.assert("You cannot pass more than one argument to the collection helper", arguments.length === 2); - } - - 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); - Ember.assert(fmt("%@ #collection: Could not find collection class %@", [data.view, path]), !!collectionClass); - } - 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; - Ember.assert('You specified an itemView, but the current context has no ' + - 'container to look the itemView up in. This probably means ' + - 'that you created a view manually, instead of through the ' + - 'container. Instead, use container.lookup("view:viewName"), ' + - 'which will properly instantiate your view.', - controller && controller.container); - container = controller.container; - itemViewClass = container.lookupFactory('view:' + hash.itemView); - Ember.assert('You specified the itemView ' + hash.itemView + ", but it was " + - "not found at " + container.describe("view:" + hash.itemView) + - " (and it was not registered in the container)", !!itemViewClass); - } else if (hash.itemViewClass) { - itemViewClass = handlebarsGet(collectionPrototype, hash.itemViewClass, options); - } else { - itemViewClass = collectionPrototype.itemViewClass; - } - - Ember.assert(fmt("%@ #collection: Could not find itemViewClass %@", [data.view, itemViewClass]), !!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') // -> "" - ``` - - @method debugger - @for Ember.Handlebars.helpers - @param {String} property - */ - function debuggerHelper(options) { - - // These are helpful values you can inspect while debugging. - var templateContext = this; - var typeOfTemplateContext = inspect(templateContext); - - debugger; - } - - __exports__.logHelper = logHelper; - __exports__.debuggerHelper = debuggerHelper; - }); -define("ember-handlebars/helpers/each", - ["ember-metal/core","ember-handlebars-compiler","ember-runtime/system/string","ember-metal/property_get","ember-metal/property_set","ember-handlebars/views/metamorph_view","ember-views/views/collection_view","ember-metal/binding","ember-runtime/controllers/controller","ember-runtime/controllers/array_controller","ember-runtime/mixins/array","ember-runtime/copy","ember-metal/run_loop","ember-metal/observer","ember-metal/events","ember-handlebars/ext","ember-metal/computed","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __exports__) { - "use strict"; - - /** - @module ember - @submodule ember-handlebars - */ - var Ember = __dependency1__["default"]; - // Ember.assert;, Ember.K - // var emberAssert = Ember.assert, - var K = Ember.K; - - var EmberHandlebars = __dependency2__["default"]; - var helpers = EmberHandlebars.helpers; - - var fmt = __dependency3__.fmt; - var get = __dependency4__.get; - var set = __dependency5__.set; - var _Metamorph = __dependency6__._Metamorph; - var _MetamorphView = __dependency6__._MetamorphView; - var CollectionView = __dependency7__["default"]; - var Binding = __dependency8__.Binding; - var ControllerMixin = __dependency9__.ControllerMixin; - var ArrayController = __dependency10__["default"]; - var EmberArray = __dependency11__["default"]; - var copy = __dependency12__["default"]; - var run = __dependency13__["default"]; - var addObserver = __dependency14__.addObserver; - var removeObserver = __dependency14__.removeObserver; - var addBeforeObserver = __dependency14__.addBeforeObserver; - var removeBeforeObserver = __dependency14__.removeBeforeObserver; - var on = __dependency15__.on; - var handlebarsGet = __dependency16__.handlebarsGet; - var computed = __dependency17__.computed; - - var handlebarsGet = __dependency16__.handlebarsGet; - - var EachView = CollectionView.extend(_Metamorph, { - - init: function() { - var itemController = get(this, 'itemController'); - var binding; - - if (itemController) { - var controller = get(this, 'controller.container').lookupFactory('controller:array').create({ - _isVirtual: true, - parentController: get(this, 'controller'), - itemController: itemController, - target: get(this, 'controller'), - _eachView: this - }); - - this.disableContentObservers(function() { - set(this, 'content', controller); - binding = new Binding('content', '_eachView.dataSource').oneWay(); - binding.connect(controller); - }); - - set(this, '_arrayController', controller); - } else { - this.disableContentObservers(function() { - binding = new Binding('content', 'dataSource').oneWay(); - binding.connect(this); - }); - } - - return this._super(); - }, - - _assertArrayLike: function(content) { - Ember.assert(fmt("The value that #each loops over must be an Array. You " + - "passed %@, but it should have been an ArrayController", - [content.constructor]), - !ControllerMixin.detect(content) || - (content && content.isGenerated) || - content instanceof ArrayController); - Ember.assert(fmt("The value that #each loops over must be an Array. You passed %@", [(ControllerMixin.detect(content) && content.get('model') !== undefined) ? fmt("'%@' (wrapped in %@)", [content.get('model'), content]) : content]), EmberArray.detect(content)); - }, - - disableContentObservers: function(callback) { - removeBeforeObserver(this, 'content', null, '_contentWillChange'); - removeObserver(this, 'content', null, '_contentDidChange'); - - callback.call(this); - - addBeforeObserver(this, 'content', null, '_contentWillChange'); - addObserver(this, 'content', null, '_contentDidChange'); - }, - - itemViewClass: _MetamorphView, - emptyViewClass: _MetamorphView, - - createChildView: function(view, attrs) { - view = this._super(view, attrs); - - // At the moment, if a container view subclass wants - // to insert keywords, it is responsible for cloning - // the keywords hash. This will be fixed momentarily. - var keyword = get(this, 'keyword'); - var content = get(view, 'content'); - - if (keyword) { - var data = get(view, 'templateData'); - - data = copy(data); - data.keywords = view.cloneKeywords(); - set(view, 'templateData', data); - - // In this case, we do not bind, because the `content` of - // a #each item cannot change. - data.keywords[keyword] = content; - } - - // If {{#each}} is looping over an array of controllers, - // point each child view at their respective controller. - if (content && content.isController) { - set(view, 'controller', content); - } - - return view; - }, - - destroy: function() { - if (!this._super()) { return; } - - var arrayController = get(this, '_arrayController'); - - if (arrayController) { - arrayController.destroy(); - } - - return this; - } - }); - - // Defeatureify doesn't seem to like nested functions that need to be removed - function _addMetamorphCheck() { - EachView.reopen({ - _checkMetamorph: on('didInsertElement', function() { - Ember.assert("The metamorph tags, " + - this.morph.start + " and " + this.morph.end + - ", have different parents.\nThe browser has fixed your template to output valid HTML (for example, check that you have properly closed all tags and have used a TBODY tag when creating a table with '{{#each}}')", - document.getElementById( this.morph.start ).parentNode === - document.getElementById( this.morph.end ).parentNode - ); - }) - }); - } - - // until ember-debug is es6ed - var runInDebug = function(f){f()}; - runInDebug( function() { - _addMetamorphCheck(); - }); - - var GroupedEach = EmberHandlebars.GroupedEach = function(context, path, options) { - var self = this, - normalized = EmberHandlebars.normalizePath(context, path, options.data); - - this.context = context; - this.path = path; - this.options = options; - this.template = options.fn; - this.containingView = options.data.view; - this.normalizedRoot = normalized.root; - this.normalizedPath = normalized.path; - this.content = this.lookupContent(); - - this.addContentObservers(); - this.addArrayObservers(); - - this.containingView.on('willClearRender', function() { - self.destroy(); - }); - }; - - GroupedEach.prototype = { - contentWillChange: function() { - this.removeArrayObservers(); - }, - - contentDidChange: function() { - this.content = this.lookupContent(); - this.addArrayObservers(); - this.rerenderContainingView(); - }, - - contentArrayWillChange: K, - - contentArrayDidChange: function() { - this.rerenderContainingView(); - }, - - lookupContent: function() { - return handlebarsGet(this.normalizedRoot, this.normalizedPath, this.options); - }, - - addArrayObservers: function() { - if (!this.content) { return; } - - this.content.addArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' - }); - }, - - removeArrayObservers: function() { - if (!this.content) { return; } - - this.content.removeArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' - }); - }, - - addContentObservers: function() { - addBeforeObserver(this.normalizedRoot, this.normalizedPath, this, this.contentWillChange); - addObserver(this.normalizedRoot, this.normalizedPath, this, this.contentDidChange); - }, - - removeContentObservers: function() { - removeBeforeObserver(this.normalizedRoot, this.normalizedPath, this.contentWillChange); - removeObserver(this.normalizedRoot, this.normalizedPath, this.contentDidChange); - }, - - render: function() { - if (!this.content) { return; } - - var content = this.content, - contentLength = get(content, 'length'), - data = this.options.data, - template = this.template; - - data.insideEach = true; - for (var i = 0; i < contentLength; i++) { - template(content.objectAt(i), { data: data }); - } - }, - - rerenderContainingView: function() { - var self = this; - run.scheduleOnce('render', this, function() { - // It's possible it's been destroyed after we enqueued a re-render call. - if (!self.destroyed) { - self.containingView.rerender(); - } - }); - }, - - destroy: function() { - this.removeContentObservers(); - if (this.content) { - this.removeArrayObservers(); - } - this.destroyed = true; - } - }; - - /** - The `{{#each}}` helper loops over elements in a collection, rendering its - block once for each item. It is an extension of the base Handlebars `{{#each}}` - helper: - - ```javascript - Developers = [{name: 'Yehuda'},{name: 'Tom'}, {name: 'Paul'}]; - ``` - - ```handlebars - {{#each Developers}} - {{name}} - {{/each}} - ``` - - `{{each}}` supports an alternative syntax with element naming: - - ```handlebars - {{#each person in Developers}} - {{person.name}} - {{/each}} - ``` - - When looping over objects that do not have properties, `{{this}}` can be used - to render the object: - - ```javascript - DeveloperNames = ['Yehuda', 'Tom', 'Paul'] - ``` - - ```handlebars - {{#each DeveloperNames}} - {{this}} - {{/each}} - ``` - ### {{else}} condition - `{{#each}}` can have a matching `{{else}}`. The contents of this block will render - if the collection is empty. - - ``` - {{#each person in Developers}} - {{person.name}} - {{else}} -

    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 -
    -
    Greetings Dave
    -
    Greetings Mary
    -
    Greetings Sara
    -
    - ``` - - 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) { - Ember.assert("If you pass more than one argument to the each helper, it must be in the form #each foo in bar", arguments[1] === "in"); - - 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); - - Ember.assert("Unable to find partial with name '"+name+"'.", template || deprecatedTemplate); - - 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; i - {{#with loggedInUser}} - Last Login: {{lastLogin}} - User Info: {{template "user_info"}} - {{/with}} - - ``` - - ```html - - ``` - - ```handlebars - {{#if isUser}} - {{template "user_info"}} - {{else}} - {{template "unlogged_user_info"}} - {{/if}} - ``` - - This helper looks for templates in the global `Ember.TEMPLATES` hash. If you - add `"; - return testEl.firstChild.innerHTML === ''; - })(); - - // IE 8 (and likely earlier) likes to move whitespace preceeding - // a script tag to appear after it. This means that we can - // accidentally remove whitespace when updating a morph. - var movesWhitespace = typeof document !== 'undefined' && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "Test: Value"; - return testEl.childNodes[0].nodeValue === 'Test:' && - testEl.childNodes[2].nodeValue === ' Value'; - })(); - - // Use this to find children by ID instead of using jQuery - var findChildById = function(element, id) { - if (element.getAttribute('id') === id) { return element; } - - var len = element.childNodes.length, idx, node, found; - for (idx=0; idx 0) { - var len = matches.length, idx; - for (idx=0; idxTest'); - canSet = el.options.length === 1; - } - - innerHTMLTags[tagName] = canSet; - - return canSet; - }; - - var setInnerHTML = function(element, html) { - var tagName = element.tagName; - - if (canSetInnerHTML(tagName)) { - setInnerHTMLWithoutFix(element, html); - } else { - // Firefox versions < 11 do not have support for element.outerHTML. - var outerHTML = element.outerHTML || new XMLSerializer().serializeToString(element); - - var startTag = outerHTML.match(new RegExp("<"+tagName+"([^>]*)>", 'i'))[0], - endTag = ''; - - var wrapper = document.createElement('div'); - setInnerHTMLWithoutFix(wrapper, startTag + html + endTag); - element = wrapper.firstChild; - while (element.tagName !== tagName) { - element = element.nextSibling; - } - } - - return element; - }; - function isSimpleClick(event) { - var modifier = event.shiftKey || event.metaKey || event.altKey || event.ctrlKey, - secondaryClick = event.which > 1; // IE9 may return undefined + var modifier = event.shiftKey || event.metaKey || event.altKey || event.ctrlKey; + var secondaryClick = event.which > 1; // IE9 may return undefined return !modifier && !secondaryClick; } - __exports__.setInnerHTML = setInnerHTML; - __exports__.isSimpleClick = isSimpleClick; + __exports__.isSimpleClick = isSimpleClick;/** + @private + @method getViewRange + @param {Ember.View} view + */ + function getViewRange(view) { + var range = document.createRange(); + range.setStartAfter(view._morph.start); + range.setEndBefore(view._morph.end); + return range; + } + + /** + `getViewClientRects` provides information about the position of the border + box edges of a view relative to the viewport. + + It is only intended to be used by development tools like the Ember Inpsector + and may not work on older browsers. + + @private + @method getViewClientRects + @param {Ember.View} view + */ + function getViewClientRects(view) { + var range = getViewRange(view); + return range.getClientRects(); + } + + __exports__.getViewClientRects = getViewClientRects;/** + `getViewBoundingClientRect` provides information about the position of the + bounding border box edges of a view relative to the viewport. + + It is only intended to be used by development tools like the Ember Inpsector + and may not work on older browsers. + + @private + @method getViewBoundingClientRect + @param {Ember.View} view + */ + function getViewBoundingClientRect(view) { + var range = getViewRange(view); + return range.getBoundingClientRect(); + } + + __exports__.getViewBoundingClientRect = getViewBoundingClientRect; }); -define("ember-views/views/collection_view", - ["ember-metal/core","ember-metal/platform","ember-metal/binding","ember-metal/merge","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/string","ember-views/views/container_view","ember-views/views/view","ember-metal/mixin","ember-runtime/mixins/array","exports"], +enifed("ember-views/views/collection_view", + ["ember-metal/core","ember-metal/binding","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/string","ember-views/views/container_view","ember-views/views/core_view","ember-views/views/view","ember-metal/mixin","ember-views/streams/read","ember-runtime/mixins/array","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { "use strict"; @@ -23394,17 +38328,16 @@ define("ember-views/views/collection_view", var Ember = __dependency1__["default"]; // Ember.assert - var create = __dependency2__.create; - var isGlobalPath = __dependency3__.isGlobalPath; - var merge = __dependency4__["default"]; - var get = __dependency5__.get; - var set = __dependency6__.set; - var fmt = __dependency7__.fmt; - var ContainerView = __dependency8__["default"]; - var CoreView = __dependency9__.CoreView; - var View = __dependency9__.View; - var observer = __dependency10__.observer; - var beforeObserver = __dependency10__.beforeObserver; + var isGlobalPath = __dependency2__.isGlobalPath; + var get = __dependency3__.get; + var set = __dependency4__.set; + var fmt = __dependency5__.fmt; + var ContainerView = __dependency6__["default"]; + var CoreView = __dependency7__["default"]; + var View = __dependency8__["default"]; + var observer = __dependency9__.observer; + var beforeObserver = __dependency9__.beforeObserver; + var readViewFactory = __dependency10__.readViewFactory; var EmberArray = __dependency11__["default"]; /** @@ -23428,27 +38361,32 @@ define("ember-views/views/collection_view", The view for each item in the collection will have its `content` property set to the item. - ## Specifying itemViewClass + ## Specifying `itemViewClass` By default the view class for each item in the managed collection will be an instance of `Ember.View`. You can supply a different class by setting the `CollectionView`'s `itemViewClass` property. - Given an empty `` and the following code: + Given the following application code: ```javascript - someItemsView = Ember.CollectionView.create({ + var App = Ember.Application.create(); + App.ItemListView = Ember.CollectionView.extend({ classNames: ['a-collection'], content: ['A','B','C'], itemViewClass: Ember.View.extend({ template: Ember.Handlebars.compile("the letter: {{view.content}}") }) }); - - someItemsView.appendTo('body'); ``` - Will result in the following HTML structure + And a simple application template: + + ```handlebars + {{view 'item-list'}} + ``` + + The following HTML will result: ```html
    @@ -23464,21 +38402,26 @@ define("ember-views/views/collection_view", "ul", "ol", "table", "thead", "tbody", "tfoot", "tr", or "select" will result in the item views receiving an appropriately matched `tagName` property. - Given an empty `` and the following code: + Given the following application code: ```javascript - anUnorderedListView = Ember.CollectionView.create({ + var App = Ember.Application.create(); + App.UnorderedListView = Ember.CollectionView.create({ tagName: 'ul', content: ['A','B','C'], itemViewClass: Ember.View.extend({ template: Ember.Handlebars.compile("the letter: {{view.content}}") }) }); - - anUnorderedListView.appendTo('body'); ``` - Will result in the following HTML structure + And a simple application template: + + ```handlebars + {{view 'unordered-list-view'}} + ``` + + The following HTML will result: ```html
      @@ -23489,7 +38432,7 @@ define("ember-views/views/collection_view", ``` Additional `tagName` pairs can be provided by adding to - `Ember.CollectionView.CONTAINER_MAP ` + `Ember.CollectionView.CONTAINER_MAP`. For example: ```javascript Ember.CollectionView.CONTAINER_MAP['article'] = 'section' @@ -23502,7 +38445,7 @@ define("ember-views/views/collection_view", `createChildView` method can be overidden: ```javascript - CustomCollectionView = Ember.CollectionView.extend({ + App.CustomCollectionView = Ember.CollectionView.extend({ createChildView: function(viewClass, attrs) { if (attrs.content.kind == 'album') { viewClass = App.AlbumView; @@ -23522,18 +38465,23 @@ define("ember-views/views/collection_view", will be the `CollectionView`s only child. ```javascript - aListWithNothing = Ember.CollectionView.create({ - classNames: ['nothing'] + var App = Ember.Application.create(); + App.ListWithNothing = Ember.CollectionView.create({ + classNames: ['nothing'], content: null, emptyView: Ember.View.extend({ template: Ember.Handlebars.compile("The collection is empty") }) }); - - aListWithNothing.appendTo('body'); ``` - Will result in the following HTML structure + And a simple application template: + + ```handlebars + {{view 'list-with-nothing'}} + ``` + + The following HTML will result: ```html
      @@ -23690,18 +38638,8 @@ define("ember-views/views/collection_view", // Loop through child views that correspond with the removed items. // Note that we loop from the end of the array to the beginning because // we are mutating it as we go. - var childViews = this._childViews, childView, idx, len; - - len = this._childViews.length; - - var removingAll = removedCount === len; - - if (removingAll) { - this.currentState.empty(this); - this.invokeRecursively(function(view) { - view.removedFromDOM = true; - }, false); - } + var childViews = this._childViews; + var childView, idx; for (idx = start + removedCount - 1; idx >= start; idx--) { childView = childViews[idx]; @@ -23724,26 +38662,24 @@ define("ember-views/views/collection_view", @param {Number} added number of object added to content */ arrayDidChange: function(content, start, removed, added) { - var addedViews = [], view, item, idx, len, itemViewClass, - emptyView; + var addedViews = []; + var view, item, idx, len, itemViewClass, emptyView, itemViewProps; len = content ? get(content, 'length') : 0; if (len) { + itemViewProps = this._itemViewProps || {}; itemViewClass = get(this, 'itemViewClass'); - if ('string' === typeof itemViewClass && isGlobalPath(itemViewClass)) { - itemViewClass = get(itemViewClass) || itemViewClass; - } + itemViewClass = readViewFactory(itemViewClass, this.container); - for (idx = start; idx < start+added; idx++) { item = content.objectAt(idx); - view = this.createChildView(itemViewClass, { - content: item, - contentIndex: idx - }); + itemViewProps.content = item; + itemViewProps.contentIndex = idx; + + view = this.createChildView(itemViewClass, itemViewProps); addedViews.push(view); } @@ -23757,6 +38693,7 @@ define("ember-views/views/collection_view", } emptyView = this.createChildView(emptyView); + addedViews.push(emptyView); set(this, 'emptyView', emptyView); @@ -23820,7 +38757,7 @@ define("ember-views/views/collection_view", __exports__["default"] = CollectionView; }); -define("ember-views/views/component", +enifed("ember-views/views/component", ["ember-metal/core","ember-views/mixins/component_template_deprecation","ember-runtime/mixins/target_action_support","ember-views/views/view","ember-metal/property_get","ember-metal/property_set","ember-metal/is_none","ember-metal/computed","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { "use strict"; @@ -23829,9 +38766,11 @@ define("ember-views/views/component", var ComponentTemplateDeprecation = __dependency2__["default"]; var TargetActionSupport = __dependency3__["default"]; - var View = __dependency4__.View;var get = __dependency5__.get; + var View = __dependency4__["default"]; + + var get = __dependency5__.get; var set = __dependency6__.set; - var isNone = __dependency7__.isNone; + var isNone = __dependency7__["default"]; var computed = __dependency8__.computed; @@ -23967,8 +38906,8 @@ define("ember-views/views/component", template: computed(function(key, value) { if (value !== undefined) { return value; } - var templateName = get(this, 'templateName'), - template = this.templateForName(templateName, 'template'); + var templateName = get(this, 'templateName'); + var template = this.templateForName(templateName, 'template'); return template || get(this, 'defaultTemplate'); @@ -23983,18 +38922,14 @@ define("ember-views/views/component", */ templateName: null, - // during render, isolate keywords - cloneKeywords: function() { - return { - view: this, - controller: this - }; + _setupKeywords: function() { + this._keywords.view.setSource(this); }, _yield: function(context, options) { - var view = options.data.view, - parentView = this._parentView, - template = get(this, 'template'); + var view = options.data.view; + var parentView = this._parentView; + var template = get(this, 'template'); if (template) { @@ -24005,7 +38940,7 @@ define("ember-views/views/component", template: template, context: get(parentView, 'context'), controller: get(parentView, 'controller'), - templateData: { keywords: parentView.cloneKeywords() } + templateData: { keywords: {} } }); } }, @@ -24104,8 +39039,8 @@ define("ember-views/views/component", @param [context] {*} a context to send with the action */ sendAction: function(action) { - var actionName, - contexts = a_slice.call(arguments, 1); + var actionName; + var contexts = a_slice.call(arguments, 1); // Send the default action if (action === undefined) { @@ -24121,14 +39056,36 @@ define("ember-views/views/component", action: actionName, actionContext: contexts }); + }, + + send: function(actionName) { + var args = [].slice.call(arguments, 1); + var target; + var hasAction = this._actions && this._actions[actionName]; + + if (hasAction) { + if (this._actions[actionName].apply(this, args) === true) { + // handler returned true, so this action will bubble + } else { + return; + } + } + + if (target = get(this, 'target')) { + target.send.apply(target, arguments); + } else { + if (!hasAction) { + throw new Error(Ember.inspect(this) + ' had no action handler for: ' + actionName); + } + } } }); __exports__["default"] = Component; }); -define("ember-views/views/container_view", - ["ember-metal/core","ember-metal/merge","ember-runtime/mixins/mutable_array","ember-metal/property_get","ember-metal/property_set","ember-views/views/view","ember-views/views/states","ember-metal/error","ember-metal/enumerable_utils","ember-metal/computed","ember-metal/run_loop","ember-metal/properties","ember-views/system/render_buffer","ember-metal/mixin","ember-runtime/system/native_array","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __exports__) { +enifed("ember-views/views/container_view", + ["ember-metal/core","ember-metal/merge","ember-runtime/mixins/mutable_array","ember-metal/property_get","ember-metal/property_set","ember-views/views/view","ember-views/views/states","ember-metal/error","ember-metal/enumerable_utils","ember-metal/computed","ember-metal/run_loop","ember-metal/properties","ember-metal/mixin","ember-runtime/system/native_array","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __exports__) { "use strict"; var Ember = __dependency1__["default"]; // Ember.assert, Ember.K @@ -24138,24 +39095,21 @@ define("ember-views/views/container_view", var get = __dependency4__.get; var set = __dependency5__.set; - var View = __dependency6__.View; - var ViewCollection = __dependency6__.ViewCollection; + var View = __dependency6__["default"]; + var cloneStates = __dependency7__.cloneStates; var EmberViewStates = __dependency7__.states; var EmberError = __dependency8__["default"]; - // ES6TODO: functions on EnumerableUtils should get their own export - var EnumerableUtils = __dependency9__["default"]; - var forEach = EnumerableUtils.forEach; + var forEach = __dependency9__.forEach; var computed = __dependency10__.computed; var run = __dependency11__["default"]; var defineProperty = __dependency12__.defineProperty; - var RenderBuffer = __dependency13__["default"]; - var observer = __dependency14__.observer; - var beforeObserver = __dependency14__.beforeObserver; - var A = __dependency15__.A; + var observer = __dependency13__.observer; + var beforeObserver = __dependency13__.beforeObserver; + var emberA = __dependency14__.A; /** @module ember @@ -24315,13 +39269,16 @@ define("ember-views/views/container_view", @extends Ember.View */ var ContainerView = View.extend(MutableArray, { - states: states, + _states: states, + + willWatchProperty: function(prop){ + }, init: function() { this._super(); var childViews = get(this, 'childViews'); - + // redefine view's childViews property that was obliterated defineProperty(this, 'childViews', View.childViewsProperty); @@ -24375,7 +39332,7 @@ define("ember-views/views/container_view", length: computed(function () { return this._childViews.length; - }).volatile(), + })["volatile"](), /** Instructs each child view to render to the passed render buffer. @@ -24385,9 +39342,18 @@ define("ember-views/views/container_view", @param {Ember.RenderBuffer} buffer the buffer to render to */ render: function(buffer) { - this.forEachChildView(function(view) { - view.renderToBuffer(buffer); - }); + var element = buffer.element(); + var dom = buffer.dom; + + if (this.tagName === '') { + element = dom.createDocumentFragment(); + buffer._element = element; + this._childViewsMorph = dom.appendMorph(element, this._morph.contextualElement); + } else { + this._childViewsMorph = dom.createMorph(element, element.lastChild, null); + } + + return element; }, instrumentName: 'container', @@ -24496,7 +39462,9 @@ define("ember-views/views/container_view", merge(states.hasElement, { childViewsWillChange: function(view, views, start, removed) { for (var i=start; i ``` - - One property can be mapped on to another by placing a ":" between + + One property can be mapped on to another by placing a ":" between the source property and the destination property: - + ```javascript AnchorView = Ember.View.extend({ tagName: 'a', @@ -25545,13 +40246,13 @@ define("ember-views/views/view", url: 'http://google.com' }); ``` - + Will result in view instances with an HTML representation of: ```html ``` - + If the return value of an `attributeBindings` monitored property is a boolean the property will follow HTML's pattern of repeating the attribute's name as its value: @@ -25704,7 +40405,7 @@ define("ember-views/views/view", }); aView = AView.create({ - controller: aController, + controller: aController }); ``` @@ -25738,8 +40439,8 @@ define("ember-views/views/view", ```javascript AViewWithLayout = Ember.View.extend({ - layout: Ember.Handlebars.compile("
      {{yield}}
      ") - template: Ember.Handlebars.compile("I got wrapped"), + layout: Ember.Handlebars.compile("
      {{yield}}
      "), + template: Ember.Handlebars.compile("I got wrapped") }); ``` @@ -25825,8 +40526,9 @@ define("ember-views/views/view", on the descendent. ```javascript - OuterView = Ember.View.extend({ - template: Ember.Handlebars.compile("outer {{#view InnerView}}inner{{/view}} outer"), + var App = Ember.Application.create(); + App.OuterView = Ember.View.extend({ + template: Ember.Handlebars.compile("outer {{#view 'inner'}}inner{{/view}} outer"), eventManager: Ember.Object.create({ mouseEnter: function(event, view) { // view might be instance of either @@ -25836,7 +40538,7 @@ define("ember-views/views/view", }) }); - InnerView = Ember.View.extend({ + App.InnerView = Ember.View.extend({ click: function(event) { // will be called if rendered inside // an OuterView because OuterView's @@ -25980,8 +40682,8 @@ define("ember-views/views/view", template: computed('templateName', function(key, value) { if (value !== undefined) { return value; } - var templateName = get(this, 'templateName'), - template = this.templateForName(templateName, 'template'); + var templateName = get(this, 'templateName'); + var template = this.templateForName(templateName, 'template'); return template || get(this, 'defaultTemplate'); @@ -26014,8 +40716,8 @@ define("ember-views/views/view", @type Function */ layout: computed(function(key) { - var layoutName = get(this, 'layoutName'), - layout = this.templateForName(layoutName, 'layout'); + var layoutName = get(this, 'layoutName'); + var layout = this.templateForName(layoutName, 'layout'); return layout || get(this, 'defaultLayout'); @@ -26029,9 +40731,13 @@ define("ember-views/views/view", templateForName: function(name, type) { if (!name) { return; } - // the defaultContainer is deprecated - var container = this.container || (Container && Container.defaultContainer); - return container && container.lookup('template:' + name); + if (!this.container) { + throw new EmberError('Container was not found when looking up a views template. ' + + 'This is most likely due to manually instantiating an Ember.View. ' + + 'See: http://git.io/EKPpnA'); + } + + return this.container.lookup('template:' + name); }, /** @@ -26053,7 +40759,7 @@ define("ember-views/views/view", } else { return get(this, '_context'); } - }).volatile(), + })["volatile"](), /** Private copy of the view's template context. This can be set directly @@ -26167,8 +40873,8 @@ define("ember-views/views/view", @return Ember.View */ nearestOfType: function(klass) { - var view = get(this, 'parentView'), - isOfType = klass instanceof Mixin ? + var view = get(this, 'parentView'); + var isOfType = klass instanceof Mixin ? function(view) { return klass.detect(view); } : function(view) { return klass.detect(view.constructor); }; @@ -26220,6 +40926,7 @@ define("ember-views/views/view", _parentViewDidChange: observer('_parentView', function() { if (this.isDestroying) { return; } + this._setupKeywords(); this.trigger('parentViewDidChange'); if (get(this, 'parentView.controller') && !get(this, 'controller')) { @@ -26237,15 +40944,22 @@ define("ember-views/views/view", }); }), - cloneKeywords: function() { - var templateData = get(this, 'templateData'); + _setupKeywords: function() { + var keywords = this._keywords; + var contextView = this._contextView || this._parentView; - var keywords = templateData ? copy(templateData.keywords) : {}; - set(keywords, 'view', get(this, 'concreteView')); - set(keywords, '_view', this); - set(keywords, 'controller', get(this, 'controller')); + if (contextView) { + var parentKeywords = contextView._keywords; - return keywords; + keywords.view.setSource(this.isVirtual ? parentKeywords.view : this); + + for (var name in parentKeywords) { + if (keywords[name]) continue; + keywords[name] = parentKeywords[name]; + } + } else { + keywords.view.setSource(this.isVirtual ? null : this); + } }, /** @@ -26268,15 +40982,12 @@ define("ember-views/views/view", if (template) { var context = get(this, 'context'); - var keywords = this.cloneKeywords(); var output; var data = { view: this, buffer: buffer, - isRenderData: true, - keywords: keywords, - insideGroup: get(this, 'templateData.insideGroup') + isRenderData: true }; // Invoke the template with the provided template context, which @@ -26313,21 +41024,6 @@ define("ember-views/views/view", return this.currentState.rerender(this); }, - clearRenderedChildren: function() { - var lengthBefore = this.lengthBeforeRender, - lengthAfter = this.lengthAfterRender; - - // If there were child views created during the last call to render(), - // remove them under the assumption that they will be re-created when - // we re-render. - - // VIEW-TODO: Unit test this path. - var childViews = this._childViews; - for (var i=lengthAfter-1; i>=lengthBefore; i--) { - if (childViews[i]) { childViews[i].destroy(); } - } - }, - /** Iterates over the view's `classNameBindings` array, inserts the value of the specified property into the `classNames` array, then creates an @@ -26338,27 +41034,37 @@ define("ember-views/views/view", @private */ _applyClassNameBindings: function(classBindings) { - var classNames = this.classNames, - elem, newClass, dasherizedClass; + var classNames = this.classNames; + var elem, newClass, dasherizedClass; // Loop through all of the configured bindings. These will be either // property names ('isUrgent') or property paths relative to the view // ('content.isUrgent') - a_forEach(classBindings, function(binding) { + forEach(classBindings, function(binding) { + + var parsedPath; + + if (typeof binding === 'string') { + parsedPath = View._parsePropertyPath(binding); + if (parsedPath.path === '') { + parsedPath.stream = new SimpleStream(true); + } else { + parsedPath.stream = this.getStream('_view.' + parsedPath.path); + } + } else { + parsedPath = binding; + } - // Variable in which the old class value is saved. The observer function // closes over this variable, so it knows which string to remove when // the property changes. var oldClass; - // Extract just the property name from bindings like 'foo:bar' - var parsedPath = View._parsePropertyPath(binding); // Set up an observer on the context. If the property changes, toggle the // class name. - var observer = function() { + var observer = this._wrapAsScheduled(function() { // Get the current value of the property - newClass = this._classStringForProperty(binding); + newClass = this._classStringForProperty(parsedPath); elem = this.$(); // If we had previously added a class to the element, remove it. @@ -26377,15 +41083,15 @@ define("ember-views/views/view", } else { oldClass = null; } - }; + }); // Get the class name for the property at its current value - dasherizedClass = this._classStringForProperty(binding); + dasherizedClass = this._classStringForProperty(parsedPath); if (dasherizedClass) { // Ensure that it gets into the classNames array // so it is displayed when we render. - a_addObject(classNames, dasherizedClass); + addObject(classNames, dasherizedClass); // Save a reference to the class name so we can remove it // if the observer fires. Remember that this variable has @@ -26393,7 +41099,7 @@ define("ember-views/views/view", oldClass = dasherizedClass; } - this.registerObserver(this, parsedPath.path, observer); + parsedPath.stream.subscribe(observer, this); // Remove className so when the view is rerendered, // the className is added based on binding reevaluation this.one('willClearRender', function() { @@ -26417,14 +41123,15 @@ define("ember-views/views/view", @private */ _applyAttributeBindings: function(buffer, attributeBindings) { - var attributeValue, - unspecifiedAttributeBindings = this._unspecifiedAttributeBindings = this._unspecifiedAttributeBindings || {}; + var attributeValue; + var unspecifiedAttributeBindings = this._unspecifiedAttributeBindings = this._unspecifiedAttributeBindings || {}; - a_forEach(attributeBindings, function(binding) { - var split = binding.split(':'), - property = split[0], - attributeName = split[1] || property; + forEach(attributeBindings, function(binding) { + var split = binding.split(':'); + var property = split[0]; + var attributeName = split[1] || property; + if (property in this) { this._setupAttributeBindingObservation(property, attributeName); @@ -26491,16 +41198,8 @@ define("ember-views/views/view", @param property @private */ - _classStringForProperty: function(property) { - var parsedPath = View._parsePropertyPath(property); - var path = parsedPath.path; - - var val = get(this, path); - if (val === undefined && isGlobalPath(path)) { - val = get(Ember.lookup, path); - } - - return View._classStringForValue(path, val, parsedPath.className, parsedPath.falsyClassName); + _classStringForProperty: function(parsedPath) { + return View._classStringForValue(parsedPath.path, parsedPath.stream.value(), parsedPath.className, parsedPath.falsyClassName); }, // .......................................................... @@ -26513,13 +41212,7 @@ define("ember-views/views/view", @property element @type DOMElement */ - element: computed('_parentView', function(key, value) { - if (value !== undefined) { - return this.currentState.setElement(this, value); - } else { - return this.currentState.getElement(this); - } - }), + element: null, /** Returns a jQuery object for this view's element. If you pass in a selector @@ -26538,9 +41231,9 @@ define("ember-views/views/view", }, mutateChildViews: function(callback) { - var childViews = this._childViews, - idx = childViews.length, - view; + var childViews = this._childViews; + var idx = childViews.length; + var view; while(--idx >= 0) { view = childViews[idx]; @@ -26555,8 +41248,8 @@ define("ember-views/views/view", if (!childViews) { return this; } - var len = childViews.length, - view, idx; + var len = childViews.length; + var view, idx; for (idx = 0; idx < len; idx++) { view = childViews[idx]; @@ -26586,12 +41279,11 @@ define("ember-views/views/view", @param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object @return {Ember.View} receiver */ - appendTo: function(target) { - // Schedule the DOM element to be created and appended to the given - // element after bindings have synchronized. - this._insertElementLater(function() { - this.$().appendTo(target); - }); + appendTo: function(selector) { + var target = jQuery(selector); + + + this.constructor.renderer.appendTo(this, target[0]); return this; }, @@ -26609,47 +41301,15 @@ define("ember-views/views/view", @param {String|DOMElement|jQuery} target A selector, element, HTML string, or jQuery object @return {Ember.View} received */ - replaceIn: function(target) { + replaceIn: function(selector) { + var target = jQuery(selector); + - this._insertElementLater(function() { - jQuery(target).empty(); - this.$().appendTo(target); - }); + this.constructor.renderer.replaceIn(this, target[0]); return this; }, - /** - Schedules a DOM operation to occur during the next render phase. This - ensures that all bindings have finished synchronizing before the view is - rendered. - - To use, pass a function that performs a DOM operation. - - Before your function is called, this view and all child views will receive - the `willInsertElement` event. After your function is invoked, this view - and all of its child views will receive the `didInsertElement` event. - - ```javascript - view._insertElementLater(function() { - this.createElement(); - this.$().appendTo('body'); - }); - ``` - - @method _insertElementLater - @param {Function} fn the function that inserts the element into the DOM - @private - */ - _insertElementLater: function(fn) { - this._scheduledInsert = run.scheduleOnce('render', this, '_insertElement', fn); - }, - - _insertElement: function (fn) { - this._scheduledInsert = null; - this.currentState.insertElement(this, fn); - }, - /** Appends the view's element to the document body. If the view does not have an HTML representation yet, `createElement()` will be called @@ -26683,11 +41343,24 @@ define("ember-views/views/view", // In the interim, we will just re-render if that happens. It is more // important than elements get garbage collected. if (!this.removedFromDOM) { this.destroyElement(); } - this.invokeRecursively(function(view) { - if (view.clearRenderedChildren) { view.clearRenderedChildren(); } - }); }, + /** + The HTML `id` of the view's element in the DOM. You can provide this + value yourself but it must be unique (just as in HTML): + + ```handlebars + {{my-component elementId="a-really-cool-id"}} + ``` + + If not manually set a default value will be provided by the framework. + + Once rendered an element's `elementId` is considered immutable and you + should never change it. + + @property elementId + @type String + */ elementId: null, /** @@ -26707,20 +41380,20 @@ define("ember-views/views/view", }, /** - Creates a DOM representation of the view and all of its - child views by recursively calling the `render()` method. + Creates a DOM representation of the view and all of its child views by + recursively calling the `render()` method. - After the element has been created, `didInsertElement` will + After the element has been inserted into the DOM, `didInsertElement` will be called on this view and all of its child views. @method createElement @return {Ember.View} receiver */ createElement: function() { - if (get(this, 'element')) { return this; } + if (this.element) { return this; } - var buffer = this.renderToBuffer(); - set(this, 'element', buffer.element()); + this._didCreateElementWithoutMorph = true; + this.constructor.renderer.renderTree(this); return this; }, @@ -26737,6 +41410,9 @@ define("ember-views/views/view", or after the view was re-rendered. Override this function to do any set up that requires an element in the document body. + When a view has children, didInsertElement will be called on the + child view(s) first, bubbling upwards through the hierarchy. + @event didInsertElement */ didInsertElement: Ember.K, @@ -26750,65 +41426,6 @@ define("ember-views/views/view", */ willClearRender: Ember.K, - /** - Run this callback on the current view (unless includeSelf is false) and recursively on child views. - - @method invokeRecursively - @param fn {Function} - @param includeSelf {Boolean} Includes itself if true. - @private - */ - invokeRecursively: function(fn, includeSelf) { - var childViews = (includeSelf === false) ? this._childViews : [this]; - var currentViews, view, currentChildViews; - - while (childViews.length) { - currentViews = childViews.slice(); - childViews = []; - - for (var i=0, l=currentViews.length; i=0; i--) { - childViews[i].removedFromDOM = true; - } - // remove from non-virtual parent view if viewName was specified if (viewName && nonVirtualParentView) { nonVirtualParentView.set(viewName, null); } - childLen = childViews.length; - for (i=childLen-1; i>=0; i--) { - childViews[i].destroy(); - } - return this; }, @@ -27251,16 +41825,18 @@ define("ember-views/views/view", _toggleVisibility: function() { var $el = this.$(); - if (!$el) { return; } - var isVisible = get(this, 'isVisible'); if (this._isVisible === isVisible) { return ; } - $el.toggle(isVisible); - + // It's important to keep these in sync, even if we don't yet have + // an element in the DOM to manipulate: this._isVisible = isVisible; + if (!$el) { return; } + + $el.toggle(isVisible); + if (this._isAncestorHidden()) { return; } if (isVisible) { @@ -27304,25 +41880,16 @@ define("ember-views/views/view", return false; }, - - clearBuffer: function() { - this.invokeRecursively(nullViewsBuffer); - }, - transitionTo: function(state, children) { - var priorState = this.currentState, - currentState = this.currentState = this.states[state]; - this.state = state; + this._transitionTo(state, children); + }, + _transitionTo: function(state, children) { + var priorState = this.currentState; + var currentState = this.currentState = this._states[state]; + this._state = state; if (priorState && priorState.exit) { priorState.exit(this); } if (currentState.enter) { currentState.enter(this); } - if (state === 'inDOM') { meta(this).cache.element = undefined; } - - if (children !== false) { - this.forEachChildView(function(view) { - view.transitionTo(state); - }); - } }, // ....................................................... @@ -27351,23 +41918,81 @@ define("ember-views/views/view", return; } - var view = this, - stateCheckedObserver = function() { - view.currentState.invokeObserver(this, observer); - }, - scheduledObserver = function() { - run.scheduleOnce('render', this, stateCheckedObserver); - }; + var scheduledObserver = this._wrapAsScheduled(observer); addObserver(root, path, target, scheduledObserver); this.one('willClearRender', function() { removeObserver(root, path, target, scheduledObserver); }); - } + }, + _wrapAsScheduled: function(fn) { + var view = this; + var stateCheckedFn = function() { + view.currentState.invokeObserver(this, fn); + }; + var scheduledFn = function() { + run.scheduleOnce('render', this, stateCheckedFn); + }; + return scheduledFn; + }, + + getStream: function(path) { + return this._getContextStream().get(path); + }, + + _getBindingForStream: function(path) { + if (this._streamBindings === undefined) { + this._streamBindings = create(null); + this.one('willDestroyElement', this, this._destroyStreamBindings); + } + + if (this._streamBindings[path] !== undefined) { + return this._streamBindings[path]; + } else { + var stream = this._getContextStream().get(path); + return this._streamBindings[path] = new StreamBinding(stream); + } + }, + + _destroyStreamBindings: function() { + var streamBindings = this._streamBindings; + for (var path in streamBindings) { + streamBindings[path].destroy(); + } + this._streamBindings = undefined; + }, + + _getContextStream: function() { + if (this._contextStream === undefined) { + this._baseContext = new KeyStream(this, 'context'); + this._contextStream = new ContextStream(this); + this.one('willDestroyElement', this, this._destroyContextStream); + } + + return this._contextStream; + }, + + _destroyContextStream: function() { + this._baseContext.destroy(); + this._baseContext = undefined; + this._contextStream.destroy(); + this._contextStream = undefined; + }, + + _unsubscribeFromStreamBindings: function() { + for (var key in this._streamBindingSubscriptions) { + var streamBinding = this[key + 'Binding']; + var callback = this._streamBindingSubscriptions[key]; + streamBinding.unsubscribe(callback); + } + } }); + deprecateProperty(View.prototype, 'state', '_state'); + deprecateProperty(View.prototype, 'states', '_states'); + /* Describe how the specified actions should behave in the various states that a view can exist in. Possible states: @@ -27395,52 +42020,6 @@ define("ember-views/views/view", // once the view has been inserted into the DOM, legal manipulations // are done on the DOM element. - function notifyMutationListeners() { - run.once(View, 'notifyMutationListeners'); - } - - var DOMManager = { - prepend: function(view, html) { - view.$().prepend(html); - notifyMutationListeners(); - }, - - after: function(view, html) { - view.$().after(html); - notifyMutationListeners(); - }, - - html: function(view, html) { - view.$().html(html); - notifyMutationListeners(); - }, - - replace: function(view) { - var element = get(view, 'element'); - - set(view, 'element', null); - - view._insertElementLater(function() { - jQuery(element).replaceWith(get(view, 'element')); - notifyMutationListeners(); - }); - }, - - remove: function(view) { - view.$().remove(); - notifyMutationListeners(); - }, - - empty: function(view) { - view.$().empty(); - notifyMutationListeners(); - } - }; - - View.reopen({ - domManager: DOMManager - }); - View.reopenClass({ /** @@ -27463,11 +42042,10 @@ define("ember-views/views/view", @private */ _parsePropertyPath: function(path) { - var split = path.split(':'), - propertyPath = split[0], - classNames = "", - className, - falsyClassName; + var split = path.split(':'); + var propertyPath = split[0]; + var classNames = ""; + var className, falsyClassName; // check if the property is defined as prop:class or prop:trueClass:falseClass if (split.length > 1) { @@ -27479,6 +42057,7 @@ define("ember-views/views/view", } return { + stream: undefined, path: propertyPath, classNames: classNames, className: (className === '') ? undefined : className, @@ -27512,9 +42091,9 @@ define("ember-views/views/view", */ _classStringForValue: function(path, val, className, falsyClassName) { if(isArray(val)) { - val = get(val, 'length') !== 0; + val = get(val, 'length') !== 0; } - + // When using the colon syntax, evaluate the truthiness or falsiness // of the value to determine which className to return if (className || falsyClassName) { @@ -27551,7 +42130,7 @@ define("ember-views/views/view", }); var mutation = EmberObject.extend(Evented).create(); - + // TODO MOVE TO RENDERER HOOKS View.addMutationListener = function(callback) { mutation.on('change', callback); }; @@ -27580,7 +42159,8 @@ define("ember-views/views/view", // method. View.childViewsProperty = childViewsProperty; - View.applyAttributeBindings = function(elem, name, value) { + View.applyAttributeBindings = function(elem, name, initialValue) { + var value = sanitizeAttributeValue(elem[0], name, initialValue); var type = typeOf(value); // if this changes, also change the logic in ember-handlebars/lib/helpers/binding.js @@ -27592,7 +42172,12 @@ define("ember-views/views/view", if (isNone(value) || value === false) { // `null`, `undefined` or `false` should remove attribute elem.removeAttr(name); - elem.prop(name, ''); + // In IE8 `prop` couldn't remove attribute when name is `required`. + if (name === 'required') { + elem.removeProp(name); + } else { + elem.prop(name, ''); + } } else if (value !== elem.prop(name)) { // value should always be properties elem.prop(name, value); @@ -27602,11695 +42187,857 @@ define("ember-views/views/view", } }; - __exports__.CoreView = CoreView; - __exports__.View = View; - __exports__.ViewCollection = ViewCollection; + __exports__["default"] = View; }); -})(); - -(function() { -define("metamorph", - [], - function() { +enifed("ember", + ["ember-metal","ember-runtime","ember-handlebars","ember-views","ember-routing","ember-routing-handlebars","ember-application","ember-extension-support"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { "use strict"; - // ========================================================================== - // Project: metamorph - // Copyright: ©2014 Tilde, Inc. All rights reserved. - // ========================================================================== - - var K = function() {}, - guid = 0, - disableRange = (function(){ - if ('undefined' !== typeof MetamorphENV) { - return MetamorphENV.DISABLE_RANGE_API; - } else if ('undefined' !== ENV) { - return ENV.DISABLE_RANGE_API; - } else { - return false; - } - })(), - - // Feature-detect the W3C range API, the extended check is for IE9 which only partially supports ranges - supportsRange = (!disableRange) && typeof document !== 'undefined' && ('createRange' in document) && (typeof Range !== 'undefined') && Range.prototype.createContextualFragment, - - // Internet Explorer prior to 9 does not allow setting innerHTML if the first element - // is a "zero-scope" element. This problem can be worked around by making - // the first node an invisible text node. We, like Modernizr, use ­ - needsShy = typeof document !== 'undefined' && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "
      "; - testEl.firstChild.innerHTML = ""; - return testEl.firstChild.innerHTML === ''; - })(), - - - // IE 8 (and likely earlier) likes to move whitespace preceeding - // a script tag to appear after it. This means that we can - // accidentally remove whitespace when updating a morph. - movesWhitespace = document && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "Test: Value"; - return testEl.childNodes[0].nodeValue === 'Test:' && - testEl.childNodes[2].nodeValue === ' Value'; - })(); - - // Constructor that supports either Metamorph('foo') or new - // Metamorph('foo'); - // - // Takes a string of HTML as the argument. - - var Metamorph = function(html) { - var self; - - if (this instanceof Metamorph) { - self = this; - } else { - self = new K(); - } - - self.innerHTML = html; - var myGuid = 'metamorph-'+(guid++); - self.start = myGuid + '-start'; - self.end = myGuid + '-end'; - - return self; - }; - - K.prototype = Metamorph.prototype; - - var rangeFor, htmlFunc, removeFunc, outerHTMLFunc, appendToFunc, afterFunc, prependFunc, startTagFunc, endTagFunc; - - outerHTMLFunc = function() { - return this.startTag() + this.innerHTML + this.endTag(); - }; - - startTagFunc = function() { - /* - * We replace chevron by its hex code in order to prevent escaping problems. - * Check this thread for more explaination: - * http://stackoverflow.com/questions/8231048/why-use-x3c-instead-of-when-generating-html-from-javascript - */ - return "hi"; - * div.firstChild.firstChild.tagName //=> "" - * - * If our script markers are inside such a node, we need to find that - * node and use *it* as the marker. - */ - var realNode = function(start) { - while (start.parentNode.tagName === "") { - start = start.parentNode; - } - - return start; - }; - - /* - * When automatically adding a tbody, Internet Explorer inserts the - * tbody immediately before the first . Other browsers create it - * before the first node, no matter what. - * - * This means the the following code: - * - * div = document.createElement("div"); - * div.innerHTML = "
      hi
      - * - * Generates the following DOM in IE: - * - * + div - * + table - * - script id='first' - * + tbody - * + tr - * + td - * - "hi" - * - script id='last' - * - * Which means that the two script tags, even though they were - * inserted at the same point in the hierarchy in the original - * HTML, now have different parents. - * - * This code reparents the first script tag by making it the tbody's - * first child. - * - */ - var fixParentage = function(start, end) { - if (start.parentNode !== end.parentNode) { - end.parentNode.insertBefore(start, end.parentNode.firstChild); - } - }; - - htmlFunc = function(html, outerToo) { - // get the real starting node. see realNode for details. - var start = realNode(document.getElementById(this.start)); - var end = document.getElementById(this.end); - var parentNode = end.parentNode; - var node, nextSibling, last; - - // make sure that the start and end nodes share the same - // parent. If not, fix it. - fixParentage(start, end); - - // remove all of the nodes after the starting placeholder and - // before the ending placeholder. - node = start.nextSibling; - while (node) { - nextSibling = node.nextSibling; - last = node === end; - - // if this is the last node, and we want to remove it as well, - // set the `end` node to the next sibling. This is because - // for the rest of the function, we insert the new nodes - // before the end (note that insertBefore(node, null) is - // the same as appendChild(node)). - // - // if we do not want to remove it, just break. - if (last) { - if (outerToo) { end = node.nextSibling; } else { break; } - } - - node.parentNode.removeChild(node); - - // if this is the last node and we didn't break before - // (because we wanted to remove the outer nodes), break - // now. - if (last) { break; } - - node = nextSibling; - } - - // get the first node for the HTML string, even in cases like - // tables and lists where a simple innerHTML on a div would - // swallow some of the content. - node = firstNodeFor(start.parentNode, html); - - if (outerToo) { - start.parentNode.removeChild(start); - } - - // copy the nodes for the HTML between the starting and ending - // placeholder. - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, end); - node = nextSibling; - } - }; - - // remove the nodes in the DOM representing this metamorph. - // - // this includes the starting and ending placeholders. - removeFunc = function() { - var start = realNode(document.getElementById(this.start)); - var end = document.getElementById(this.end); - - this.html(''); - start.parentNode.removeChild(start); - end.parentNode.removeChild(end); - }; - - appendToFunc = function(parentNode) { - var node = firstNodeFor(parentNode, this.outerHTML()); - var nextSibling; - - while (node) { - nextSibling = node.nextSibling; - parentNode.appendChild(node); - node = nextSibling; - } - }; - - afterFunc = function(html) { - // get the real starting node. see realNode for details. - var end = document.getElementById(this.end); - var insertBefore = end.nextSibling; - var parentNode = end.parentNode; - var nextSibling; - var node; - - // get the first node for the HTML string, even in cases like - // tables and lists where a simple innerHTML on a div would - // swallow some of the content. - node = firstNodeFor(parentNode, html); - - // copy the nodes for the HTML between the starting and ending - // placeholder. - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, insertBefore); - node = nextSibling; - } - }; - - prependFunc = function(html) { - var start = document.getElementById(this.start); - var parentNode = start.parentNode; - var nextSibling; - var node; - - node = firstNodeFor(parentNode, html); - var insertBefore = start.nextSibling; - - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, insertBefore); - node = nextSibling; - } - }; + /* global navigator */ + // require the main entry points for each of these packages + // this is so that the global exports occur properly + + // do this to ensure that Ember.Test is defined properly on the global + // if it is present. + if (Ember.__loader.registry['ember-testing']) { + requireModule('ember-testing'); } - Metamorph.prototype.html = function(html) { - this.checkRemoved(); - if (html === undefined) { return this.innerHTML; } - - htmlFunc.call(this, html); - - this.innerHTML = html; - }; - - Metamorph.prototype.replaceWith = function(html) { - this.checkRemoved(); - htmlFunc.call(this, html, true); - }; - - Metamorph.prototype.remove = removeFunc; - Metamorph.prototype.outerHTML = outerHTMLFunc; - Metamorph.prototype.appendTo = appendToFunc; - Metamorph.prototype.after = afterFunc; - Metamorph.prototype.prepend = prependFunc; - Metamorph.prototype.startTag = startTagFunc; - Metamorph.prototype.endTag = endTagFunc; - - Metamorph.prototype.isRemoved = function() { - var before = document.getElementById(this.start); - var after = document.getElementById(this.end); - - return !before || !after; - }; - - Metamorph.prototype.checkRemoved = function() { - if (this.isRemoved()) { - throw new Error("Cannot perform operations on a Metamorph that is not in the DOM."); - } - }; - - return Metamorph; - }); - -})(); - -(function() { -define("ember-handlebars-compiler", - ["ember-metal/core","exports"], - function(__dependency1__, __exports__) { - "use strict"; /** + Ember + @module ember - @submodule ember-handlebars-compiler */ - var Ember = __dependency1__["default"]; - - // ES6Todo: you'll need to import debugger once debugger is es6'd. - if (typeof Ember.assert === 'undefined') { Ember.assert = function(){}; }; - if (typeof Ember.FEATURES === 'undefined') { Ember.FEATURES = { isEnabled: function(){} }; }; - - var objectCreate = Object.create || function(parent) { - function F() {} - F.prototype = parent; - return new F(); - }; - - // set up for circular references later - var View, Component; - - // ES6Todo: when ember-debug is es6'ed import this. - // var emberAssert = Ember.assert; - var Handlebars = (Ember.imports && Ember.imports.Handlebars) || (this && this.Handlebars); - if (!Handlebars && typeof require === 'function') { - Handlebars = require('handlebars'); - } - - - - /** - Prepares the Handlebars templating library for use inside Ember's view - system. - - The `Ember.Handlebars` object is the standard Handlebars library, extended to - use Ember's `get()` method instead of direct property access, which allows - computed properties to be used inside templates. - - To create an `Ember.Handlebars` template, call `Ember.Handlebars.compile()`. - This will return a function that can be used by `Ember.View` for rendering. - - @class Handlebars - @namespace Ember - */ - var EmberHandlebars = Ember.Handlebars = objectCreate(Handlebars); - - /** - Register a bound helper or custom view helper. - - ## Simple bound helper example - - ```javascript - Ember.Handlebars.helper('capitalize', function(value) { - return value.toUpperCase(); }); - ``` - - The above bound helper can be used inside of templates as follows: - - ```handlebars - {{capitalize name}} - ``` - - In this case, when the `name` property of the template's context changes, - the rendered value of the helper will update to reflect this change. - - For more examples of bound helpers, see documentation for - `Ember.Handlebars.registerBoundHelper`. - - ## Custom view helper example - - Assuming a view subclass named `App.CalendarView` were defined, a helper - for rendering instances of this view could be registered as follows: - - ```javascript - Ember.Handlebars.helper('calendar', App.CalendarView): - ``` - - The above bound helper can be used inside of templates as follows: - - ```handlebars - {{calendar}} - ``` - - Which is functionally equivalent to: - - ```handlebars - {{view App.CalendarView}} - ``` - - Options in the helper will be passed to the view in exactly the same - manner as with the `view` helper. - - @method helper - @for Ember.Handlebars - @param {String} name - @param {Function|Ember.View} function or view class constructor - @param {String} dependentKeys* - */ - EmberHandlebars.helper = function(name, value) { - if (!View) { View = requireModule('ember-views/views/view')['View']; } // ES6TODO: stupid circular dep - if (!Component) { Component = requireModule('ember-views/views/component')['default']; } // ES6TODO: stupid circular dep - - - if (View.detect(value)) { - EmberHandlebars.registerHelper(name, EmberHandlebars.makeViewHelper(value)); - } else { - EmberHandlebars.registerBoundHelper.apply(null, arguments); - } - }; - - /** - Returns a helper function that renders the provided ViewClass. - - Used internally by Ember.Handlebars.helper and other methods - involving helper/component registration. - - @private - @method makeViewHelper - @for Ember.Handlebars - @param {Function} ViewClass view class constructor - @since 1.2.0 - */ - EmberHandlebars.makeViewHelper = function(ViewClass) { - return function(options) { - return EmberHandlebars.helpers.view.call(this, ViewClass, options); - }; - }; - - /** - @class helpers - @namespace Ember.Handlebars - */ - EmberHandlebars.helpers = objectCreate(Handlebars.helpers); - - /** - Override the the opcode compiler and JavaScript compiler for Handlebars. - - @class Compiler - @namespace Ember.Handlebars - @private - @constructor - */ - EmberHandlebars.Compiler = function() {}; - - // Handlebars.Compiler doesn't exist in runtime-only - if (Handlebars.Compiler) { - EmberHandlebars.Compiler.prototype = objectCreate(Handlebars.Compiler.prototype); - } - - EmberHandlebars.Compiler.prototype.compiler = EmberHandlebars.Compiler; - - /** - @class JavaScriptCompiler - @namespace Ember.Handlebars - @private - @constructor - */ - EmberHandlebars.JavaScriptCompiler = function() {}; - - // Handlebars.JavaScriptCompiler doesn't exist in runtime-only - if (Handlebars.JavaScriptCompiler) { - EmberHandlebars.JavaScriptCompiler.prototype = objectCreate(Handlebars.JavaScriptCompiler.prototype); - EmberHandlebars.JavaScriptCompiler.prototype.compiler = EmberHandlebars.JavaScriptCompiler; - } - - - EmberHandlebars.JavaScriptCompiler.prototype.namespace = "Ember.Handlebars"; - - EmberHandlebars.JavaScriptCompiler.prototype.initializeBuffer = function() { - return "''"; - }; - - /** - Override the default buffer for Ember Handlebars. By default, Handlebars - creates an empty String at the beginning of each invocation and appends to - it. Ember's Handlebars overrides this to append to a single shared buffer. - - @private - @method appendToBuffer - @param string {String} - */ - EmberHandlebars.JavaScriptCompiler.prototype.appendToBuffer = function(string) { - return "data.buffer.push("+string+");"; - }; - - // Hacks ahead: - // Handlebars presently has a bug where the `blockHelperMissing` hook - // doesn't get passed the name of the missing helper name, but rather - // gets passed the value of that missing helper evaluated on the current - // context, which is most likely `undefined` and totally useless. - // - // So we alter the compiled template function to pass the name of the helper - // instead, as expected. - // - // This can go away once the following is closed: - // https://github.com/wycats/handlebars.js/issues/634 - - var DOT_LOOKUP_REGEX = /helpers\.(.*?)\)/, - BRACKET_STRING_LOOKUP_REGEX = /helpers\['(.*?)'/, - INVOCATION_SPLITTING_REGEX = /(.*blockHelperMissing\.call\(.*)(stack[0-9]+)(,.*)/; - - EmberHandlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation = function(source) { - var helperInvocation = source[source.length - 1], - helperName = (DOT_LOOKUP_REGEX.exec(helperInvocation) || BRACKET_STRING_LOOKUP_REGEX.exec(helperInvocation))[1], - matches = INVOCATION_SPLITTING_REGEX.exec(helperInvocation); - - source[source.length - 1] = matches[1] + "'" + helperName + "'" + matches[3]; - }; - - var stringifyBlockHelperMissing = EmberHandlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation; - - var originalBlockValue = EmberHandlebars.JavaScriptCompiler.prototype.blockValue; - EmberHandlebars.JavaScriptCompiler.prototype.blockValue = function() { - originalBlockValue.apply(this, arguments); - stringifyBlockHelperMissing(this.source); - }; - - var originalAmbiguousBlockValue = EmberHandlebars.JavaScriptCompiler.prototype.ambiguousBlockValue; - EmberHandlebars.JavaScriptCompiler.prototype.ambiguousBlockValue = function() { - originalAmbiguousBlockValue.apply(this, arguments); - stringifyBlockHelperMissing(this.source); - }; - - /** - Rewrite simple mustaches from `{{foo}}` to `{{bind "foo"}}`. This means that - all simple mustaches in Ember's Handlebars will also set up an observer to - keep the DOM up to date when the underlying property changes. - - @private - @method mustache - @for Ember.Handlebars.Compiler - @param mustache - */ - EmberHandlebars.Compiler.prototype.mustache = function(mustache) { - if (!(mustache.params.length || mustache.hash)) { - var id = new Handlebars.AST.IdNode([{ part: '_triageMustache' }]); - - // Update the mustache node to include a hash value indicating whether the original node - // was escaped. This will allow us to properly escape values when the underlying value - // changes and we need to re-render the value. - if (!mustache.escaped) { - mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]); - mustache.hash.pairs.push(["unescaped", new Handlebars.AST.StringNode("true")]); - } - mustache = new Handlebars.AST.MustacheNode([id].concat([mustache.id]), mustache.hash, !mustache.escaped); - } - - return Handlebars.Compiler.prototype.mustache.call(this, mustache); - }; - - /** - Used for precompilation of Ember Handlebars templates. This will not be used - during normal app execution. - - @method precompile - @for Ember.Handlebars - @static - @param {String} string The template to precompile - @param {Boolean} asObject optional parameter, defaulting to true, of whether or not the - compiled template should be returned as an Object or a String - */ - EmberHandlebars.precompile = function(string, asObject) { - var ast = Handlebars.parse(string); - - var options = { - knownHelpers: { - action: true, - unbound: true, - 'bind-attr': true, - template: true, - view: true, - _triageMustache: true - }, - data: true, - stringParams: true - }; - - asObject = asObject === undefined ? true : asObject; - - var environment = new EmberHandlebars.Compiler().compile(ast, options); - return new EmberHandlebars.JavaScriptCompiler().compile(environment, options, undefined, asObject); - }; - - // We don't support this for Handlebars runtime-only - if (Handlebars.compile) { - /** - The entry point for Ember Handlebars. This replaces the default - `Handlebars.compile` and turns on template-local data and String - parameters. - - @method compile - @for Ember.Handlebars - @static - @param {String} string The template to compile - @return {Function} - */ - EmberHandlebars.compile = function(string) { - var ast = Handlebars.parse(string); - var options = { data: true, stringParams: true }; - var environment = new EmberHandlebars.Compiler().compile(ast, options); - var templateSpec = new EmberHandlebars.JavaScriptCompiler().compile(environment, options, undefined, true); - - var template = EmberHandlebars.template(templateSpec); - template.isMethod = false; //Make sure we don't wrap templates with ._super - - return template; - }; - } - - __exports__["default"] = EmberHandlebars; - }); -})(); - -(function() { -define("ember-handlebars/component_lookup", - ["ember-runtime/system/object","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var EmberObject = __dependency1__["default"]; - - var ComponentLookup = EmberObject.extend({ - lookupFactory: function(name, container) { - - container = container || this.container; - - var fullName = 'component:' + name, - templateFullName = 'template:components/' + name, - templateRegistered = container && container.has(templateFullName); - - if (templateRegistered) { - container.injection(fullName, 'layout', templateFullName); - } - - var Component = container.lookupFactory(fullName); - - // Only treat as a component if either the component - // or a template has been registered. - if (templateRegistered || Component) { - if (!Component) { - container.register(fullName, Ember.Component); - Component = container.lookupFactory(fullName); - } - return Component; - } - } - }); - - __exports__["default"] = ComponentLookup; - }); -define("ember-handlebars/controls", - ["ember-handlebars/controls/checkbox","ember-handlebars/controls/text_field","ember-handlebars/controls/text_area","ember-metal/core","ember-handlebars-compiler","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { - "use strict"; - var Checkbox = __dependency1__["default"]; - var TextField = __dependency2__["default"]; - var TextArea = __dependency3__["default"]; - - var Ember = __dependency4__["default"]; - // Ember.assert - // var emberAssert = Ember.assert; - - var EmberHandlebars = __dependency5__["default"]; - var helpers = EmberHandlebars.helpers; - /** - @module ember - @submodule ember-handlebars-compiler - */ - - /** - - The `{{input}}` helper inserts an HTML `` tag into the template, - with a `type` value of either `text` or `checkbox`. If no `type` is provided, - `text` will be the default value applied. The attributes of `{{input}}` - match those of the native HTML tag as closely as possible for these two types. - - ## Use as text field - An `{{input}}` with no `type` or a `type` of `text` will render an HTML text input. - The following HTML attributes can be set via the helper: - - - - - - - - - - - - -
      `readonly``required``autofocus`
      `value``placeholder``disabled`
      `size``tabindex``maxlength`
      `name``min``max`
      `pattern``accept``autocomplete`
      `autosave``formaction``formenctype`
      `formmethod``formnovalidate``formtarget`
      `height``inputmode``multiple`
      `step``width``form`
      `selectionDirection``spellcheck` 
      - - - When set to a quoted string, these values will be directly applied to the HTML - element. When left unquoted, these values will be bound to a property on the - template's current rendering context (most typically a controller instance). - - ## Unbound: - - ```handlebars - {{input value="http://www.facebook.com"}} - ``` - - - ```html - - ``` - - ## Bound: - - ```javascript - App.ApplicationController = Ember.Controller.extend({ - firstName: "Stanley", - entryNotAllowed: true - }); - ``` - - - ```handlebars - {{input type="text" value=firstName disabled=entryNotAllowed size="50"}} - ``` - - - ```html - - ``` - - ## Extension - - Internally, `{{input type="text"}}` creates an instance of `Ember.TextField`, passing - arguments from the helper to `Ember.TextField`'s `create` method. You can extend the - capabilities of text inputs in your applications by reopening this class. For example, - if you are building a Bootstrap project where `data-*` attributes are used, you - can add one to the `TextField`'s `attributeBindings` property: - - - ```javascript - Ember.TextField.reopen({ - attributeBindings: ['data-error'] - }); - ``` - - Keep in mind when writing `Ember.TextField` subclasses that `Ember.TextField` - itself extends `Ember.Component`, meaning that it does NOT inherit - the `controller` of the parent view. - - See more about [Ember components](api/classes/Ember.Component.html) - - - ## Use as checkbox - - An `{{input}}` with a `type` of `checkbox` will render an HTML checkbox input. - The following HTML attributes can be set via the helper: - - * `checked` - * `disabled` - * `tabindex` - * `indeterminate` - * `name` - * `autofocus` - * `form` - - - When set to a quoted string, these values will be directly applied to the HTML - element. When left unquoted, these values will be bound to a property on the - template's current rendering context (most typically a controller instance). - - ## Unbound: - - ```handlebars - {{input type="checkbox" name="isAdmin"}} - ``` - - ```html - - ``` - - ## Bound: - - ```javascript - App.ApplicationController = Ember.Controller.extend({ - isAdmin: true - }); - ``` - - - ```handlebars - {{input type="checkbox" checked=isAdmin }} - ``` - - - ```html - - ``` - - ## Extension - - Internally, `{{input type="checkbox"}}` creates an instance of `Ember.Checkbox`, passing - arguments from the helper to `Ember.Checkbox`'s `create` method. You can extend the - capablilties of checkbox inputs in your applications by reopening this class. For example, - if you wanted to add a css class to all checkboxes in your application: - - - ```javascript - Ember.Checkbox.reopen({ - classNames: ['my-app-checkbox'] - }); - ``` - - - @method input - @for Ember.Handlebars.helpers - @param {Hash} options - */ - function inputHelper(options) { - - var hash = options.hash, - types = options.hashTypes, - inputType = hash.type, - onEvent = hash.on; - - delete hash.type; - delete hash.on; - - if (inputType === 'checkbox') { - return helpers.view.call(this, Checkbox, options); - } else { - if (inputType) { hash.type = inputType; } - hash.onEvent = onEvent || 'enter'; - return helpers.view.call(this, TextField, options); - } - } - - /** - `{{textarea}}` inserts a new instance of ` - ``` - - Bound: - - In the following example, the `writtenWords` property on `App.ApplicationController` - will be updated live as the user types 'Lots of text that IS bound' into - the text area of their browser's window. - - ```javascript - App.ApplicationController = Ember.Controller.extend({ - writtenWords: "Lots of text that IS bound" - }); - ``` - - ```handlebars - {{textarea value=writtenWords}} - ``` - - Would result in the following HTML: - - ```html - - ``` - - If you wanted a one way binding between the text area and a div tag - somewhere else on your screen, you could use `Ember.computed.oneWay`: - - ```javascript - App.ApplicationController = Ember.Controller.extend({ - writtenWords: "Lots of text that IS bound", - outputWrittenWords: Ember.computed.oneWay("writtenWords") - }); - ``` - - ```handlebars - {{textarea value=writtenWords}} - -
      - {{outputWrittenWords}} -
      - ``` - - Would result in the following HTML: - - ```html - - - <-- the following div will be updated in real time as you type --> - -
      - 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 - - - <-- both updated in real time --> - - - ``` - - ## Extension - - Internally, `{{textarea}}` creates an instance of `Ember.TextArea`, passing - arguments from the helper to `Ember.TextArea`'s `create` method. You can - extend the capabilities of text areas in your application by reopening this - class. For example, if you are building a Bootstrap project where `data-*` - attributes are used, you can globally add support for a `data-*` attribute - on all `{{textarea}}`s' in your app by reopening `Ember.TextArea` or - `Ember.TextSupport` and adding it to the `attributeBindings` concatenated - property: - - ```javascript - Ember.TextArea.reopen({ - attributeBindings: ['data-error'] - }); - ``` - - Keep in mind when writing `Ember.TextArea` subclasses that `Ember.TextArea` - itself extends `Ember.Component`, meaning that it does NOT inherit - the `controller` of the parent view. - - See more about [Ember components](api/classes/Ember.Component.html) - - @method textarea - @for Ember.Handlebars.helpers - @param {Hash} options - */ - function textareaHelper(options) { - - var hash = options.hash, - types = options.hashTypes; - - return helpers.view.call(this, TextArea, options); - } - - __exports__.inputHelper = inputHelper; - __exports__.textareaHelper = textareaHelper; - }); -define("ember-handlebars/controls/checkbox", - ["ember-metal/property_get","ember-metal/property_set","ember-views/views/view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var get = __dependency1__.get; - var set = __dependency2__.set; - var View = __dependency3__.View; - - /** - @module ember - @submodule ember-handlebars - */ - - /** - The internal class used to create text inputs when the `{{input}}` - helper is used with `type` of `checkbox`. - - See [handlebars.helpers.input](/api/classes/Ember.Handlebars.helpers.html#method_input) for usage details. - - ## Direct manipulation of `checked` - - The `checked` attribute of an `Ember.Checkbox` object should always be set - through the Ember object or by interacting with its rendered element - representation via the mouse, keyboard, or touch. Updating the value of the - checkbox via jQuery will result in the checked value of the object and its - element losing synchronization. - - ## Layout and LayoutName properties - - Because HTML `input` elements are self closing `layout` and `layoutName` - properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s - layout section for more information. - - @class Checkbox - @namespace Ember - @extends Ember.View - */ - var Checkbox = View.extend({ - instrumentDisplay: '{{input type="checkbox"}}', - - classNames: ['ember-checkbox'], - - tagName: 'input', - - attributeBindings: ['type', 'checked', 'indeterminate', 'disabled', 'tabindex', 'name', - 'autofocus', 'required', 'form'], - - type: "checkbox", - checked: false, - disabled: false, - indeterminate: false, - - init: function() { - this._super(); - this.on("change", this, this._updateElementValue); - }, - - didInsertElement: function() { - this._super(); - get(this, 'element').indeterminate = !!get(this, 'indeterminate'); - }, - - _updateElementValue: function() { - set(this, 'checked', this.$().prop('checked')); - } - }); - - __exports__["default"] = Checkbox; - }); -define("ember-handlebars/controls/select", - ["ember-handlebars-compiler","ember-metal/enumerable_utils","ember-metal/property_get","ember-metal/property_set","ember-views/views/view","ember-views/views/collection_view","ember-metal/utils","ember-metal/is_none","ember-metal/computed","ember-runtime/system/native_array","ember-metal/mixin","ember-metal/properties","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { - "use strict"; - /*jshint eqeqeq:false newcap:false */ - - /** - @module ember - @submodule ember-handlebars - */ - - var EmberHandlebars = __dependency1__["default"]; - var EnumerableUtils = __dependency2__["default"]; - var get = __dependency3__.get; - var set = __dependency4__.set; - var View = __dependency5__.View; - var CollectionView = __dependency6__["default"]; - var isArray = __dependency7__.isArray; - var isNone = __dependency8__["default"]; - var computed = __dependency9__.computed; - var A = __dependency10__.A; - var observer = __dependency11__.observer; - var defineProperty = __dependency12__.defineProperty; - - var indexOf = EnumerableUtils.indexOf, - indexesOf = EnumerableUtils.indexesOf, - forEach = EnumerableUtils.forEach, - replace = EnumerableUtils.replace, - precompileTemplate = EmberHandlebars.compile; - - var SelectOption = View.extend({ - instrumentDisplay: 'Ember.SelectOption', - - tagName: 'option', - attributeBindings: ['value', 'selected'], - - defaultTemplate: function(context, options) { - options = { data: options.data, hash: {} }; - EmberHandlebars.helpers.bind.call(context, "view.label", options); - }, - - init: function() { - this.labelPathDidChange(); - this.valuePathDidChange(); - - this._super(); - }, - - selected: computed(function() { - var content = get(this, 'content'), - selection = get(this, 'parentView.selection'); - if (get(this, 'parentView.multiple')) { - return selection && indexOf(selection, content.valueOf()) > -1; - } else { - // Primitives get passed through bindings as objects... since - // `new Number(4) !== 4`, we use `==` below - return content == selection; - } - }).property('content', 'parentView.selection'), - - labelPathDidChange: observer('parentView.optionLabelPath', function() { - var labelPath = get(this, 'parentView.optionLabelPath'); - - if (!labelPath) { return; } - - defineProperty(this, 'label', computed(function() { - return get(this, labelPath); - }).property(labelPath)); - }), - - valuePathDidChange: observer('parentView.optionValuePath', function() { - var valuePath = get(this, 'parentView.optionValuePath'); - - if (!valuePath) { return; } - - defineProperty(this, 'value', computed(function() { - return get(this, valuePath); - }).property(valuePath)); - }) - }); - - var SelectOptgroup = CollectionView.extend({ - instrumentDisplay: 'Ember.SelectOptgroup', - - tagName: 'optgroup', - attributeBindings: ['label'], - - selectionBinding: 'parentView.selection', - multipleBinding: 'parentView.multiple', - optionLabelPathBinding: 'parentView.optionLabelPath', - optionValuePathBinding: 'parentView.optionValuePath', - - itemViewClassBinding: 'parentView.optionView' - }); - - /** - The `Ember.Select` view class renders a - [select](https://developer.mozilla.org/en/HTML/Element/select) HTML element, - allowing the user to choose from a list of options. - - The text and `value` property of each ` - - - ``` - - The `value` attribute of the selected `"); - return buffer; - } - -function program3(depth0,data) { - - var stack1; - stack1 = helpers.each.call(depth0, "view.groupedContent", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(4, program4, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - else { data.buffer.push(''); } - } -function program4(depth0,data) { - - - data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.groupView", {hash:{ - 'content': ("content"), - 'label': ("label") - },hashTypes:{'content': "ID",'label': "ID"},hashContexts:{'content': depth0,'label': depth0},contexts:[depth0],types:["ID"],data:data}))); - } - -function program6(depth0,data) { - - var stack1; - stack1 = helpers.each.call(depth0, "view.content", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(7, program7, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - else { data.buffer.push(''); } - } -function program7(depth0,data) { - - - data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.optionView", {hash:{ - 'content': ("") - },hashTypes:{'content': "ID"},hashContexts:{'content': depth0},contexts:[depth0],types:["ID"],data:data}))); - } - - stack1 = helpers['if'].call(depth0, "view.prompt", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - stack1 = helpers['if'].call(depth0, "view.optionGroupPath", {hash:{},hashTypes:{},hashContexts:{},inverse:self.program(6, program6, data),fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - return buffer; - -}), - attributeBindings: ['multiple', 'disabled', 'tabindex', 'name', 'required', 'autofocus', - 'form', 'size'], - - /** - The `multiple` attribute of the select element. Indicates whether multiple - options can be selected. - - @property multiple - @type Boolean - @default false - */ - multiple: false, - - /** - The `disabled` attribute of the select element. Indicates whether - the element is disabled from interactions. - - @property disabled - @type Boolean - @default false - */ - disabled: false, - - /** - The `required` attribute of the select element. Indicates whether - a selected option is required for form validation. - - @property required - @type Boolean - @default false - @since 1.5.0 - */ - required: false, - - /** - The list of options. - - If `optionLabelPath` and `optionValuePath` are not overridden, this should - be a list of strings, which will serve simultaneously as labels and values. - - Otherwise, this should be a list of objects. For instance: - - ```javascript - Ember.Select.create({ - content: A([ - { id: 1, firstName: 'Yehuda' }, - { id: 2, firstName: 'Tom' } - ]), - optionLabelPath: 'content.firstName', - optionValuePath: 'content.id' - }); - ``` - - @property content - @type Array - @default null - */ - content: null, - - /** - When `multiple` is `false`, the element of `content` that is currently - selected, if any. - - When `multiple` is `true`, an array of such elements. - - @property selection - @type Object or Array - @default null - */ - selection: null, - - /** - In single selection mode (when `multiple` is `false`), value can be used to - get the current selection's value or set the selection by it's value. - - It is not currently supported in multiple selection mode. - - @property value - @type String - @default null - */ - value: computed(function(key, value) { - if (arguments.length === 2) { return value; } - var valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''); - return valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection'); - }).property('selection'), - - /** - If given, a top-most dummy option will be rendered to serve as a user - prompt. - - @property prompt - @type String - @default null - */ - prompt: null, - - /** - The path of the option labels. See [content](/api/classes/Ember.Select.html#property_content). - - @property optionLabelPath - @type String - @default 'content' - */ - optionLabelPath: 'content', - - /** - The path of the option values. See [content](/api/classes/Ember.Select.html#property_content). - - @property optionValuePath - @type String - @default 'content' - */ - optionValuePath: 'content', - - /** - The path of the option group. - When this property is used, `content` should be sorted by `optionGroupPath`. - - @property optionGroupPath - @type String - @default null - */ - optionGroupPath: null, - - /** - The view class for optgroup. - - @property groupView - @type Ember.View - @default Ember.SelectOptgroup - */ - groupView: SelectOptgroup, - - groupedContent: computed(function() { - var groupPath = get(this, 'optionGroupPath'); - var groupedContent = A(); - var content = get(this, 'content') || []; - - forEach(content, function(item) { - var label = get(item, groupPath); - - if (get(groupedContent, 'lastObject.label') !== label) { - groupedContent.pushObject({ - label: label, - content: A() - }); - } - - get(groupedContent, 'lastObject.content').push(item); - }); - - return groupedContent; - }).property('optionGroupPath', 'content.@each'), - - /** - The view class for option. - - @property optionView - @type Ember.View - @default Ember.SelectOption - */ - optionView: SelectOption, - - _change: function() { - if (get(this, 'multiple')) { - this._changeMultiple(); - } else { - this._changeSingle(); - } - }, - - selectionDidChange: observer('selection.@each', function() { - var selection = get(this, 'selection'); - if (get(this, 'multiple')) { - if (!isArray(selection)) { - set(this, 'selection', A([selection])); - return; - } - this._selectionDidChangeMultiple(); - } else { - this._selectionDidChangeSingle(); - } - }), - - valueDidChange: observer('value', function() { - var content = get(this, 'content'), - value = get(this, 'value'), - valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''), - selectedValue = (valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection')), - selection; - - if (value !== selectedValue) { - selection = content ? content.find(function(obj) { - return value === (valuePath ? get(obj, valuePath) : obj); - }) : null; - - this.set('selection', selection); - } - }), - - - _triggerChange: function() { - var selection = get(this, 'selection'); - var value = get(this, 'value'); - - if (!isNone(selection)) { this.selectionDidChange(); } - if (!isNone(value)) { this.valueDidChange(); } - - this._change(); - }, - - _changeSingle: function() { - var selectedIndex = this.$()[0].selectedIndex, - content = get(this, 'content'), - prompt = get(this, 'prompt'); - - if (!content || !get(content, 'length')) { return; } - if (prompt && selectedIndex === 0) { set(this, 'selection', null); return; } - - if (prompt) { selectedIndex -= 1; } - set(this, 'selection', content.objectAt(selectedIndex)); - }, - - - _changeMultiple: function() { - var options = this.$('option:selected'), - prompt = get(this, 'prompt'), - offset = prompt ? 1 : 0, - content = get(this, 'content'), - selection = get(this, 'selection'); - - if (!content) { return; } - if (options) { - var selectedIndexes = options.map(function() { - return this.index - offset; - }).toArray(); - var newSelection = content.objectsAt(selectedIndexes); - - if (isArray(selection)) { - replace(selection, 0, get(selection, 'length'), newSelection); - } else { - set(this, 'selection', newSelection); - } - } - }, - - _selectionDidChangeSingle: function() { - var el = this.get('element'); - if (!el) { return; } - - var content = get(this, 'content'), - selection = get(this, 'selection'), - selectionIndex = content ? indexOf(content, selection) : -1, - prompt = get(this, 'prompt'); - - if (prompt) { selectionIndex += 1; } - if (el) { el.selectedIndex = selectionIndex; } - }, - - _selectionDidChangeMultiple: function() { - var content = get(this, 'content'), - selection = get(this, 'selection'), - selectedIndexes = content ? indexesOf(content, selection) : [-1], - prompt = get(this, 'prompt'), - offset = prompt ? 1 : 0, - options = this.$('option'), - adjusted; - - if (options) { - options.each(function() { - adjusted = this.index > -1 ? this.index - offset : -1; - this.selected = indexOf(selectedIndexes, adjusted) > -1; - }); - } - }, - - init: function() { - this._super(); - this.on("didInsertElement", this, this._triggerChange); - this.on("change", this, this._change); - } - }); - - __exports__["default"] = Select - __exports__.Select = Select; - __exports__.SelectOption = SelectOption; - __exports__.SelectOptgroup = SelectOptgroup; - }); -define("ember-handlebars/controls/text_area", - ["ember-metal/property_get","ember-views/views/component","ember-handlebars/controls/text_support","ember-metal/mixin","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - - /** - @module ember - @submodule ember-handlebars - */ - var get = __dependency1__.get; - var Component = __dependency2__["default"]; - var TextSupport = __dependency3__["default"]; - var observer = __dependency4__.observer; - - /** - The internal class used to create textarea element when the `{{textarea}}` - helper is used. - - See [handlebars.helpers.textarea](/api/classes/Ember.Handlebars.helpers.html#method_textarea) for usage details. - - ## Layout and LayoutName properties - - Because HTML `textarea` elements do not contain inner HTML the `layout` and - `layoutName` properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s - layout section for more information. - - @class TextArea - @namespace Ember - @extends Ember.Component - @uses Ember.TextSupport - */ - var TextArea = Component.extend(TextSupport, { - instrumentDisplay: '{{textarea}}', - - classNames: ['ember-text-area'], - - tagName: "textarea", - attributeBindings: ['rows', 'cols', 'name', 'selectionEnd', 'selectionStart', 'wrap'], - rows: null, - cols: null, - - _updateElementValue: observer('value', function() { - // We do this check so cursor position doesn't get affected in IE - var value = get(this, 'value'), - $el = this.$(); - if ($el && value !== $el.val()) { - $el.val(value); - } - }), - - init: function() { - this._super(); - this.on("didInsertElement", this, this._updateElementValue); - } - - }); - - __exports__["default"] = TextArea; - }); -define("ember-handlebars/controls/text_field", - ["ember-metal/property_get","ember-metal/property_set","ember-views/views/component","ember-handlebars/controls/text_support","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-handlebars - */ - - var get = __dependency1__.get; - var set = __dependency2__.set; - var Component = __dependency3__["default"]; - var TextSupport = __dependency4__["default"]; - - /** - - The internal class used to create text inputs when the `{{input}}` - helper is used with `type` of `text`. - - See [Handlebars.helpers.input](/api/classes/Ember.Handlebars.helpers.html#method_input) for usage details. - - ## Layout and LayoutName properties - - Because HTML `input` elements are self closing `layout` and `layoutName` - properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s - layout section for more information. - - @class TextField - @namespace Ember - @extends Ember.Component - @uses Ember.TextSupport - */ - var TextField = Component.extend(TextSupport, { - instrumentDisplay: '{{input type="text"}}', - - classNames: ['ember-text-field'], - tagName: "input", - attributeBindings: ['type', 'value', 'size', 'pattern', 'name', 'min', 'max', - 'accept', 'autocomplete', 'autosave', 'formaction', - 'formenctype', 'formmethod', 'formnovalidate', 'formtarget', - 'height', 'inputmode', 'list', 'multiple', 'pattern', 'step', - 'width'], - - /** - The `value` attribute of the input element. As the user inputs text, this - property is updated live. - - @property value - @type String - @default "" - */ - value: "", - - /** - The `type` attribute of the input element. - - @property type - @type String - @default "text" - */ - type: "text", - - /** - The `size` of the text field in characters. - - @property size - @type String - @default null - */ - size: null, - - /** - The `pattern` attribute of input element. - - @property pattern - @type String - @default null - */ - pattern: null, - - /** - The `min` attribute of input element used with `type="number"` or `type="range"`. - - @property min - @type String - @default null - @since 1.4.0 - */ - min: null, - - /** - The `max` attribute of input element used with `type="number"` or `type="range"`. - - @property max - @type String - @default null - @since 1.4.0 - */ - max: null - }); - - __exports__["default"] = TextField; - }); -define("ember-handlebars/controls/text_support", - ["ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-runtime/mixins/target_action_support","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-handlebars - */ - - var get = __dependency1__.get; - var set = __dependency2__.set; - var Mixin = __dependency3__.Mixin; - var TargetActionSupport = __dependency4__["default"]; - - /** - Shared mixin used by `Ember.TextField` and `Ember.TextArea`. - - @class TextSupport - @namespace Ember - @uses Ember.TargetActionSupport - @extends Ember.Mixin - @private - */ - var TextSupport = Mixin.create(TargetActionSupport, { - value: "", - - attributeBindings: ['placeholder', 'disabled', 'maxlength', 'tabindex', 'readonly', - 'autofocus', 'form', 'selectionDirection', 'spellcheck', 'required', - 'title', 'autocapitalize', 'autocorrect'], - placeholder: null, - disabled: false, - maxlength: null, - - init: function() { - this._super(); - this.on("focusOut", this, this._elementValueDidChange); - this.on("change", this, this._elementValueDidChange); - this.on("paste", this, this._elementValueDidChange); - this.on("cut", this, this._elementValueDidChange); - this.on("input", this, this._elementValueDidChange); - this.on("keyUp", this, this.interpretKeyEvents); - }, - - /** - The action to be sent when the user presses the return key. - - This is similar to the `{{action}}` helper, but is fired when - the user presses the return key when editing a text field, and sends - the value of the field as the context. - - @property action - @type String - @default null - */ - action: null, - - /** - The event that should send the action. - - Options are: - - * `enter`: the user pressed enter - * `keyPress`: the user pressed a key - - @property onEvent - @type String - @default enter - */ - onEvent: 'enter', - - /** - Whether they `keyUp` event that triggers an `action` to be sent continues - propagating to other views. - - By default, when the user presses the return key on their keyboard and - the text field has an `action` set, the action will be sent to the view's - controller and the key event will stop propagating. - - If you would like parent views to receive the `keyUp` event even after an - action has been dispatched, set `bubbles` to true. - - @property bubbles - @type Boolean - @default false - */ - bubbles: false, - - interpretKeyEvents: function(event) { - var map = TextSupport.KEY_EVENTS; - var method = map[event.keyCode]; - - this._elementValueDidChange(); - if (method) { return this[method](event); } - }, - - _elementValueDidChange: function() { - set(this, 'value', this.$().val()); - }, - - /** - The action to be sent when the user inserts a new line. - - Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 13. - Uses sendAction to send the `enter` action to the controller. - - @method insertNewline - @param {Event} event - */ - insertNewline: function(event) { - sendAction('enter', this, event); - sendAction('insert-newline', this, event); - }, - - /** - Called when the user hits escape. - - Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 27. - Uses sendAction to send the `escape-press` action to the controller. - - @method cancel - @param {Event} event - */ - cancel: function(event) { - sendAction('escape-press', this, event); - }, - - /** - Called when the text area is focused. - - @method focusIn - @param {Event} event - */ - focusIn: function(event) { - sendAction('focus-in', this, event); - }, - - /** - Called when the text area is blurred. - - @method focusOut - @param {Event} event - */ - focusOut: function(event) { - sendAction('focus-out', this, event); - }, - - /** - The action to be sent when the user presses a key. Enabled by setting - the `onEvent` property to `keyPress`. - - Uses sendAction to send the `keyPress` action to the controller. - - @method keyPress - @param {Event} event - */ - keyPress: function(event) { - sendAction('key-press', this, event); - } - - }); - - TextSupport.KEY_EVENTS = { - 13: 'insertNewline', - 27: 'cancel' - }; - - // In principle, this shouldn't be necessary, but the legacy - // sendAction semantics for TextField are different from - // the component semantics so this method normalizes them. - function sendAction(eventName, view, event) { - var action = get(view, eventName), - on = get(view, 'onEvent'), - value = get(view, 'value'); - - // back-compat support for keyPress as an event name even though - // it's also a method name that consumes the event (and therefore - // incompatible with sendAction semantics). - if (on === eventName || (on === 'keyPress' && eventName === 'key-press')) { - view.sendAction('action', value); - } - - view.sendAction(eventName, value); - - if (action || on === eventName) { - if(!get(view, 'bubbles')) { - event.stopPropagation(); - } - } - } - - __exports__["default"] = TextSupport; - }); -define("ember-handlebars/ext", - ["ember-metal/core","ember-runtime/system/string","ember-handlebars-compiler","ember-metal/property_get","ember-metal/binding","ember-metal/error","ember-metal/mixin","ember-metal/is_empty","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { - "use strict"; - var Ember = __dependency1__["default"]; - // Ember.FEATURES, Ember.assert, Ember.Handlebars, Ember.lookup - // var emberAssert = Ember.assert; - - var fmt = __dependency2__.fmt; - - var EmberHandlebars = __dependency3__["default"]; - var helpers = EmberHandlebars.helpers; - - var get = __dependency4__.get; - var isGlobalPath = __dependency5__.isGlobalPath; - var EmberError = __dependency6__["default"]; - var IS_BINDING = __dependency7__.IS_BINDING; - - // late bound via requireModule because of circular dependencies. - var resolveHelper, - SimpleHandlebarsView; - - var isEmpty = __dependency8__["default"]; - - var slice = [].slice, originalTemplate = EmberHandlebars.template; - - /** - If a path starts with a reserved keyword, returns the root - that should be used. - - @private - @method normalizePath - @for Ember - @param root {Object} - @param path {String} - @param data {Hash} - */ - function normalizePath(root, path, data) { - var keywords = (data && data.keywords) || {}, - keyword, isKeyword; - - // Get the first segment of the path. For example, if the - // path is "foo.bar.baz", returns "foo". - keyword = path.split('.', 1)[0]; - - // Test to see if the first path is a keyword that has been - // passed along in the view's data hash. If so, we will treat - // that object as the new root. - if (keywords.hasOwnProperty(keyword)) { - // Look up the value in the template's data hash. - root = keywords[keyword]; - isKeyword = true; - - // Handle cases where the entire path is the reserved - // word. In that case, return the object itself. - if (path === keyword) { - path = ''; - } else { - // Strip the keyword from the path and look up - // the remainder from the newly found root. - path = path.substr(keyword.length+1); - } - } - - return { root: root, path: path, isKeyword: isKeyword }; - }; - - - /** - Lookup both on root and on window. If the path starts with - a keyword, the corresponding object will be looked up in the - template's data hash and used to resolve the path. - - @method get - @for Ember.Handlebars - @param {Object} root The object to look up the property on - @param {String} path The path to be lookedup - @param {Object} options The template's option hash - */ - function handlebarsGet(root, path, options) { - var data = options && options.data, - normalizedPath = normalizePath(root, path, data), - value; - - - root = normalizedPath.root; - path = normalizedPath.path; - - value = get(root, path); - - if (value === undefined && root !== Ember.lookup && isGlobalPath(path)) { - value = get(Ember.lookup, path); - } - - - return value; - } - - /** - This method uses `Ember.Handlebars.get` to lookup a value, then ensures - that the value is escaped properly. - - If `unescaped` is a truthy value then the escaping will not be performed. - - @method getEscaped - @for Ember.Handlebars - @param {Object} root The object to look up the property on - @param {String} path The path to be lookedup - @param {Object} options The template's option hash - @since 1.4.0 - */ - function getEscaped(root, path, options) { - var result = handlebarsGet(root, path, options); - - if (result === null || result === undefined) { - result = ""; - } else if (!(result instanceof Handlebars.SafeString)) { - result = String(result); - } - if (!options.hash.unescaped){ - result = Handlebars.Utils.escapeExpression(result); - } - - return result; - }; - - function resolveParams(context, params, options) { - var resolvedParams = [], types = options.types, param, type; - - for (var i=0, l=params.length; i{{user.name}} - -
      -
      {{user.role.label}}
      - {{user.role.id}} - -

      {{user.role.description}}

      -
      - ``` - - `{{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.name}}
      - -
      - {{#with user.role}} -
      {{label}}
      - {{id}} - -

      {{description}}

      - {{/with}} -
      - ``` - - ### `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}} -
      - There are {{blogPosts.length}} blog posts written by {{user.name}}. -
      - - {{#each post in blogPosts}} -
    • {{post.title}}
    • - {{/each}} - {{/with}} - ``` - - Without the `as` operator, it would be impossible to reference `user.name` in the example above. - - NOTE: The alias should not reuse a name from the bound property path. - For example: `{{#with foo.bar as foo}}` is not supported because it attempts to alias using - the first part of the property path, `foo`. Instead, use `{{#with foo.bar as baz}}`. - - ### `controller` option - - Adding `controller='something'` instructs the `{{with}}` helper to create and use an instance of - the specified controller with the new context as its content. - - This is very similar to using an `itemController` option with the `{{each}}` helper. - - ```handlebars - {{#with users.posts controller='userBlogPosts'}} - {{!- The current context is wrapped in our controller instance }} - {{/with}} - ``` - - In the above example, the template provided to the `{{with}}` block is now wrapped in the - `userBlogPost` controller, which provides a very elegant way to decorate the context with custom - functions/properties. - - @method with - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string - */ - function withHelper(context, options) { - var bindContext, preserveContext, controller, helperName = 'with'; - - if (arguments.length === 4) { - var keywordName, path, rootPath, normalized, contextPath; - - options = arguments[3]; - keywordName = arguments[2]; - path = arguments[0]; - - if (path) { - helperName += ' ' + path + ' as ' + keywordName; - } - - - var localizedOptions = o_create(options); - localizedOptions.data = o_create(options.data); - localizedOptions.data.keywords = o_create(options.data.keywords || {}); - - if (isGlobalPath(path)) { - contextPath = path; - } else { - normalized = normalizePath(this, path, options.data); - path = normalized.path; - rootPath = normalized.root; - - // This is a workaround for the fact that you cannot bind separate objects - // together. When we implement that functionality, we should use it here. - var contextKey = jQuery.expando + guidFor(rootPath); - localizedOptions.data.keywords[contextKey] = rootPath; - // if the path is '' ("this"), just bind directly to the current context - contextPath = path ? contextKey + '.' + path : contextKey; - } - - localizedOptions.hash.keywordName = keywordName; - localizedOptions.hash.keywordPath = contextPath; - - bindContext = this; - context = path; - options = localizedOptions; - preserveContext = true; - } else { - - helperName += ' ' + context; - bindContext = options.contexts[0]; - preserveContext = false; - } - - options.helperName = helperName; - options.isWithHelper = true; - - return bind.call(bindContext, context, options, preserveContext, exists); - } - /** - See [boundIf](/api/classes/Ember.Handlebars.helpers.html#method_boundIf) - and [unboundIf](/api/classes/Ember.Handlebars.helpers.html#method_unboundIf) - - @method if - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string - */ - function ifHelper(context, options) { - - options.helperName = options.helperName || ('if ' + context); - - if (options.data.isUnbound) { - return helpers.unboundIf.call(options.contexts[0], context, options); - } else { - return helpers.boundIf.call(options.contexts[0], context, options); - } - } - - /** - @method unless - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string - */ - function unlessHelper(context, options) { - - var fn = options.fn, inverse = options.inverse, helperName = 'unless'; - - if (context) { - helperName += ' ' + context; - } - - options.fn = inverse; - options.inverse = fn; - - options.helperName = options.helperName || helperName; - - if (options.data.isUnbound) { - return helpers.unboundIf.call(options.contexts[0], context, options); - } else { - return helpers.boundIf.call(options.contexts[0], context, options); - } - } - - /** - `bind-attr` allows you to create a binding between DOM element attributes and - Ember objects. For example: - - ```handlebars - imageTitle - ``` - - The above handlebars template will fill the ``'s `src` attribute will - the value of the property referenced with `"imageUrl"` and its `alt` - attribute with the value of the property referenced with `"imageTitle"`. - - If the rendering context of this template is the following object: - - ```javascript - { - imageUrl: 'http://lolcats.info/haz-a-funny', - imageTitle: 'A humorous image of a cat' - } - ``` - - The resulting HTML output will be: - - ```html - A humorous image of a cat - ``` - - `bind-attr` cannot redeclare existing DOM element attributes. The use of `src` - in the following `bind-attr` example will be ignored and the hard coded value - of `src="/failwhale.gif"` will take precedence: - - ```handlebars - imageTitle - ``` - - ### `bind-attr` and the `class` attribute - - `bind-attr` supports a special syntax for handling a number of cases unique - to the `class` DOM element attribute. The `class` attribute combines - multiple discrete values into a single attribute as a space-delimited - list of strings. Each string can be: - - * a string return value of an object's property. - * a boolean return value of an object's property - * a hard-coded value - - A string return value works identically to other uses of `bind-attr`. The - return value of the property will become the value of the attribute. For - example, the following view and template: - - ```javascript - AView = View.extend({ - someProperty: function() { - return "aValue"; - }.property() - }) - ``` - - ```handlebars - - ``` - - A boolean return value will insert a specified class name if the property - returns `true` and remove the class name if the property returns `false`. - - A class name is provided via the syntax - `somePropertyName:class-name-if-true`. - - ```javascript - AView = View.extend({ - someBool: true - }) - ``` - - ```handlebars - - ``` - - Result in the following rendered output: - - ```html - - ``` - - An additional section of the binding can be provided if you want to - replace the existing class instead of removing it when the boolean - value changes: - - ```handlebars - - ``` - - A hard-coded value can be used by prepending `:` to the desired - class name: `:class-name-to-always-apply`. - - ```handlebars - - ``` - - Results in the following rendered output: - - ```html - - ``` - - All three strategies - string return value, boolean return value, and - hard-coded value – can be combined in a single declaration: - - ```handlebars - - ``` - - @method bind-attr - @for Ember.Handlebars.helpers - @param {Hash} options - @return {String} HTML string - */ - function bindAttrHelper(options) { - var attrs = options.hash; - - - var view = options.data.view; - var ret = []; - - // we relied on the behavior of calling without - // context to mean this === window, but when running - // "use strict", it's possible for this to === undefined; - var ctx = this || window; - - // Generate a unique id for this element. This will be added as a - // data attribute to the element so it can be looked up when - // the bound property changes. - var dataId = ++Ember.uuid; - - // Handle classes differently, as we can bind multiple classes - var classBindings = attrs['class']; - if (classBindings != null) { - var classResults = bindClasses(ctx, classBindings, view, dataId, options); - - ret.push('class="' + Handlebars.Utils.escapeExpression(classResults.join(' ')) + '"'); - delete attrs['class']; - } - - var attrKeys = keys(attrs); - - // For each attribute passed, create an observer and emit the - // current value of the property as an attribute. - forEach.call(attrKeys, function(attr) { - var path = attrs[attr], - normalized; - - - normalized = normalizePath(ctx, path, options.data); - - var value = (path === 'this') ? normalized.root : handlebarsGet(ctx, path, options), - type = typeOf(value); - - - var observer, invoker; - - observer = function observer() { - var result = handlebarsGet(ctx, path, options); - - - var elem = view.$("[data-bindattr-" + dataId + "='" + dataId + "']"); - - // If we aren't able to find the element, it means the element - // to which we were bound has been removed from the view. - // In that case, we can assume the template has been re-rendered - // and we need to clean up the observer. - if (!elem || elem.length === 0) { - removeObserver(normalized.root, normalized.path, invoker); - return; - } - - View.applyAttributeBindings(elem, attr, result); - }; - - // Add an observer to the view for when the property changes. - // When the observer fires, find the element using the - // unique data id and update the attribute to the new value. - // Note: don't add observer when path is 'this' or path - // is whole keyword e.g. {{#each x in list}} ... {{bind-attr attr="x"}} - if (path !== 'this' && !(normalized.isKeyword && normalized.path === '' )) { - view.registerObserver(normalized.root, normalized.path, observer); - } - - // if this changes, also change the logic in ember-views/lib/views/view.js - if ((type === 'string' || (type === 'number' && !isNaN(value)))) { - ret.push(attr + '="' + Handlebars.Utils.escapeExpression(value) + '"'); - } else if (value && type === 'boolean') { - // The developer controls the attr name, so it should always be safe - ret.push(attr + '="' + attr + '"'); - } - }, this); - - // Add the unique identifier - // NOTE: We use all lower-case since Firefox has problems with mixed case in SVG - ret.push('data-bindattr-' + dataId + '="' + dataId + '"'); - return new SafeString(ret.join(' ')); - } - - /** - See `bind-attr` - - @method bindAttr - @for Ember.Handlebars.helpers - @deprecated - @param {Function} context - @param {Hash} options - @return {String} HTML string - */ - function bindAttrHelperDeprecated() { - return helpers['bind-attr'].apply(this, arguments); - } - - /** - Helper that, given a space-separated string of property paths and a context, - returns an array of class names. Calling this method also has the side - effect of setting up observers at those property paths, such that if they - change, the correct class name will be reapplied to the DOM element. - - For example, if you pass the string "fooBar", it will first look up the - "fooBar" value of the context. If that value is true, it will add the - "foo-bar" class to the current element (i.e., the dasherized form of - "fooBar"). If the value is a string, it will add that string as the class. - Otherwise, it will not add any new class name. - - @private - @method bindClasses - @for Ember.Handlebars - @param {Ember.Object} context The context from which to lookup properties - @param {String} classBindings A string, space-separated, of class bindings - to use - @param {View} view The view in which observers should look for the - element to update - @param {Srting} bindAttrId Optional bindAttr id used to lookup elements - @return {Array} An array of class names to add - */ - function bindClasses(context, classBindings, view, bindAttrId, options) { - var ret = [], newClass, value, elem; - - // Helper method to retrieve the property from the context and - // determine which class string to return, based on whether it is - // a Boolean or not. - var classStringForPath = function(root, parsedPath, options) { - var val, - path = parsedPath.path; - - if (path === 'this') { - val = root; - } else if (path === '') { - val = true; - } else { - val = handlebarsGet(root, path, options); - } - - return View._classStringForValue(path, val, parsedPath.className, parsedPath.falsyClassName); - }; - - // For each property passed, loop through and setup - // an observer. - forEach.call(classBindings.split(' '), function(binding) { - - // Variable in which the old class value is saved. The observer function - // closes over this variable, so it knows which string to remove when - // the property changes. - var oldClass; - - var observer, invoker; - - var parsedPath = View._parsePropertyPath(binding), - path = parsedPath.path, - pathRoot = context, - normalized; - - if (path !== '' && path !== 'this') { - normalized = normalizePath(context, path, options.data); - - pathRoot = normalized.root; - path = normalized.path; - } - - // Set up an observer on the context. If the property changes, toggle the - // class name. - observer = function() { - // Get the current value of the property - newClass = classStringForPath(context, parsedPath, options); - elem = bindAttrId ? view.$("[data-bindattr-" + bindAttrId + "='" + bindAttrId + "']") : view.$(); - - // If we can't find the element anymore, a parent template has been - // re-rendered and we've been nuked. Remove the observer. - if (!elem || elem.length === 0) { - removeObserver(pathRoot, path, invoker); - } else { - // If we had previously added a class to the element, remove it. - if (oldClass) { - elem.removeClass(oldClass); - } - - // If necessary, add a new class. Make sure we keep track of it so - // it can be removed in the future. - if (newClass) { - elem.addClass(newClass); - oldClass = newClass; - } else { - oldClass = null; - } - } - }; - - if (path !== '' && path !== 'this') { - view.registerObserver(pathRoot, path, observer); - } - - // We've already setup the observer; now we just need to figure out the - // correct behavior right now on the first pass through. - value = classStringForPath(context, parsedPath, options); - - if (value) { - ret.push(value); - - // Make sure we save the current value so that it can be removed if the - // observer fires. - oldClass = value; - } - }); - - return ret; - }; - - __exports__.bind = bind; - __exports__._triageMustacheHelper = _triageMustacheHelper; - __exports__.resolveHelper = resolveHelper; - __exports__.bindHelper = bindHelper; - __exports__.boundIfHelper = boundIfHelper; - __exports__.unboundIfHelper = unboundIfHelper; - __exports__.withHelper = withHelper; - __exports__.ifHelper = ifHelper; - __exports__.unlessHelper = unlessHelper; - __exports__.bindAttrHelper = bindAttrHelper; - __exports__.bindAttrHelperDeprecated = bindAttrHelperDeprecated; - __exports__.bindClasses = bindClasses; - }); -define("ember-handlebars/helpers/collection", - ["ember-metal/core","ember-metal/utils","ember-handlebars-compiler","ember-runtime/system/string","ember-metal/property_get","ember-handlebars/ext","ember-handlebars/helpers/view","ember-metal/computed","ember-views/views/collection_view","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __exports__) { - "use strict"; - /** - @module ember - @submodule ember-handlebars - */ - - var Ember = __dependency1__["default"]; - // Ember.assert, Ember.deprecate - var inspect = __dependency2__.inspect; - - // var emberAssert = Ember.assert; - // emberDeprecate = Ember.deprecate; - - var EmberHandlebars = __dependency3__["default"]; - var helpers = EmberHandlebars.helpers; - - var fmt = __dependency4__.fmt; - var get = __dependency5__.get; - var handlebarsGet = __dependency6__.handlebarsGet; - var ViewHelper = __dependency7__.ViewHelper; - var computed = __dependency8__.computed; - var CollectionView = __dependency9__["default"]; - - var alias = computed.alias; - /** - `{{collection}}` is a `Ember.Handlebars` helper for adding instances of - `Ember.CollectionView` to a template. See [Ember.CollectionView](/api/classes/Ember.CollectionView.html) - for additional information on how a `CollectionView` functions. - - `{{collection}}`'s primary use is as a block helper with a `contentBinding` - option pointing towards an `Ember.Array`-compatible object. An `Ember.View` - instance will be created for each item in its `content` property. Each view - will have its own `content` property set to the appropriate item in the - collection. - - The provided block will be applied as the template for each item's view. - - Given an empty `` the following template: - - ```handlebars - {{#collection contentBinding="App.items"}} - Hi {{view.content.name}} - {{/collection}} - ``` - - And the following application code - - ```javascript - App = Ember.Application.create() - App.items = [ - Ember.Object.create({name: 'Dave'}), - Ember.Object.create({name: 'Mary'}), - Ember.Object.create({name: 'Sara'}) - ] - ``` - - Will result in the HTML structure below - - ```html -
      -
      Hi Dave
      -
      Hi Mary
      -
      Hi Sara
      -
      - ``` - - ### 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 -
      -
      Greetings Dave
      -
      Greetings Mary
      -
      Greetings 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 -
      -

      Howdy Dave

      -

      Howdy Mary

      -

      Howdy 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') // -> "" - ``` - - @method debugger - @for Ember.Handlebars.helpers - @param {String} property - */ - function debuggerHelper(options) { - - // These are helpful values you can inspect while debugging. - var templateContext = this; - var typeOfTemplateContext = inspect(templateContext); - - debugger; - } - - __exports__.logHelper = logHelper; - __exports__.debuggerHelper = debuggerHelper; - }); -define("ember-handlebars/helpers/each", - ["ember-metal/core","ember-handlebars-compiler","ember-runtime/system/string","ember-metal/property_get","ember-metal/property_set","ember-handlebars/views/metamorph_view","ember-views/views/collection_view","ember-metal/binding","ember-runtime/controllers/controller","ember-runtime/controllers/array_controller","ember-runtime/mixins/array","ember-runtime/copy","ember-metal/run_loop","ember-metal/observer","ember-metal/events","ember-handlebars/ext","ember-metal/computed","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __exports__) { - "use strict"; - - /** - @module ember - @submodule ember-handlebars - */ - var Ember = __dependency1__["default"]; - // Ember.assert;, Ember.K - // var emberAssert = Ember.assert, - var K = Ember.K; - - var EmberHandlebars = __dependency2__["default"]; - var helpers = EmberHandlebars.helpers; - - var fmt = __dependency3__.fmt; - var get = __dependency4__.get; - var set = __dependency5__.set; - var _Metamorph = __dependency6__._Metamorph; - var _MetamorphView = __dependency6__._MetamorphView; - var CollectionView = __dependency7__["default"]; - var Binding = __dependency8__.Binding; - var ControllerMixin = __dependency9__.ControllerMixin; - var ArrayController = __dependency10__["default"]; - var EmberArray = __dependency11__["default"]; - var copy = __dependency12__["default"]; - var run = __dependency13__["default"]; - var addObserver = __dependency14__.addObserver; - var removeObserver = __dependency14__.removeObserver; - var addBeforeObserver = __dependency14__.addBeforeObserver; - var removeBeforeObserver = __dependency14__.removeBeforeObserver; - var on = __dependency15__.on; - var handlebarsGet = __dependency16__.handlebarsGet; - var computed = __dependency17__.computed; - - var handlebarsGet = __dependency16__.handlebarsGet; - - var EachView = CollectionView.extend(_Metamorph, { - - init: function() { - var itemController = get(this, 'itemController'); - var binding; - - if (itemController) { - var controller = get(this, 'controller.container').lookupFactory('controller:array').create({ - _isVirtual: true, - parentController: get(this, 'controller'), - itemController: itemController, - target: get(this, 'controller'), - _eachView: this - }); - - this.disableContentObservers(function() { - set(this, 'content', controller); - binding = new Binding('content', '_eachView.dataSource').oneWay(); - binding.connect(controller); - }); - - set(this, '_arrayController', controller); - } else { - this.disableContentObservers(function() { - binding = new Binding('content', 'dataSource').oneWay(); - binding.connect(this); - }); - } - - return this._super(); - }, - - _assertArrayLike: function(content) { - }, - - disableContentObservers: function(callback) { - removeBeforeObserver(this, 'content', null, '_contentWillChange'); - removeObserver(this, 'content', null, '_contentDidChange'); - - callback.call(this); - - addBeforeObserver(this, 'content', null, '_contentWillChange'); - addObserver(this, 'content', null, '_contentDidChange'); - }, - - itemViewClass: _MetamorphView, - emptyViewClass: _MetamorphView, - - createChildView: function(view, attrs) { - view = this._super(view, attrs); - - // At the moment, if a container view subclass wants - // to insert keywords, it is responsible for cloning - // the keywords hash. This will be fixed momentarily. - var keyword = get(this, 'keyword'); - var content = get(view, 'content'); - - if (keyword) { - var data = get(view, 'templateData'); - - data = copy(data); - data.keywords = view.cloneKeywords(); - set(view, 'templateData', data); - - // In this case, we do not bind, because the `content` of - // a #each item cannot change. - data.keywords[keyword] = content; - } - - // If {{#each}} is looping over an array of controllers, - // point each child view at their respective controller. - if (content && content.isController) { - set(view, 'controller', content); - } - - return view; - }, - - destroy: function() { - if (!this._super()) { return; } - - var arrayController = get(this, '_arrayController'); - - if (arrayController) { - arrayController.destroy(); - } - - return this; - } - }); - - // Defeatureify doesn't seem to like nested functions that need to be removed - function _addMetamorphCheck() { - EachView.reopen({ - _checkMetamorph: on('didInsertElement', function() { - }) - }); - } - - // until ember-debug is es6ed - var runInDebug = function(f){f()}; - - var GroupedEach = EmberHandlebars.GroupedEach = function(context, path, options) { - var self = this, - normalized = EmberHandlebars.normalizePath(context, path, options.data); - - this.context = context; - this.path = path; - this.options = options; - this.template = options.fn; - this.containingView = options.data.view; - this.normalizedRoot = normalized.root; - this.normalizedPath = normalized.path; - this.content = this.lookupContent(); - - this.addContentObservers(); - this.addArrayObservers(); - - this.containingView.on('willClearRender', function() { - self.destroy(); - }); - }; - - GroupedEach.prototype = { - contentWillChange: function() { - this.removeArrayObservers(); - }, - - contentDidChange: function() { - this.content = this.lookupContent(); - this.addArrayObservers(); - this.rerenderContainingView(); - }, - - contentArrayWillChange: K, - - contentArrayDidChange: function() { - this.rerenderContainingView(); - }, - - lookupContent: function() { - return handlebarsGet(this.normalizedRoot, this.normalizedPath, this.options); - }, - - addArrayObservers: function() { - if (!this.content) { return; } - - this.content.addArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' - }); - }, - - removeArrayObservers: function() { - if (!this.content) { return; } - - this.content.removeArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' - }); - }, - - addContentObservers: function() { - addBeforeObserver(this.normalizedRoot, this.normalizedPath, this, this.contentWillChange); - addObserver(this.normalizedRoot, this.normalizedPath, this, this.contentDidChange); - }, - - removeContentObservers: function() { - removeBeforeObserver(this.normalizedRoot, this.normalizedPath, this.contentWillChange); - removeObserver(this.normalizedRoot, this.normalizedPath, this.contentDidChange); - }, - - render: function() { - if (!this.content) { return; } - - var content = this.content, - contentLength = get(content, 'length'), - data = this.options.data, - template = this.template; - - data.insideEach = true; - for (var i = 0; i < contentLength; i++) { - template(content.objectAt(i), { data: data }); - } - }, - - rerenderContainingView: function() { - var self = this; - run.scheduleOnce('render', this, function() { - // It's possible it's been destroyed after we enqueued a re-render call. - if (!self.destroyed) { - self.containingView.rerender(); - } - }); - }, - - destroy: function() { - this.removeContentObservers(); - if (this.content) { - this.removeArrayObservers(); - } - this.destroyed = true; - } - }; - - /** - The `{{#each}}` helper loops over elements in a collection, rendering its - block once for each item. It is an extension of the base Handlebars `{{#each}}` - helper: - - ```javascript - Developers = [{name: 'Yehuda'},{name: 'Tom'}, {name: 'Paul'}]; - ``` - - ```handlebars - {{#each Developers}} - {{name}} - {{/each}} - ``` - - `{{each}}` supports an alternative syntax with element naming: - - ```handlebars - {{#each person in Developers}} - {{person.name}} - {{/each}} - ``` - - When looping over objects that do not have properties, `{{this}}` can be used - to render the object: - - ```javascript - DeveloperNames = ['Yehuda', 'Tom', 'Paul'] - ``` - - ```handlebars - {{#each DeveloperNames}} - {{this}} - {{/each}} - ``` - ### {{else}} condition - `{{#each}}` can have a matching `{{else}}`. The contents of this block will render - if the collection is empty. - - ``` - {{#each person in Developers}} - {{person.name}} - {{else}} -

      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 -
      -
      Greetings Dave
      -
      Greetings Mary
      -
      Greetings Sara
      -
      - ``` - - 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; i - {{#with loggedInUser}} - Last Login: {{lastLogin}} - User Info: {{template "user_info"}} - {{/with}} - - ``` - - ```html - - ``` - - ```handlebars - {{#if isUser}} - {{template "user_info"}} - {{else}} - {{template "unlogged_user_info"}} - {{/if}} - ``` - - This helper looks for templates in the global `Ember.TEMPLATES` hash. If you - add `