From 10d49c6983ef1e8b57abde2141e86da7f081877d Mon Sep 17 00:00:00 2001 From: Damien Mathieu <42@dmathieu.com> Date: Fri, 13 Dec 2013 15:05:32 +0100 Subject: [PATCH 01/28] refactor and add tests to tailing --- assets/scripts/app/app.coffee | 2 +- assets/scripts/app/tailing.coffee | 53 +++++++------ assets/scripts/spec/unit/tailing_spec.coffee | 78 ++++++++++++++++++++ assets/styles/main/log.sass | 4 + 4 files changed, 112 insertions(+), 25 deletions(-) create mode 100644 assets/scripts/spec/unit/tailing_spec.coffee diff --git a/assets/scripts/app/app.coffee b/assets/scripts/app/app.coffee index 4e16be6f..3f5adc8e 100644 --- a/assets/scripts/app/app.coffee +++ b/assets/scripts/app/app.coffee @@ -33,7 +33,7 @@ unless window.TravisApplication @slider = new Travis.Slider() @pusher = new Travis.Pusher(Travis.config.pusher_key) if Travis.config.pusher_key - @tailing = new Travis.Tailing() + @tailing = new Travis.Tailing($(window), '#tail', '#log') @set('auth', Travis.Auth.create(app: this, endpoint: Travis.config.api_endpoint)) diff --git a/assets/scripts/app/tailing.coffee b/assets/scripts/app/tailing.coffee index 276b18c7..a9be33df 100644 --- a/assets/scripts/app/tailing.coffee +++ b/assets/scripts/app/tailing.coffee @@ -1,12 +1,17 @@ -@Travis.Tailing = -> - @position = $(window).scrollTop() - $(window).scroll( $.throttle( 200, @onScroll.bind(this) ) ) - this - -$.extend Travis.Tailing.prototype, +class @Travis.Tailing options: timeout: 200 + tail: -> + $(@tail_selector) + log: -> + $(@log_selector) + + constructor: (@window, @tail_selector, @log_selector) -> + @position = @window.scrollTop() + @window.scroll( $.throttle( 200, @onScroll.bind(this) ) ) + this + run: -> @autoScroll() @positionButton() @@ -16,38 +21,38 @@ $.extend Travis.Tailing.prototype, if @active() then @stop() else @start() active: -> - $('#tail').hasClass('active') + @tail().hasClass('active') start: -> - $('#tail').addClass('active') + @tail().addClass('active') @run() stop: -> - $('#tail').removeClass('active') + @tail().removeClass('active') autoScroll: -> - return unless @active() - win = $(window) - log = $('#log') - logBottom = log.offset().top + log.outerHeight() + 40 - winBottom = win.scrollTop() + win.height() - win.scrollTop(logBottom - win.height()) if logBottom - winBottom > 0 + return false unless @active() + logBottom = @log().offset().top + @log().outerHeight() + 40 + winBottom = @window.scrollTop() + @window.height() + + if logBottom - winBottom > 0 + @window.scrollTop(logBottom - @window.height()) + true + else + false onScroll: -> @positionButton() - position = $(window).scrollTop() + position = @window.scrollTop() @stop() if position < @position @position = position positionButton: -> - tail = $('#tail') - return if tail.length is 0 - offset = $(window).scrollTop() - $('#log').offset().top - max = $('#log').height() - $('#tail').height() + 5 + return if @tail().length is 0 + offset = @window.scrollTop() - @log().offset().top + max = @log().height() - @tail().height() + 5 offset = max if offset > max - if offset > 0 - tail.css(position: 'fixed', right: 32) + @tail().addClass('scrolling') else - tail.css(position: 'absolute', right: 2) - + @tail().removeClass('scrolling') diff --git a/assets/scripts/spec/unit/tailing_spec.coffee b/assets/scripts/spec/unit/tailing_spec.coffee new file mode 100644 index 00000000..a3d59125 --- /dev/null +++ b/assets/scripts/spec/unit/tailing_spec.coffee @@ -0,0 +1,78 @@ +fakeWindow = + scroll: sinon.spy() + scrollTop: sinon.stub().returns(0) + height: sinon.stub().returns(40) +element = jQuery('
') +log = jQuery('
') +tail = new Travis.Tailing(fakeWindow, '#specTail', '#specLog') +tail.tail = -> element +tail.log = -> log + +module "Travis.Tailing", + setup: -> + jQuery('body').append(element) + jQuery('body').append(log) + + teardown: -> + element.remove() + log.remove() + tail.stop() + +test "toggle", -> + equal(element.hasClass('active'), false) + tail.toggle() + equal(element.hasClass('active'), true) + tail.toggle() + stop() + + Ember.run.later -> + start() + equal(element.hasClass('active'), false) + , 300 + +test "active", -> + equal(tail.active(), false) + element.addClass('active') + equal(tail.active(), true) + +test "autoscroll when inactive", -> + tail.scrollTo = sinon.spy() + + equal(tail.active(), false) + equal(tail.autoScroll(), false) + equal(tail.scrollTo.called, false) + +test "autoscroll", -> + element.addClass('active') + log.offset = -> {top: 1} + log.outerHeight = -> 1 + + equal(tail.active(), true) + equal(tail.autoScroll(), true) + equal(fakeWindow.scrollTop.calledWith(2), true) + +test "autoscroll when we're at the bottom", -> + element.addClass('active') + log.offset = -> {top: 0} + log.outerHeight = -> 0 + + equal(tail.active(), true) + equal(tail.autoScroll(), false) + equal(fakeWindow.scrollTop.calledWith(0), false) + +test 'should stop scrolling if the position changed', -> + element.addClass('active') + tail.position = 100 + tail.onScroll() + equal(element.hasClass('active'), false) + +test 'positionButton adds the scrolling class', -> + log.offset = -> {top: -1} + + tail.positionButton() + equal(element.hasClass('scrolling'), true) + +test 'positionButton removes the scrolling class', -> + log.offset = -> {top: 1} + tail.positionButton() + equal(element.hasClass('scrolling'), false) diff --git a/assets/styles/main/log.sass b/assets/styles/main/log.sass index 54e1067e..c88a8dcf 100644 --- a/assets/styles/main/log.sass +++ b/assets/styles/main/log.sass @@ -117,6 +117,10 @@ pre#log label display: inline + &.scrolling + position: fixed + right: 32px + .status display: inline-block margin-right: 1px From 1920785f507cc37bb2ae9607f20c76debc9d77e4 Mon Sep 17 00:00:00 2001 From: Damien Mathieu <42@dmathieu.com> Date: Fri, 13 Dec 2013 17:01:02 +0100 Subject: [PATCH 02/28] fix regression when going below the log zone Since a431b68c2858a2d96f35accfe577497344de98a2, when going below the max of the log files, the position button would remain in fixed position. This fixes it. --- assets/scripts/app/tailing.coffee | 9 +++++++-- assets/scripts/spec/unit/tailing_spec.coffee | 11 +++++++++++ assets/styles/main/log.sass | 4 ++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/assets/scripts/app/tailing.coffee b/assets/scripts/app/tailing.coffee index a9be33df..3df8c872 100644 --- a/assets/scripts/app/tailing.coffee +++ b/assets/scripts/app/tailing.coffee @@ -51,8 +51,13 @@ class @Travis.Tailing return if @tail().length is 0 offset = @window.scrollTop() - @log().offset().top max = @log().height() - @tail().height() + 5 - offset = max if offset > max - if offset > 0 + + if offset > 0 && offset <= max + @tail().removeClass('bottom') @tail().addClass('scrolling') else + if offset > max + @tail().addClass('bottom') + else + @tail().removeClass('bottom') @tail().removeClass('scrolling') diff --git a/assets/scripts/spec/unit/tailing_spec.coffee b/assets/scripts/spec/unit/tailing_spec.coffee index a3d59125..18a836f0 100644 --- a/assets/scripts/spec/unit/tailing_spec.coffee +++ b/assets/scripts/spec/unit/tailing_spec.coffee @@ -71,8 +71,19 @@ test 'positionButton adds the scrolling class', -> tail.positionButton() equal(element.hasClass('scrolling'), true) + equal(element.hasClass('bottom'), false) test 'positionButton removes the scrolling class', -> log.offset = -> {top: 1} tail.positionButton() equal(element.hasClass('scrolling'), false) + equal(element.hasClass('bottom'), false) + +test 'positionButton sets the button as bottom', -> + log.offset = -> {top: -100} + log.height = -> 50 + tail.height = -> 1 + + tail.positionButton() + equal(element.hasClass('scrolling'), false) + equal(element.hasClass('bottom'), true) diff --git a/assets/styles/main/log.sass b/assets/styles/main/log.sass index c88a8dcf..2e0ad5ff 100644 --- a/assets/styles/main/log.sass +++ b/assets/styles/main/log.sass @@ -121,6 +121,10 @@ pre#log position: fixed right: 32px + &.bottom + bottom: 45px + top: inherit + .status display: inline-block margin-right: 1px From 536866d7d6895bcc515387bed2b6aab65b9d4e25 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Thu, 19 Dec 2013 14:05:19 +0100 Subject: [PATCH 03/28] Get config headers only from jobs when preparing jobs table After changes in travis-core, irrelevant headers are removed when matrix is expanded. For example python version is removed from a ruby build. Build's config is not altered, so in order to get only effective keys, we need to iterate over jobs. --- assets/scripts/app/models/build.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/scripts/app/models/build.coffee b/assets/scripts/app/models/build.coffee index 779f7ad1..667daee9 100644 --- a/assets/scripts/app/models/build.coffee +++ b/assets/scripts/app/models/build.coffee @@ -45,7 +45,7 @@ require 'travis/model' ).property('jobs.@each.allowFailure') rawConfigKeys: (-> - keys = Travis.Helpers.configKeys(@get('config')) + keys = [] @get('jobs').forEach (job) -> Travis.Helpers.configKeys(job.get('config')).forEach (key) -> From 80510badffc9cb3bce21f42aa91481ecb23ac9e1 Mon Sep 17 00:00:00 2001 From: Hiro Asari Date: Tue, 31 Dec 2013 19:47:28 -0500 Subject: [PATCH 04/28] Remove `mailto:` URLs Addresses https://github.com/travis-ci/travis-ci/issues/1807 --- assets/scripts/app/templates/builds/show.hbs | 4 ++-- assets/scripts/app/templates/jobs/show.hbs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/scripts/app/templates/builds/show.hbs b/assets/scripts/app/templates/builds/show.hbs index bf8e5354..5096dd60 100644 --- a/assets/scripts/app/templates/builds/show.hbs +++ b/assets/scripts/app/templates/builds/show.hbs @@ -35,11 +35,11 @@ {{/if}} {{#if commit.authorName}}
{{t builds.author}}
-
{{commit.authorName}}
+
{{commit.authorName}}
{{/if}} {{#if commit.committerName}}
{{t builds.committer}}
-
{{commit.committerName}}
+
{{commit.committerName}}
{{/if}} {{/with}} diff --git a/assets/scripts/app/templates/jobs/show.hbs b/assets/scripts/app/templates/jobs/show.hbs index ebee6825..0d8fa914 100644 --- a/assets/scripts/app/templates/jobs/show.hbs +++ b/assets/scripts/app/templates/jobs/show.hbs @@ -34,11 +34,11 @@ {{/if}} {{#if commit.authorName}}
{{t jobs.author}}
-
{{commit.authorName}}
+
{{commit.authorName}}
{{/if}} {{#if commit.committerName}}
{{t jobs.committer}}
-
{{commit.committerName}}
+
{{commit.committerName}}
{{/if}} {{/with}} From 67db5f486b1ca46ca0a62d6e21d6b06f028466fe Mon Sep 17 00:00:00 2001 From: Hiro Asari Date: Tue, 31 Dec 2013 19:51:00 -0500 Subject: [PATCH 05/28] Remove Bundler patch The patch does not seem necessary any longer. --- Gemfile | 58 --------------------------------------------------------- 1 file changed, 58 deletions(-) diff --git a/Gemfile b/Gemfile index 2aa50f3a..a7922e0b 100644 --- a/Gemfile +++ b/Gemfile @@ -40,61 +40,3 @@ group :test do gem 'sinatra-contrib' end -require 'bundler/installer' - -module ::Bundler - class Installer < Environment - MAX_RETRIES = 3 - - def install_gem_from_spec(spec, standalone = false) - retries = 1 - # Download the gem to get the spec, because some specs that are returned - # by rubygems.org are broken and wrong. - Bundler::Fetcher.fetch(spec) if spec.source.is_a?(Bundler::Source::Rubygems) - - # Fetch the build settings, if there are any - settings = Bundler.settings["build.#{spec.name}"] - Bundler.rubygems.with_build_args [settings] do - spec.source.install(spec) - Bundler.ui.debug "from #{spec.loaded_from} " - end - - # newline comes after installing, some gems say "with native extensions" - Bundler.ui.info "" - if Bundler.settings[:bin] && standalone - generate_standalone_bundler_executable_stubs(spec) - elsif Bundler.settings[:bin] - generate_bundler_executable_stubs(spec, :force => true) - end - - FileUtils.rm_rf(Bundler.tmp) - rescue Gem::RemoteFetcher::FetchError => e - if retries <= MAX_RETRIES - Bundler.ui.warn "#{e.class}: #{e.message}" - Bundler.ui.warn "Installing #{spec.name} (#{spec.version}) failed." - Bundler.ui.warn "Retrying (#{retries}/#{MAX_RETRIES})" - retries += 1 - sleep retries - retry - else - Bundler.ui.warn "Installing #{spec.name} (#{spec.version}) failed after #{retries} retries: #{e.message}." - Bundler.ui.warn "Giving up" - msg = "An error, most likely because of network issues, has occurred trying to install #{spec.name} (#{spec.version}), " - msg << "and Bundler cannot continue." - raise Bundler::InstallError, msg - end - rescue Exception => e - # install hook failed - raise e if e.is_a?(Bundler::InstallHookError) || e.is_a?(Bundler::SecurityError) - - # other failure, likely a native extension build failure - Bundler.ui.info "" - Bundler.ui.warn "#{e.class}: #{e.message}" - msg = "An error occurred while installing #{spec.name} (#{spec.version})," - msg << " and Bundler cannot continue.\nMake sure that `gem install" - msg << " #{spec.name} -v '#{spec.version}'` succeeds before bundling." - Bundler.ui.debug e.backtrace.join("\n") - raise Bundler::InstallError, msg - end - end -end From 4d83e1a4cb9c75e3fd2bde23b519b35a65a8bfe2 Mon Sep 17 00:00:00 2001 From: Hiro Asari Date: Tue, 31 Dec 2013 23:21:10 -0500 Subject: [PATCH 06/28] Remove urlAuthor and urlCommitter fields No longer in views, so no need to keep these. --- assets/scripts/app/controllers/build.coffee | 8 -------- assets/scripts/app/controllers/job.coffee | 8 -------- assets/scripts/app/views/job.coffee | 8 -------- 3 files changed, 24 deletions(-) diff --git a/assets/scripts/app/controllers/build.coffee b/assets/scripts/app/controllers/build.coffee index 2446117b..ba99f348 100644 --- a/assets/scripts/app/controllers/build.coffee +++ b/assets/scripts/app/controllers/build.coffee @@ -15,11 +15,3 @@ Travis.BuildController = Ember.Controller.extend urlGithubCommit: (-> Travis.Urls.githubCommit(@get('repo.slug'), @get('commit.sha')) ).property('repo.slug', 'commit.sha') - - urlAuthor: (-> - Travis.Urls.email(@get('commit.authorEmail')) - ).property('commit.authorEmail') - - urlCommitter: (-> - Travis.Urls.email(@get('commit.committerEmail')) - ).property('commit.committerEmail') diff --git a/assets/scripts/app/controllers/job.coffee b/assets/scripts/app/controllers/job.coffee index 417e3eb3..148dbad7 100644 --- a/assets/scripts/app/controllers/job.coffee +++ b/assets/scripts/app/controllers/job.coffee @@ -13,11 +13,3 @@ Travis.JobController = Em.Controller.extend urlGithubCommit: (-> Travis.Urls.githubCommit(@get('repo.slug'), @get('commit.sha')) ).property('repo.slug', 'commit.sha') - - urlAuthor: (-> - Travis.Urls.email(@get('commit.authorEmail')) - ).property('commit.authorEmail') - - urlCommitter: (-> - Travis.Urls.email(@get('commit.committerEmail')) - ).property('commit.committerEmail') diff --git a/assets/scripts/app/views/job.coffee b/assets/scripts/app/views/job.coffee index 9027ebf7..1edaf375 100644 --- a/assets/scripts/app/views/job.coffee +++ b/assets/scripts/app/views/job.coffee @@ -29,11 +29,3 @@ Travis.reopen urlGithubCommit: (-> Travis.Urls.githubCommit(@get('repo.slug'), @get('commit.sha')) ).property('repo.slug', 'commit.sha') - - urlAuthor: (-> - Travis.Urls.email(@get('commit.authorEmail')) - ).property('commit.authorEmail') - - urlCommitter: (-> - Travis.Urls.email(@get('commit.committerEmail')) - ).property('commit.committerEmail') From d5699386879eb2f1d39f47611c303805a34ace1f Mon Sep 17 00:00:00 2001 From: Mathias Meyer Date: Thu, 2 Jan 2014 12:26:20 +0100 Subject: [PATCH 07/28] Fix typo in Code Climate popup. --- assets/scripts/app/templates/repos/show/tools.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/scripts/app/templates/repos/show/tools.hbs b/assets/scripts/app/templates/repos/show/tools.hbs index b2541b3c..f6368253 100644 --- a/assets/scripts/app/templates/repos/show/tools.hbs +++ b/assets/scripts/app/templates/repos/show/tools.hbs @@ -59,7 +59,7 @@ Integrating Code Climate's test coverage reporting with your test suite on Travis CI allows to track changes in coverage over time. If you haven't tried it out already, sign - up today for to improve your code's quality. New customers get 20% off for the first three months! + up today to improve your code's quality. New customers get 20% off for the first three months!

From c80a4e4160fabc27802c879228b380c6210912cf Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Tue, 7 Jan 2014 21:15:19 +0100 Subject: [PATCH 08/28] No need to abort transition: rejecting will abort automatically --- assets/scripts/app/routes.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/assets/scripts/app/routes.coffee b/assets/scripts/app/routes.coffee index 0f8186c1..14cfd682 100644 --- a/assets/scripts/app/routes.coffee +++ b/assets/scripts/app/routes.coffee @@ -59,7 +59,6 @@ Ember.Route.reopen if !@signedIn() && @get('needsAuth') Travis.auth.set('afterSignInTransition', transition) - transition.abort() Ember.RSVP.reject("needs-auth") else @_super.apply(this, arguments) From c9e0f07a5a4a2f9512872ba416d5f583a007793e Mon Sep 17 00:00:00 2001 From: Damien Mathieu <42@dmathieu.com> Date: Tue, 7 Jan 2014 11:18:15 +0100 Subject: [PATCH 09/28] highlight multiple lines We can now highlight multiple lines, adding #L1-L2 to the hash. We can select the ending line with shift + click. Closes travis-ci/travis-ci#1829 --- assets/scripts/app/controllers/build.coffee | 2 +- assets/scripts/app/controllers/job.coffee | 2 +- assets/scripts/app/controllers/repo.coffee | 10 +++ assets/scripts/app/routes.coffee | 5 +- assets/scripts/app/views/log.coffee | 64 ++++++++++++------- .../lib/travis/line_number_parser.coffee | 15 ++++- .../spec/unit/line_number_parser_spec.coffee | 12 ++++ assets/styles/_mixins/colors.sass | 2 + assets/styles/main/log.sass | 2 + 9 files changed, 86 insertions(+), 28 deletions(-) create mode 100644 assets/scripts/spec/unit/line_number_parser_spec.coffee diff --git a/assets/scripts/app/controllers/build.coffee b/assets/scripts/app/controllers/build.coffee index ba99f348..1efa56bd 100644 --- a/assets/scripts/app/controllers/build.coffee +++ b/assets/scripts/app/controllers/build.coffee @@ -2,7 +2,7 @@ Travis.BuildController = Ember.Controller.extend needs: ['repo'] repoBinding: 'controllers.repo.repo' commitBinding: 'build.commit' - lineNumberBinding: 'controllers.repo.lineNumber' + lineNumbersBinding: 'controllers.repo.lineNumbers' currentUserBinding: 'controllers.repo.currentUser' tabBinding: 'controllers.repo.tab' diff --git a/assets/scripts/app/controllers/job.coffee b/assets/scripts/app/controllers/job.coffee index 148dbad7..92e97862 100644 --- a/assets/scripts/app/controllers/job.coffee +++ b/assets/scripts/app/controllers/job.coffee @@ -4,7 +4,7 @@ Travis.JobController = Em.Controller.extend jobBinding: 'controllers.repo.job' repoBinding: 'controllers.repo.repo' commitBinding: 'job.commit' - lineNumberBinding: 'controllers.repo.lineNumber' + lineNumbersBinding: 'controllers.repo.lineNumbers' currentUserBinding: 'controllers.repo.currentUser' tabBinding: 'controllers.repo.tab' diff --git a/assets/scripts/app/controllers/repo.coffee b/assets/scripts/app/controllers/repo.coffee index 88276d71..cbd4fa34 100644 --- a/assets/scripts/app/controllers/repo.coffee +++ b/assets/scripts/app/controllers/repo.coffee @@ -72,3 +72,13 @@ Travis.RepoController = Travis.Controller.extend urlGithub: (-> Travis.Urls.githubRepo(@get('repo.slug')) ).property('repo.slug') + + setLineNumbers: (start, end) -> + lines = [] + index = start + + while index <= (end || start) + lines.push(index) + index++ + + @set('lineNumbers', lines) diff --git a/assets/scripts/app/routes.coffee b/assets/scripts/app/routes.coffee index 0f8186c1..6907527d 100644 --- a/assets/scripts/app/routes.coffee +++ b/assets/scripts/app/routes.coffee @@ -82,7 +82,7 @@ Ember.Route.reopen Travis.Router.reopen transitionTo: -> - this.container.lookup('controller:repo').set('lineNumber', null) + this.container.lookup('controller:repo').set('lineNumbers', []) @_super.apply this, arguments @@ -113,7 +113,8 @@ Travis.ApplicationRoute = Ember.Route.extend Travis.LineNumberParser, setupController: -> @_super.apply this, arguments - this.controllerFor('repo').set('lineNumber', @fetchLineNumber()) + line_numbers = @fetchLineNumbers(document.location.hash) + this.controllerFor('repo').setLineNumbers line_numbers[0], line_numbers[1] Travis.SetupLastBuild = Ember.Mixin.create setupController: -> diff --git a/assets/scripts/app/views/log.coffee b/assets/scripts/app/views/log.coffee index 2bacdfe2..f2216ad1 100644 --- a/assets/scripts/app/views/log.coffee +++ b/assets/scripts/app/views/log.coffee @@ -26,7 +26,7 @@ Travis.reopen console.log 'log view: did insert' if Log.DEBUG @_super.apply this, arguments @createEngine() - @lineNumberDidChange() + @lineNumbersDidChange() willDestroyElement: -> console.log 'log view: will destroy' if Log.DEBUG @@ -63,9 +63,9 @@ Travis.reopen @engine.set(part.number, part.content) @propertyDidChange('limited') - lineNumberDidChange: (-> - @scroll.set(number) if !@get('isDestroyed') && number = @get('controller.lineNumber') - ).observes('controller.lineNumber') + lineNumbersDidChange: (-> + @scroll.set(numbers) if !@get('isDestroyed') && numbers = @get('controller.lineNumbers') + ).observes('controller.lineNumbers') limited: (-> @engine?.limit?.limited @@ -80,22 +80,35 @@ Travis.reopen event.preventDefault() numberLineOnHover: -> - $('#log').on 'mouseenter', 'a', -> - $(@).attr('href', '#L' + ($("#log p:visible").index(@parentNode) + 1)) + $('#log').on 'mouseenter', 'a', (event) -> + hovered = $("#log p:visible").index(@parentNode) + 1 + selected = $("#log p:visible").index($('#log p.highlight')) + 1 + + if event.shiftKey + end = "-L#{hovered}" + start = selected + else + start = hovered + end = '' + + $(@).attr('href', "#L#{start}#{end}") click: (event) -> - if (href = $(event.target).attr('href')) && matches = href?.match(/#L(\d+)$/) - @lineNumberClicked(matches[1]) + if (href = $(event.target).attr('href')) && matches = href?.match(Travis.LineNumberRegex) + lines = [matches[1], matches[3]].sort (a, b) -> a - b + + @lineNumberClicked(lines[0], lines[1]) event.stopPropagation() false else target = $(event.target) target.closest('.fold').toggleClass('open') - lineNumberClicked: (number) -> - path = "#{window.location.pathname}#L#{number}" + lineNumberClicked: (start, end) -> + second_number = if end then "-L#{end}" else '' + path = "#{window.location.pathname}#L#{start}#{second_number}" window.history.pushState({ path: path }, null, path); - @set('controller.lineNumber', number) + @get('controller.controllers.repo').setLineNumbers(start, end) actions: toTop: () -> @@ -105,25 +118,32 @@ Travis.reopen Log.Scroll = -> Log.Scroll.prototype = $.extend new Log.Listener, - set: (number) -> - return unless number - @number = number + set: (numbers) -> + return unless numbers.length > 0 + @numbers = numbers @tryScroll() insert: (log, data, pos) -> - @tryScroll() if @number + @tryScroll() if @numbers true tryScroll: -> - if element = $("#log p:visible")[@number - 1] + @removeHighlights() + @scrollToFirstLine() + + first_line = @numbers[0] - 1 + last_line = @numbers[@numbers.length - 1] + + $('#log').find('p:visible').slice(first_line, last_line).addClass('highlight') + @numbers = undefined + + removeHighlights: -> + $('#log p.highlight').removeClass('highlight') + + scrollToFirstLine: -> + if element = $("#log p:visible")[@numbers[0] - 1] $('#main').scrollTop(0) $('html, body').scrollTop($(element).offset()?.top) # weird, html works in chrome, body in firefox - @highlight(element) - @number = undefined - - highlight: (element) -> - $('#log p.highlight').removeClass('highlight') - $(element).addClass('highlight') # Log.Logger = -> # Log.Logger.prototype = $.extend new Log.Listener, diff --git a/assets/scripts/lib/travis/line_number_parser.coffee b/assets/scripts/lib/travis/line_number_parser.coffee index 2abd54df..8cbc87e2 100644 --- a/assets/scripts/lib/travis/line_number_parser.coffee +++ b/assets/scripts/lib/travis/line_number_parser.coffee @@ -1,3 +1,14 @@ +Travis.LineNumberRegex = /#L(\d+)(-L(\d+))?$/ Travis.LineNumberParser = Ember.Mixin.create - fetchLineNumber: -> - match[1] if match = document.location.hash.match(/#L(\d+)$/) + + fetchLineNumbers: (hash) -> + if match = hash.match(Travis.LineNumberRegex) + start = match[1] + end = match[3] + + if end? + [start, end] + else + [start] + else + [] diff --git a/assets/scripts/spec/unit/line_number_parser_spec.coffee b/assets/scripts/spec/unit/line_number_parser_spec.coffee new file mode 100644 index 00000000..e55e15e1 --- /dev/null +++ b/assets/scripts/spec/unit/line_number_parser_spec.coffee @@ -0,0 +1,12 @@ +object = Ember.Object.extend(Travis.LineNumberParser) +subject = object.create() + +module "Travis.LineNumberParser", + test "without line numbers", -> + deepEqual subject.fetchLineNumbers(''), [] + + test "with a start date only", -> + deepEqual subject.fetchLineNumbers('#L5'), ['5'] + + test "with a start and end dates", -> + deepEqual subject.fetchLineNumbers('#L5-L6'), ['5', '6'] diff --git a/assets/styles/_mixins/colors.sass b/assets/styles/_mixins/colors.sass index e54bf4fa..ec6e5313 100644 --- a/assets/styles/_mixins/colors.sass +++ b/assets/styles/_mixins/colors.sass @@ -23,6 +23,7 @@ $yellow-light-2: #fffcf4 $gray-dark-1: #333 $gray-dark-2: #444 $gray-dark-3: #666 +$gray-dark-4: #777 $gray-medium-1: #999 $gray-medium-2: #aaa $gray-medium-3: #c4cbcc @@ -65,6 +66,7 @@ $color-bg-log: #222222 $color-bg-log-fold: $gray-dark-1 $color-bg-log-hover: $gray-dark-2 $color-bg-log-highlight: $gray-dark-3 +$color-bg-log-fold-highlight: $gray-dark-4 $color-bg-slider: $slate-blue-3 $color-bg-left: $gray-light-4 $color-bg-list-odd: $white diff --git a/assets/styles/main/log.sass b/assets/styles/main/log.sass index 2e0ad5ff..d5793a54 100644 --- a/assets/styles/main/log.sass +++ b/assets/styles/main/log.sass @@ -57,6 +57,8 @@ pre#log // &.active p:first-of-type background: $color-bg-log-fold inline-image('ui/log.fold.open.2.png') no-repeat 8px 3px + &.highlight + background-color: $color-bg-log-fold-highlight &:not(.open) p:first-of-type visibility: visible From 5260fac6dc15740ce5797bed39ceb0363fa7dbdc Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Tue, 7 Jan 2014 20:51:01 -0500 Subject: [PATCH 10/28] Update to Ember 1.3.0. --- assets/scripts/vendor/ember.js | 5570 +++++++++++++++++++-------- assets/scripts/vendor/handlebars.js | 4657 +++++++++++----------- 2 files changed, 6362 insertions(+), 3865 deletions(-) diff --git a/assets/scripts/vendor/ember.js b/assets/scripts/vendor/ember.js index 735ee4b1..2758331a 100644 --- a/assets/scripts/vendor/ember.js +++ b/assets/scripts/vendor/ember.js @@ -1,15 +1,14 @@ -// ========================================================================== -// Project: Ember - JavaScript Application Framework -// Copyright: Copyright 2011-2013 Tilde Inc. and contributors -// Portions Copyright 2006-2011 Strobe Inc. -// Portions Copyright 2008-2011 Apple Inc. All rights reserved. -// License: Licensed under MIT license -// See https://raw.github.com/emberjs/ember.js/master/LICENSE -// ========================================================================== +/*! + * @overview Ember - JavaScript Application Framework + * @copyright Copyright 2011-2014 Tilde Inc. and contributors + * Portions Copyright 2006-2011 Strobe Inc. + * Portions Copyright 2008-2011 Apple Inc. All rights reserved. + * @license Licensed under MIT license + * See https://raw.github.com/emberjs/ember.js/master/LICENSE + * @version 1.3.0 + */ - // Version: 1.2.0 - (function() { /*global __fail__*/ @@ -32,7 +31,20 @@ if ('undefined' === typeof Ember) { } } -Ember.ENV = 'undefined' === typeof ENV ? {} : ENV; +// This needs to be kept in sync with the logic in +// `packages/ember-metal/lib/core.js`. +// +// This is duplicated here to ensure that `Ember.ENV` +// is setup even if `Ember` is not loaded yet. +if (Ember.ENV) { + // do nothing if Ember.ENV is already setup +} else if ('undefined' !== typeof EmberENV) { + Ember.ENV = EmberENV; +} else if('undefined' !== typeof ENV) { + Ember.ENV = ENV; +} else { + Ember.ENV = {}; +} if (!('MANDATORY_SETTER' in Ember.ENV)) { Ember.ENV.MANDATORY_SETTER = true; // default to true for debug dist @@ -184,20 +196,19 @@ if (!Ember.testing) { })(); -// ========================================================================== -// Project: Ember - JavaScript Application Framework -// Copyright: Copyright 2011-2013 Tilde Inc. and contributors -// Portions Copyright 2006-2011 Strobe Inc. -// Portions Copyright 2008-2011 Apple Inc. All rights reserved. -// License: Licensed under MIT license -// See https://raw.github.com/emberjs/ember.js/master/LICENSE -// ========================================================================== +/*! + * @overview Ember - JavaScript Application Framework + * @copyright Copyright 2011-2014 Tilde Inc. and contributors + * Portions Copyright 2006-2011 Strobe Inc. + * Portions Copyright 2008-2011 Apple Inc. All rights reserved. + * @license Licensed under MIT license + * See https://raw.github.com/emberjs/ember.js/master/LICENSE + * @version 1.3.0 + */ - // Version: 1.2.0 - (function() { -var define, requireModule; +var define, requireModule, require, requirejs; (function() { var registry = {}, seen = {}; @@ -206,36 +217,52 @@ var define, requireModule; registry[name] = { deps: deps, callback: callback }; }; - requireModule = function(name) { + requirejs = require = requireModule = function(name) { + requirejs._eak_seen = registry; + if (seen[name]) { return seen[name]; } seen[name] = {}; - var mod, deps, callback, reified, exports; - - mod = registry[name]; - - if (!mod) { - throw new Error("Module '" + name + "' not found."); + if (!registry[name]) { + throw new Error("Could not find module " + name); } - deps = mod.deps; - callback = mod.callback; - reified = []; + var mod = registry[name], + deps = mod.deps, + callback = mod.callback, + reified = [], + exports; for (var i=0, l=deps.length; i 1) { watching[keyName]--; @@ -3975,10 +4042,6 @@ Ember.finishChains = function(obj) { (function() { -/** - @module ember-metal -*/ - })(); @@ -4047,21 +4110,19 @@ var metaFor = Ember.meta, // utils.js generateGuid = Ember.generateGuid, IS_PATH = /[\.\*]/; - // returns true if the passed path is just a keyName function isKeyName(path) { return path==='*' || !IS_PATH.test(path); } /** - @private - Starts watching a property on an object. Whenever the property changes, invokes `Ember.propertyWillChange` and `Ember.propertyDidChange`. This is the primitive used by observers and dependent keys; usually you will never call this method directly but instead use higher level methods like `Ember.addObserver()` + @private @method watch @for Ember @param obj @@ -4071,13 +4132,11 @@ Ember.watch = function(obj, _keyPath) { // can't watch length on Array - it is special... if (_keyPath === 'length' && typeOf(obj) === 'array') { return; } - - if (isKeyName(_keyPath)) { - watchKey(obj, _keyPath); - } else { - watchPath(obj, _keyPath); - } - + if (isKeyName(_keyPath)) { + watchKey(obj, _keyPath); + } else { + watchPath(obj, _keyPath); + } }; Ember.isWatching = function isWatching(obj, key) { @@ -4091,22 +4150,19 @@ Ember.unwatch = function(obj, _keyPath) { // can't watch length on Array - it is special... if (_keyPath === 'length' && typeOf(obj) === 'array') { return; } - - if (isKeyName(_keyPath)) { - unwatchKey(obj, _keyPath); - } else { - unwatchPath(obj, _keyPath); - } - + if (isKeyName(_keyPath)) { + unwatchKey(obj, _keyPath); + } else { + unwatchPath(obj, _keyPath); + } }; /** - @private - Call on an object when you first beget it from another object. This will setup any chained watchers on the object instance as needed. This method is safe to call multiple times. + @private @method rewatch @for Ember @param obj @@ -4384,11 +4440,11 @@ ComputedPropertyPrototype.cacheable = function(aFlag) { mode the computed property will not automatically cache the return value. ```javascript - MyApp.outsideService = Ember.Object.create({ + MyApp.outsideService = Ember.Object.extend({ value: function() { return OutsideService.getValue(); }.property().volatile() - }); + }).create(); ``` @method volatile @@ -4404,12 +4460,14 @@ ComputedPropertyPrototype.volatile = function() { mode the computed property will throw an error when set. ```javascript - MyApp.person = Ember.Object.create({ + MyApp.Person = Ember.Object.extend({ guid: function() { return 'guid-guid-guid'; }.property().readOnly() }); + MyApp.person = MyApp.Person.create(); + MyApp.person.set('guid', 'new-guid'); // will throw an exception ``` @@ -4427,7 +4485,7 @@ ComputedPropertyPrototype.readOnly = function(readOnly) { arguments containing key paths that this computed property depends on. ```javascript - MyApp.president = Ember.Object.create({ + MyApp.President = Ember.Object.extend({ fullName: Ember.computed(function() { return this.get('firstName') + ' ' + this.get('lastName'); @@ -4435,6 +4493,12 @@ ComputedPropertyPrototype.readOnly = function(readOnly) { // and lastName }).property('firstName', 'lastName') }); + + MyApp.president = MyApp.President.create({ + firstName: 'Barack', + lastName: 'Obama', + }); + MyApp.president.get('fullName'); // Barack Obama ``` @method property @@ -4443,15 +4507,12 @@ ComputedPropertyPrototype.readOnly = function(readOnly) { @chainable */ ComputedPropertyPrototype.property = function() { - var addArg; + var args; - var args = []; - for (var i = 0, l = arguments.length; i < l; i++) { - - args.push(arguments[i]); - - } + args = a_slice.call(arguments); + + this._dependentKeys = args; return this; }; @@ -4765,11 +4826,15 @@ registerComputed('empty', function(dependentKey) { A computed property that returns true if the value of the dependent property is NOT null, an empty string, empty array, or empty function. + Note: When using `Ember.computed.notEmpty` to watch an array make sure to + use the `array.[]` syntax so the computed can subscribe to transitions + from empty to non-empty states. + Example ```javascript var Hamster = Ember.Object.extend({ - hasStuff: Ember.computed.notEmpty('backpack') + hasStuff: Ember.computed.notEmpty('backpack.[]') }); var hamster = Hamster.create({backpack: ['Food', 'Sleeping Bag', 'Tent']}); hamster.get('hasStuff'); // true @@ -5149,7 +5214,7 @@ registerComputedWithProperties('any', function(properties) { ```javascript var Hamster = Ember.Object.extend({ - clothes: Ember.computed.map('hat', 'shirt') + clothes: Ember.computed.collect('hat', 'shirt') }); var hamster = Hamster.create(); hamster.get('clothes'); // [null, null] @@ -5158,7 +5223,7 @@ registerComputedWithProperties('any', function(properties) { hamster.get('clothes'); // ['Camp Hat', 'Camp Shirt'] ``` - @method computed.map + @method computed.collect @for Ember @param {String} dependentKey* @return {Ember.ComputedProperty} computed property which maps @@ -5304,7 +5369,6 @@ Ember.computed.defaultTo = function(defaultPath) { var AFTER_OBSERVERS = ':change', BEFORE_OBSERVERS = ':before'; - function changeEvent(keyName) { return keyName+AFTER_OBSERVERS; } @@ -5321,10 +5385,8 @@ function beforeEvent(keyName) { @param {Function|String} [method] */ Ember.addObserver = function(obj, _path, target, method) { - - Ember.addListener(obj, changeEvent(_path), target, method); - Ember.watch(obj, _path); - + Ember.addListener(obj, changeEvent(_path), target, method); + Ember.watch(obj, _path); return this; }; @@ -5341,10 +5403,9 @@ Ember.observersFor = function(obj, path) { @param {Function|String} [method] */ Ember.removeObserver = function(obj, _path, target, method) { - - Ember.unwatch(obj, _path); - Ember.removeListener(obj, changeEvent(_path), target, method); - + Ember.unwatch(obj, _path); + Ember.removeListener(obj, changeEvent(_path), target, method); + return this; }; @@ -5356,10 +5417,9 @@ Ember.removeObserver = function(obj, _path, target, method) { @param {Function|String} [method] */ Ember.addBeforeObserver = function(obj, _path, target, method) { - - Ember.addListener(obj, beforeEvent(_path), target, method); - Ember.watch(obj, _path); - + Ember.addListener(obj, beforeEvent(_path), target, method); + Ember.watch(obj, _path); + return this; }; @@ -5399,10 +5459,9 @@ Ember.beforeObserversFor = function(obj, path) { @param {Function|String} [method] */ Ember.removeBeforeObserver = function(obj, _path, target, method) { - - Ember.unwatch(obj, _path); - Ember.removeListener(obj, beforeEvent(_path), target, method); - + Ember.unwatch(obj, _path); + Ember.removeListener(obj, beforeEvent(_path), target, method); + return this; }; @@ -5734,7 +5793,7 @@ define("backburner", method = target[method]; } - var stack = this.DEBUG ? new Error().stack : undefined, + var stack = this.DEBUG ? new Error() : undefined, args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; if (!this.currentInstance) { createAutorun(this); } return this.currentInstance.schedule(queueName, target, method, args, false, stack); @@ -5750,7 +5809,7 @@ define("backburner", method = target[method]; } - var stack = this.DEBUG ? new Error().stack : undefined, + var stack = this.DEBUG ? new Error() : undefined, args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; if (!this.currentInstance) { createAutorun(this); } return this.currentInstance.schedule(queueName, target, method, args, true, stack); @@ -5821,18 +5880,7 @@ define("backburner", timers.splice(i, 0, executeAt, fn); - if (laterTimer && laterTimerExpiresAt < executeAt) { return fn; } - - if (laterTimer) { - clearTimeout(laterTimer); - laterTimer = null; - } - laterTimer = global.setTimeout(function() { - executeTimers(self); - laterTimer = null; - laterTimerExpiresAt = null; - }, wait); - laterTimerExpiresAt = executeAt; + updateLaterTimer(self, executeAt, wait); return fn; }, @@ -5841,30 +5889,25 @@ define("backburner", var self = this, args = arguments, wait = parseInt(pop.call(args), 10), - throttler; + throttler, + index, + timer; - for (var i = 0, l = throttlers.length; i < l; i++) { - throttler = throttlers[i]; - if (throttler[0] === target && throttler[1] === method) { return; } // do nothing - } + index = findThrottler(target, method); + if (index > -1) { return throttlers[index]; } // throttled - var timer = global.setTimeout(function() { + timer = global.setTimeout(function() { self.run.apply(self, args); - // remove throttler - var index = -1; - for (var i = 0, l = throttlers.length; i < l; i++) { - throttler = throttlers[i]; - if (throttler[0] === target && throttler[1] === method) { - index = i; - break; - } - } - + var index = findThrottler(target, method); if (index > -1) { throttlers.splice(index, 1); } }, wait); - throttlers.push([target, method, timer]); + throttler = [target, method, timer]; + + throttlers.push(throttler); + + return throttler; }, debounce: function(target, method /* , args, wait, [immediate] */) { @@ -5873,7 +5916,8 @@ define("backburner", immediate = pop.call(args), wait, index, - debouncee; + debouncee, + timer; if (typeof immediate === "number" || typeof immediate === "string") { wait = immediate; @@ -5886,18 +5930,18 @@ define("backburner", // Remove debouncee index = findDebouncee(target, method); - if (index !== -1) { + if (index > -1) { debouncee = debouncees[index]; debouncees.splice(index, 1); clearTimeout(debouncee[2]); } - var timer = global.setTimeout(function() { + timer = global.setTimeout(function() { if (!immediate) { self.run.apply(self, args); } - index = findDebouncee(target, method); - if (index) { + var index = findDebouncee(target, method); + if (index > -1) { debouncees.splice(index, 1); } }, wait); @@ -5906,7 +5950,11 @@ define("backburner", self.run.apply(self, args); } - debouncees.push([target, method, timer]); + debouncee = [target, method, timer]; + + debouncees.push(debouncee); + + return debouncee; }, cancelTimers: function() { @@ -5939,19 +5987,47 @@ define("backburner", }, cancel: function(timer) { - if (timer && typeof timer === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce + var timerType = typeof timer; + + if (timer && timerType === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce return timer.queue.cancel(timer); - } else if (typeof timer === 'function') { // we're cancelling a setTimeout + } else if (timerType === 'function') { // we're cancelling a setTimeout for (var i = 0, l = timers.length; i < l; i += 2) { if (timers[i + 1] === timer) { timers.splice(i, 2); // remove the two elements return true; } } + } else if (window.toString.call(timer) === "[object Array]"){ // we're cancelling a throttle or debounce + return this._cancelItem(findThrottler, throttlers, timer) || + this._cancelItem(findDebouncee, debouncees, timer); } else { return; // timer was null or not a timer } + }, + + _cancelItem: function(findMethod, array, timer){ + var item, + index; + + if (timer.length < 3) { return false; } + + index = findMethod(timer[0], timer[1]); + + if(index > -1) { + + item = array[index]; + + if(item[2] === timer[2]){ + array.splice(index, 1); + clearTimeout(timer[2]); + return true; + } + } + + return false; } + }; Backburner.prototype.schedule = Backburner.prototype.defer; @@ -5966,6 +6042,20 @@ define("backburner", }); } + function updateLaterTimer(self, executeAt, wait) { + if (!laterTimer || executeAt < laterTimerExpiresAt) { + if (laterTimer) { + clearTimeout(laterTimer); + } + laterTimer = global.setTimeout(function() { + laterTimer = null; + laterTimerExpiresAt = null; + executeTimers(self); + }, wait); + laterTimerExpiresAt = executeAt; + } + } + function executeTimers(self) { var now = +new Date(), time, fns, i, l; @@ -5985,12 +6075,7 @@ define("backburner", }); if (timers.length) { - laterTimer = global.setTimeout(function() { - executeTimers(self); - laterTimer = null; - laterTimerExpiresAt = null; - }, timers[0] - now); - laterTimerExpiresAt = timers[0]; + updateLaterTimer(self, timers[0], timers[0] - now); } } @@ -6009,10 +6094,24 @@ define("backburner", return index; } + function findThrottler(target, method) { + var throttler, + index = -1; + + for (var i = 0, l = throttlers.length; i < l; i++) { + throttler = throttlers[i]; + if (throttler[0] === target && throttler[1] === method) { + index = i; + break; + } + } + + return index; + } + __exports__.Backburner = Backburner; }); - })(); @@ -6086,7 +6185,6 @@ Ember.run = function(target, method) { }; /** - If no run-loop is present, it creates a new one. If a run loop is present it will queue itself to run on the existing run-loops action queue. @@ -6120,7 +6218,7 @@ Ember.run = function(target, method) { May be a function or a string. If you pass a string then it will be looked up on the passed target. @param {Object} [args*] Any additional arguments you wish to pass to the method. - @return {Object} return value from invoking the passed function. Please note, + @return {Object} Return value from invoking the passed function. Please note, when called within an existing loop, no return value is possible. */ Ember.run.join = function(target, method) { @@ -6209,7 +6307,8 @@ Ember.run.end = function() { console.log("scheduled on actions queue"); }); - // Note the functions will be run in order based on the run queues order. Output would be: + // Note the functions will be run in order based on the run queues order. + // Output would be: // scheduled on sync queue // scheduled on actions queue ``` @@ -6366,7 +6465,8 @@ Ember.run.scheduleOnce = function(queue, target, method) { ```javascript Ember.run.next(myContext, function() { - // code to be executed in the next run loop, which will be scheduled after the current one + // code to be executed in the next run loop, + // which will be scheduled after the current one }); ``` @@ -7032,6 +7132,7 @@ var Mixin, REQUIRED, Alias, defineProperty = Ember.defineProperty, guidFor = Ember.guidFor; + function mixinsMeta(obj) { var m = Ember.meta(obj, true), ret = m.mixins; if (!ret) { @@ -7211,7 +7312,8 @@ function mergeMixins(mixins, m, descs, values, base, keys) { for(var i=0, l=mixins.length; i= 0.10.x - function useSetImmediate() { - return function(callback, arg) { - /* global setImmediate */ - setImmediate(function(){ - callback(arg); - }); - }; - } - - function useMutationObserver() { - var queue = []; - - var observer = new BrowserMutationObserver(function() { - var toProcess = queue.slice(); - queue = []; - - toProcess.forEach(function(tuple) { - var callback = tuple[0], arg= tuple[1]; - callback(arg); - }); - }); - - var element = document.createElement('div'); - observer.observe(element, { attributes: true }); - - // Chrome Memory Leak: https://bugs.webkit.org/show_bug.cgi?id=93661 - window.addEventListener('unload', function(){ - observer.disconnect(); - observer = null; - }, false); - - return function(callback, arg) { - queue.push([callback, arg]); - element.setAttribute('drainQueue', 'drainQueue'); - }; - } - - function useSetTimeout() { - return function(callback, arg) { - local.setTimeout(function() { - callback(arg); - }, 1); - }; - } - - if (typeof setImmediate === 'function') { - async = useSetImmediate(); - } else if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') { - async = useNextTick(); - } else if (BrowserMutationObserver) { - async = useMutationObserver(); - } else { - async = useSetTimeout(); - } - - - __exports__.async = async; - }); -define("rsvp/config", - ["rsvp/async","exports"], +define("rsvp/config", + ["./events","exports"], function(__dependency1__, __exports__) { "use strict"; - var async = __dependency1__.async; + var EventTarget = __dependency1__["default"]; - var config = {}; - config.async = async; + var config = { + instrument: false + }; + EventTarget.mixin(config); + + function configure(name, value) { + if (name === 'onerror') { + // handle for legacy users that expect the actual + // error to be passed to their function added via + // `RSVP.configure('onerror', someFunctionHere);` + config.on('error', value); + return; + } + + if (arguments.length === 2) { + config[name] = value; + } else { + return config[name]; + } + } __exports__.config = config; + __exports__.configure = configure; }); -define("rsvp/defer", - ["rsvp/promise","exports"], +define("rsvp/defer", + ["./promise","exports"], function(__dependency1__, __exports__) { "use strict"; - var Promise = __dependency1__.Promise; + var Promise = __dependency1__["default"]; - function defer() { - var deferred = { - // pre-allocate shape - resolve: undefined, - reject: undefined, - promise: undefined - }; + /** + `RSVP.defer` returns an object similar to jQuery's `$.Deferred`. + `RSVP.defer` should be used when porting over code reliant on `$.Deferred`'s + interface. New code should use the `RSVP.Promise` constructor instead. + + The object returned from `RSVP.defer` is a plain object with three properties: + + * promise - an `RSVP.Promise`. + * reject - a function that causes the `promise` property on this object to + become rejected + * resolve - a function that causes the `promise` property on this object to + become fulfilled. + + Example: + + ```javascript + var deferred = RSVP.defer(); + + deferred.resolve("Success!"); + + defered.promise.then(function(value){ + // value here is "Success!" + }); + ``` + + @method defer + @for RSVP + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Object} + */ + + __exports__["default"] = function defer(label) { + var deferred = { }; deferred.promise = new Promise(function(resolve, reject) { deferred.resolve = resolve; deferred.reject = reject; - }); + }, label); return deferred; - } - - - __exports__.defer = defer; + }; }); -define("rsvp/events", +define("rsvp/events", ["exports"], function(__exports__) { "use strict"; - var Event = function(type, options) { - this.type = type; - - for (var option in options) { - if (!options.hasOwnProperty(option)) { continue; } - - this[option] = options[option]; - } - }; - var indexOf = function(callbacks, callback) { for (var i=0, l=callbacks.length; i 1; + }; + + RSVP.filter(promises, filterFn).then(function(result){ + // result is [ 2, 3 ] + }); + ``` + + If any of the `promises` given to `RSVP.filter` are rejected, the first promise + that is rejected will be given as an argument to the returned promise's + rejection handler. For example: + + ```javascript + var promise1 = RSVP.resolve(1); + var promise2 = RSVP.reject(new Error("2")); + var promise3 = RSVP.reject(new Error("3")); + var promises = [ promise1, promise2, promise3 ]; + + var filterFn = function(item){ + return item > 1; + }; + + RSVP.filter(promises, filterFn).then(function(array){ + // Code here never runs because there are rejected promises! + }, function(reason) { + // reason.message === "2" + }); + ``` + + `RSVP.filter` will also wait for any promises returned from `filterFn`. + For instance, you may want to fetch a list of users then return a subset + of those users based on some asynchronous operation: + + ```javascript + + var alice = { name: 'alice' }; + var bob = { name: 'bob' }; + var users = [ alice, bob ]; + + var promises = users.map(function(user){ + return RSVP.resolve(user); + }); + + var filterFn = function(user){ + // Here, Alice has permissions to create a blog post, but Bob does not. + return getPrivilegesForUser(user).then(function(privs){ + return privs.can_create_blog_post === true; + }); + }; + RSVP.filter(promises, filterFn).then(function(users){ + // true, because the server told us only Alice can create a blog post. + users.length === 1; + // false, because Alice is the only user present in `users` + users[0] === bob; + }); + ``` + + @method filter + @for RSVP + @param {Array} promises + @param {Function} filterFn - function to be called on each resolved value to + filter the final results. + @param {String} label optional string describing the promise. Useful for + tooling. + @return {Promise} + */ + function filter(promises, filterFn, label) { + return all(promises, label).then(function(values){ + if (!isArray(promises)) { + throw new TypeError('You must pass an array to filter.'); + } + + if (!isFunction(filterFn)){ + throw new TypeError("You must pass a function to filter's second argument."); + } + + return map(promises, filterFn, label).then(function(filterResults){ + var i, + valuesLen = values.length, + filtered = []; + + for (i = 0; i < valuesLen; i++){ + if(filterResults[i]) filtered.push(values[i]); + } + return filtered; + }); + }); } - function hash(promises) { - var results = {}, deferred = defer(), remaining = size(promises); - - if (remaining === 0) { - deferred.resolve({}); - } - - var resolver = function(prop) { - return function(value) { - resolveAll(prop, value); - }; - }; - - var resolveAll = function(prop, value) { - results[prop] = value; - if (--remaining === 0) { - deferred.resolve(results); - } - }; - - var rejectAll = function(error) { - deferred.reject(error); - }; - - for (var prop in promises) { - if (promises[prop] && typeof promises[prop].then === 'function') { - promises[prop].then(resolver(prop), rejectAll); - } else { - resolveAll(prop, promises[prop]); - } - } - - return deferred.promise; - } - - - __exports__.hash = hash; + __exports__["default"] = filter; }); -define("rsvp/node", - ["rsvp/promise","rsvp/all","exports"], +define("rsvp/hash", + ["./promise","./utils","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; - var Promise = __dependency1__.Promise; - var all = __dependency2__.all; + var Promise = __dependency1__["default"]; + var isNonThenable = __dependency2__.isNonThenable; + var keysOf = __dependency2__.keysOf; + + /** + `RSVP.hash` is similar to `RSVP.all`, but takes an object instead of an array + for its `promises` argument. + + Returns a promise that is fulfilled when all the given promises have been + fulfilled, or rejected if any of them become rejected. The returned promise + is fulfilled with a hash that has the same key names as the `promises` object + argument. If any of the values in the object are not promises, they will + simply be copied over to the fulfilled object. + + Example: + + ```javascript + var promises = { + myPromise: RSVP.resolve(1), + yourPromise: RSVP.resolve(2), + theirPromise: RSVP.resolve(3), + notAPromise: 4 + }; + + RSVP.hash(promises).then(function(hash){ + // hash here is an object that looks like: + // { + // myPromise: 1, + // yourPromise: 2, + // theirPromise: 3, + // notAPromise: 4 + // } + }); + ```` + + If any of the `promises` given to `RSVP.hash` are rejected, the first promise + that is rejected will be given as the reason to the rejection handler. + + Example: + + ```javascript + var promises = { + myPromise: RSVP.resolve(1), + rejectedPromise: RSVP.reject(new Error("rejectedPromise")), + anotherRejectedPromise: RSVP.reject(new Error("anotherRejectedPromise")), + }; + + RSVP.hash(promises).then(function(hash){ + // Code here never runs because there are rejected promises! + }, function(reason) { + // reason.message === "rejectedPromise" + }); + ``` + + An important note: `RSVP.hash` is intended for plain JavaScript objects that + are just a set of keys and values. `RSVP.hash` will NOT preserve prototype + chains. + + Example: + + ```javascript + function MyConstructor(){ + this.example = RSVP.resolve("Example"); + } + + MyConstructor.prototype = { + protoProperty: RSVP.resolve("Proto Property") + }; + + var myObject = new MyConstructor(); + + RSVP.hash(myObject).then(function(hash){ + // protoProperty will not be present, instead you will just have an + // object that looks like: + // { + // example: "Example" + // } + // + // hash.hasOwnProperty('protoProperty'); // false + // 'undefined' === typeof hash.protoProperty + }); + ``` + + @method hash + @for RSVP + @param {Object} promises + @param {String} label optional string that describes the promise. + Useful for tooling. + @return {Promise} promise that is fulfilled when all properties of `promises` + have been fulfilled, or rejected if any of them become rejected. + @static + */ + __exports__["default"] = function hash(object, label) { + return new Promise(function(resolve, reject){ + var results = {}; + var keys = keysOf(object); + var remaining = keys.length; + var entry, property; + + if (remaining === 0) { + resolve(results); + return; + } + + function fulfilledTo(property) { + return function(value) { + results[property] = value; + if (--remaining === 0) { + resolve(results); + } + }; + } + + function onRejection(reason) { + remaining = 0; + reject(reason); + } + + for (var i = 0; i < keys.length; i++) { + property = keys[i]; + entry = object[property]; + + if (isNonThenable(entry)) { + results[property] = entry; + if (--remaining === 0) { + resolve(results); + } + } else { + Promise.cast(entry).then(fulfilledTo(property), onRejection); + } + } + }); + }; + }); +define("rsvp/instrument", + ["./config","./utils","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var config = __dependency1__.config; + var now = __dependency2__.now; + + __exports__["default"] = function instrument(eventName, promise, child) { + // instrumentation should not disrupt normal usage. + try { + config.trigger(eventName, { + guid: promise._guidKey + promise._id, + eventName: eventName, + detail: promise._detail, + childGuid: child && promise._guidKey + child._id, + label: promise._label, + timeStamp: now(), + stack: new Error(promise._label).stack + }); + } catch(error) { + setTimeout(function(){ + throw error; + }, 0); + } + }; + }); +define("rsvp/map", + ["./promise","./all","./utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var Promise = __dependency1__["default"]; + var all = __dependency2__["default"]; + var isArray = __dependency3__.isArray; + var isFunction = __dependency3__.isFunction; + + /** + `RSVP.map` is similar to JavaScript's native `map` method, except that it + waits for all promises to become fulfilled before running the `mapFn` on + each item in given to `promises`. `RSVP.map` returns a promise that will + become fulfilled with the result of running `mapFn` on the values the promises + become fulfilled with. + + For example: + + ```javascript + + var promise1 = RSVP.resolve(1); + var promise2 = RSVP.resolve(2); + var promise3 = RSVP.resolve(3); + var promises = [ promise1, promise2, promise3 ]; + + var mapFn = function(item){ + return item + 1; + }; + + RSVP.map(promises, mapFn).then(function(result){ + // result is [ 2, 3, 4 ] + }); + ``` + + If any of the `promises` given to `RSVP.map` are rejected, the first promise + that is rejected will be given as an argument to the returned promise's + rejection handler. For example: + + ```javascript + var promise1 = RSVP.resolve(1); + var promise2 = RSVP.reject(new Error("2")); + var promise3 = RSVP.reject(new Error("3")); + var promises = [ promise1, promise2, promise3 ]; + + var mapFn = function(item){ + return item + 1; + }; + + RSVP.map(promises, mapFn).then(function(array){ + // Code here never runs because there are rejected promises! + }, function(reason) { + // reason.message === "2" + }); + ``` + + `RSVP.map` will also wait if a promise is returned from `mapFn`. For example, + say you want to get all comments from a set of blog posts, but you need + the blog posts first becuase they contain a url to those comments. + + ```javscript + + var mapFn = function(blogPost){ + // getComments does some ajax and returns an RSVP.Promise that is fulfilled + // with some comments data + return getComments(blogPost.comments_url); + }; + + // getBlogPosts does some ajax and returns an RSVP.Promise that is fulfilled + // with some blog post data + RSVP.map(getBlogPosts(), mapFn).then(function(comments){ + // comments is the result of asking the server for the comments + // of all blog posts returned from getBlogPosts() + }); + ``` + + @method map + @for RSVP + @param {Array} promises + @param {Function} mapFn function to be called on each fulfilled promise. + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} promise that is fulfilled with the result of calling + `mapFn` on each fulfilled promise or value when they become fulfilled. + The promise will be rejected if any of the given `promises` become rejected. + @static + */ + __exports__["default"] = function map(promises, mapFn, label) { + return all(promises, label).then(function(results){ + if (!isArray(promises)) { + throw new TypeError('You must pass an array to map.'); + } + + if (!isFunction(mapFn)){ + throw new TypeError("You must pass a function to map's second argument."); + } + + + var resultLen = results.length, + mappedResults = [], + i; + + for (i = 0; i < resultLen; i++){ + mappedResults.push(mapFn(results[i])); + } + + return all(mappedResults, label); + }); + }; + }); +define("rsvp/node", + ["./promise","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Promise = __dependency1__["default"]; + + var slice = Array.prototype.slice; function makeNodeCallbackFor(resolve, reject) { return function (error, value) { if (error) { reject(error); } else if (arguments.length > 2) { - resolve(Array.prototype.slice.call(arguments, 1)); + resolve(slice.call(arguments, 1)); } else { resolve(value); } }; } - function denodeify(nodeFunc) { + /** + `RSVP.denodeify` takes a "node-style" function and returns a function that + will return an `RSVP.Promise`. You can use `denodeify` in Node.js or the + browser when you'd prefer to use promises over using callbacks. For example, + `denodeify` transforms the following: + + ```javascript + var fs = require('fs'); + + fs.readFile('myfile.txt', function(err, data){ + if (err) return handleError(err); + handleData(data); + }); + ``` + + into: + + ```javascript + var fs = require('fs'); + + var readFile = RSVP.denodeify(fs.readFile); + + readFile('myfile.txt').then(handleData, handleError); + ``` + + Using `denodeify` makes it easier to compose asynchronous operations instead + of using callbacks. For example, instead of: + + ```javascript + var fs = require('fs'); + var log = require('some-async-logger'); + + fs.readFile('myfile.txt', function(err, data){ + if (err) return handleError(err); + fs.writeFile('myfile2.txt', data, function(err){ + if (err) throw err; + log('success', function(err) { + if (err) throw err; + }); + }); + }); + ``` + + You can chain the operations together using `then` from the returned promise: + + ```javascript + var fs = require('fs'); + var denodeify = RSVP.denodeify; + var readFile = denodeify(fs.readFile); + var writeFile = denodeify(fs.writeFile); + var log = denodeify(require('some-async-logger')); + + readFile('myfile.txt').then(function(data){ + return writeFile('myfile2.txt', data); + }).then(function(){ + return log('SUCCESS'); + }).then(function(){ + // success handler + }, function(reason){ + // rejection handler + }); + ``` + + @method denodeify + @for RSVP + @param {Function} nodeFunc a "node-style" function that takes a callback as + its last argument. The callback expects an error to be passed as its first + argument (if an error occurred, otherwise null), and the value from the + operation as its second argument ("function(err, value){ }"). + @param {Any} binding optional argument for binding the "this" value when + calling the `nodeFunc` function. + @return {Function} a function that wraps `nodeFunc` to return an + `RSVP.Promise` + @static + */ + __exports__["default"] = function denodeify(nodeFunc, binding) { return function() { - var nodeArgs = Array.prototype.slice.call(arguments), resolve, reject; - var thisArg = this; + var nodeArgs = slice.call(arguments), resolve, reject; + var thisArg = this || binding; - var promise = new Promise(function(nodeResolve, nodeReject) { - resolve = nodeResolve; - reject = nodeReject; + return new Promise(function(resolve, reject) { + Promise.all(nodeArgs).then(function(nodeArgs) { + try { + nodeArgs.push(makeNodeCallbackFor(resolve, reject)); + nodeFunc.apply(thisArg, nodeArgs); + } catch(e) { + reject(e); + } + }); }); - - all(nodeArgs).then(function(nodeArgs) { - nodeArgs.push(makeNodeCallbackFor(resolve, reject)); - - try { - nodeFunc.apply(thisArg, nodeArgs); - } catch(e) { - reject(e); - } - }); - - return promise; }; - } - - - __exports__.denodeify = denodeify; + }; }); -define("rsvp/promise", - ["rsvp/config","rsvp/events","exports"], - function(__dependency1__, __dependency2__, __exports__) { +define("rsvp/promise", + ["./config","./events","./instrument","./utils","./promise/cast","./promise/all","./promise/race","./promise/resolve","./promise/reject","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __exports__) { "use strict"; var config = __dependency1__.config; - var EventTarget = __dependency2__.EventTarget; + var EventTarget = __dependency2__["default"]; + var instrument = __dependency3__["default"]; + var objectOrFunction = __dependency4__.objectOrFunction; + var isFunction = __dependency4__.isFunction; + var now = __dependency4__.now; + var cast = __dependency5__["default"]; + var all = __dependency6__["default"]; + var race = __dependency7__["default"]; + var Resolve = __dependency8__["default"]; + var Reject = __dependency9__["default"]; - function objectOrFunction(x) { - return isFunction(x) || (typeof x === "object" && x !== null); - } + var guidKey = 'rsvp_' + now() + '-'; + var counter = 0; - function isFunction(x){ - return typeof x === "function"; - } + function noop() {} - var Promise = function(resolver) { - var promise = this, - resolved = false; + __exports__["default"] = Promise; - if (typeof resolver !== 'function') { - throw new TypeError('You must pass a resolver function as the sole argument to the promise constructor'); + + /** + Promise objects represent the eventual result of an asynchronous operation. The + primary way of interacting with a promise is through its `then` method, which + registers callbacks to receive either a promise’s eventual value or the reason + why the promise cannot be fulfilled. + + Terminology + ----------- + + - `promise` is an object or function with a `then` method whose behavior conforms to this specification. + - `thenable` is an object or function that defines a `then` method. + - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). + - `exception` is a value that is thrown using the throw statement. + - `reason` is a value that indicates why a promise was rejected. + - `settled` the final resting state of a promise, fulfilled or rejected. + + A promise can be in one of three states: pending, fulfilled, or rejected. + + Promises that are fulfilled have a fulfillment value and are in the fulfilled + state. Promises that are rejected have a rejection reason and are in the + rejected state. A fulfillment value is never a thenable. Similarly, a + rejection reason is never a thenable. + + Promises can also be said to *resolve* a value. If this value is also a + promise, then the original promise's settled state will match the value's + settled state. So a promise that *resolves* a promise that rejects will + itself reject, and a promise that *resolves* a promise that fulfills will + itself fulfill. + + + Basic Usage: + ------------ + + ```js + var promise = new Promise(function(resolve, reject) { + // on success + resolve(value); + + // on failure + reject(reason); + }); + + promise.then(function(value) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Advanced Usage: + --------------- + + Promises shine when abstracting away asynchronous interactions such as + `XMLHttpRequest`s. + + ```js + function getJSON(url) { + return new Promise(function(resolve, reject){ + var xhr = new XMLHttpRequest(); + + xhr.open('GET', url); + xhr.onreadystatechange = handler; + xhr.responseType = 'json'; + xhr.setRequestHeader('Accept', 'application/json'); + xhr.send(); + + function handler() { + if (this.readyState === this.DONE) { + if (this.status === 200) { + resolve(this.response); + } else { + reject(new Error("getJSON: `" + url + "` failed with status: [" + this.status + "]"); + } + } + }; + }); } - if (!(promise instanceof Promise)) { - return new Promise(resolver); + getJSON('/posts.json').then(function(json) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Unlike callbacks, promises are great composable primitives. + + ```js + Promise.all([ + getJSON('/posts'), + getJSON('/comments') + ]).then(function(values){ + values[0] // => postsJSON + values[1] // => commentsJSON + + return values; + }); + ``` + + @class RSVP.Promise + @param {function} + @param {String} label optional string for labeling the promise. + Useful for tooling. + @constructor + */ + function Promise(resolver, label) { + if (!isFunction(resolver)) { + throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); } - var resolvePromise = function(value) { - if (resolved) { return; } - resolved = true; + if (!(this instanceof Promise)) { + throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); + } + + this._id = counter++; + this._label = label; + this._subscribers = []; + + if (config.instrument) { + instrument('created', this); + } + + if (noop !== resolver) { + invokeResolver(resolver, this); + } + } + + function invokeResolver(resolver, promise) { + function resolvePromise(value) { resolve(promise, value); - }; + } - var rejectPromise = function(value) { - if (resolved) { return; } - resolved = true; - reject(promise, value); - }; - - this.on('promise:resolved', function(event) { - this.trigger('success', { detail: event.detail }); - }, this); - - this.on('promise:failed', function(event) { - this.trigger('error', { detail: event.detail }); - }, this); - - this.on('error', onerror); + function rejectPromise(reason) { + reject(promise, reason); + } try { resolver(resolvePromise, rejectPromise); } catch(e) { rejectPromise(e); } - }; - - function onerror(event) { - if (config.onerror) { - config.onerror(event.detail); - } } - var invokeCallback = function(type, promise, callback, event) { + Promise.cast = cast; + Promise.all = all; + Promise.race = race; + Promise.resolve = Resolve; + Promise.reject = Reject; + + var PENDING = void 0; + var SEALED = 0; + var FULFILLED = 1; + var REJECTED = 2; + + function subscribe(parent, child, onFulfillment, onRejection) { + var subscribers = parent._subscribers; + var length = subscribers.length; + + subscribers[length] = child; + subscribers[length + FULFILLED] = onFulfillment; + subscribers[length + REJECTED] = onRejection; + } + + function publish(promise, settled) { + var child, callback, subscribers = promise._subscribers, detail = promise._detail; + + if (config.instrument) { + instrument(settled === FULFILLED ? 'fulfilled' : 'rejected', promise); + } + + for (var i = 0; i < subscribers.length; i += 3) { + child = subscribers[i]; + callback = subscribers[i + settled]; + + invokeCallback(settled, child, callback, detail); + } + + promise._subscribers = null; + } + + Promise.prototype = { + constructor: Promise, + + _id: undefined, + _guidKey: guidKey, + _label: undefined, + + _state: undefined, + _detail: undefined, + _subscribers: undefined, + + _onerror: function (reason) { + config.trigger('error', reason); + }, + + /** + The primary way of interacting with a promise is through its `then` method, + which registers callbacks to receive either a promise's eventual value or the + reason why the promise cannot be fulfilled. + + ```js + findUser().then(function(user){ + // user is available + }, function(reason){ + // user is unavailable, and you are given the reason why + }); + ``` + + Chaining + -------- + + The return value of `then` is itself a promise. This second, "downstream" + promise is resolved with the return value of the first promise's fulfillment + or rejection handler, or rejected if the handler throws an exception. + + ```js + findUser().then(function (user) { + return user.name; + }, function (reason) { + return "default name"; + }).then(function (userName) { + // If `findUser` fulfilled, `userName` will be the user's name, otherwise it + // will be `"default name"` + }); + + findUser().then(function (user) { + throw new Error("Found user, but still unhappy"); + }, function (reason) { + throw new Error("`findUser` rejected and we're unhappy"); + }).then(function (value) { + // never reached + }, function (reason) { + // if `findUser` fulfilled, `reason` will be "Found user, but still unhappy". + // If `findUser` rejected, `reason` will be "`findUser` rejected and we're unhappy". + }); + ``` + If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. + + ```js + findUser().then(function (user) { + throw new PedagogicalException("Upstream error"); + }).then(function (value) { + // never reached + }).then(function (value) { + // never reached + }, function (reason) { + // The `PedgagocialException` is propagated all the way down to here + }); + ``` + + Assimilation + ------------ + + Sometimes the value you want to propagate to a downstream promise can only be + retrieved asynchronously. This can be achieved by returning a promise in the + fulfillment or rejection handler. The downstream promise will then be pending + until the returned promise is settled. This is called *assimilation*. + + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // The user's comments are now available + }); + ``` + + If the assimliated promise rejects, then the downstream promise will also reject. + + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // If `findCommentsByAuthor` fulfills, we'll have the value here + }, function (reason) { + // If `findCommentsByAuthor` rejects, we'll have the reason here + }); + ``` + + Simple Example + -------------- + + Synchronous Example + + ```javascript + var result; + + try { + result = findResult(); + // success + } catch(reason) { + // failure + } + ``` + + Errback Example + + ```js + findResult(function(result, err){ + if (err) { + // failure + } else { + // success + } + }); + ``` + + Promise Example; + + ```javascript + findResult().then(function(result){ + // success + }, function(reason){ + // failure + }); + ``` + + Advanced Example + -------------- + + Synchronous Example + + ```javascript + var author, books; + + try { + author = findAuthor(); + books = findBooksByAuthor(author); + // success + } catch(reason) { + // failure + } + ``` + + Errback Example + + ```js + + function foundBooks(books) { + + } + + function failure(reason) { + + } + + findAuthor(function(author, err){ + if (err) { + failure(err); + // failure + } else { + try { + findBoooksByAuthor(author, function(books, err) { + if (err) { + failure(err); + } else { + try { + foundBooks(books); + } catch(reason) { + failure(reason); + } + } + }); + } catch(error) { + failure(err); + } + // success + } + }); + ``` + + Promise Example; + + ```javascript + findAuthor(). + then(findBooksByAuthor). + then(function(books){ + // found books + }).catch(function(reason){ + // something went wrong + }); + ``` + + @method then + @param {Function} onFulfilled + @param {Function} onRejected + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} + */ + then: function(onFulfillment, onRejection, label) { + var promise = this; + this._onerror = null; + + var thenPromise = new this.constructor(noop, label); + + if (this._state) { + var callbacks = arguments; + config.async(function invokePromiseCallback() { + invokeCallback(promise._state, thenPromise, callbacks[promise._state - 1], promise._detail); + }); + } else { + subscribe(this, thenPromise, onFulfillment, onRejection); + } + + if (config.instrument) { + instrument('chained', promise, thenPromise); + } + + return thenPromise; + }, + + /** + `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same + as the catch block of a try/catch statement. + + ```js + function findAuthor(){ + throw new Error("couldn't find that author"); + } + + // synchronous + try { + findAuthor(); + } catch(reason) { + // something went wrong + } + + // async with promises + findAuthor().catch(function(reason){ + // something went wrong + }); + ``` + + @method catch + @param {Function} onRejection + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} + */ + 'catch': function(onRejection, label) { + return this.then(null, onRejection, label); + }, + + /** + `finally` will be invoked regardless of the promise's fate just as native + try/catch/finally behaves + + Synchronous example: + + ```js + findAuthor() { + if (Math.random() > 0.5) { + throw new Error(); + } + return new Author(); + } + + try { + return findAuthor(); // succeed or fail + } catch(error) { + return findOtherAuther(); + } finally { + // always runs + // doesn't affect the return value + } + ``` + + Asynchronous example: + + ```js + findAuthor().catch(function(reason){ + return findOtherAuther(); + }).finally(function(){ + // author was either found, or not + }); + ``` + + @method finally + @param {Function} callback + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} + */ + 'finally': function(callback, label) { + var constructor = this.constructor; + + return this.then(function(value) { + return constructor.cast(callback()).then(function(){ + return value; + }); + }, function(reason) { + return constructor.cast(callback()).then(function(){ + throw reason; + }); + }, label); + } + }; + + function invokeCallback(settled, promise, callback, detail) { var hasCallback = isFunction(callback), value, error, succeeded, failed; if (hasCallback) { try { - value = callback(event.detail); + value = callback(detail); succeeded = true; } catch(e) { failed = true; error = e; } } else { - value = event.detail; + value = detail; succeeded = true; } @@ -8309,62 +9441,11 @@ define("rsvp/promise", resolve(promise, value); } else if (failed) { reject(promise, error); - } else if (type === 'resolve') { + } else if (settled === FULFILLED) { resolve(promise, value); - } else if (type === 'reject') { + } else if (settled === REJECTED) { reject(promise, value); } - }; - - Promise.prototype = { - constructor: Promise, - - isRejected: undefined, - isFulfilled: undefined, - rejectedReason: undefined, - fulfillmentValue: undefined, - - then: function(done, fail) { - this.off('error', onerror); - - var thenPromise = new this.constructor(function() {}); - - if (this.isFulfilled) { - config.async(function(promise) { - invokeCallback('resolve', thenPromise, done, { detail: promise.fulfillmentValue }); - }, this); - } - - if (this.isRejected) { - config.async(function(promise) { - invokeCallback('reject', thenPromise, fail, { detail: promise.rejectedReason }); - }, this); - } - - this.on('promise:resolved', function(event) { - invokeCallback('resolve', thenPromise, done, event); - }); - - this.on('promise:failed', function(event) { - invokeCallback('reject', thenPromise, fail, event); - }); - - return thenPromise; - }, - - fail: function(fail) { - return this.then(null, fail); - } - }; - - EventTarget.mixin(Promise.prototype); - - function resolve(promise, value) { - if (promise === value) { - fulfill(promise, value); - } else if (!handleThenable(promise, value)) { - fulfill(promise, value); - } } function handleThenable(promise, value) { @@ -8394,12 +9475,13 @@ define("rsvp/promise", resolved = true; reject(promise, val); - }); + }, 'derived from: ' + (promise._label || ' unknown promise')); return true; } } } catch (error) { + if (resolved) { return true; } reject(promise, error); return true; } @@ -8407,110 +9489,640 @@ define("rsvp/promise", return false; } + function resolve(promise, value) { + if (promise === value) { + fulfill(promise, value); + } else if (!handleThenable(promise, value)) { + fulfill(promise, value); + } + } + function fulfill(promise, value) { - config.async(function() { - promise.trigger('promise:resolved', { detail: value }); - promise.isFulfilled = true; - promise.fulfillmentValue = value; - }); + if (promise._state !== PENDING) { return; } + promise._state = SEALED; + promise._detail = value; + + config.async(publishFulfillment, promise); } - function reject(promise, value) { - config.async(function() { - promise.trigger('promise:failed', { detail: value }); - promise.isRejected = true; - promise.rejectedReason = value; - }); + function reject(promise, reason) { + if (promise._state !== PENDING) { return; } + promise._state = SEALED; + promise._detail = reason; + + config.async(publishRejection, promise); } + function publishFulfillment(promise) { + publish(promise, promise._state = FULFILLED); + } - __exports__.Promise = Promise; + function publishRejection(promise) { + if (promise._onerror) { + promise._onerror(promise._detail); + } + + publish(promise, promise._state = REJECTED); + } }); -define("rsvp/reject", - ["rsvp/promise","exports"], +define("rsvp/promise/all", + ["../utils","exports"], function(__dependency1__, __exports__) { "use strict"; - var Promise = __dependency1__.Promise; + var isArray = __dependency1__.isArray; + var isNonThenable = __dependency1__.isNonThenable; - function reject(reason) { - return new Promise(function (resolve, reject) { - reject(reason); + /** + `RSVP.Promise.all` accepts an array of promises, and returns a new promise which + is fulfilled with an array of fulfillment values for the passed promises, or + rejected with the reason of the first passed promise to be rejected. It casts all + elements of the passed iterable to promises as it runs this algorithm. + + Example: + + ```javascript + var promise1 = RSVP.resolve(1); + var promise2 = RSVP.resolve(2); + var promise3 = RSVP.resolve(3); + var promises = [ promise1, promise2, promise3 ]; + + RSVP.Promise.all(promises).then(function(array){ + // The array here would be [ 1, 2, 3 ]; }); - } + ``` + If any of the `promises` given to `RSVP.all` are rejected, the first promise + that is rejected will be given as an argument to the returned promises's + rejection handler. For example: - __exports__.reject = reject; - }); -define("rsvp/resolve", - ["rsvp/promise","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Promise = __dependency1__.Promise; + Example: - function resolve(thenable) { - return new Promise(function(resolve, reject) { - resolve(thenable); + ```javascript + var promise1 = RSVP.resolve(1); + var promise2 = RSVP.reject(new Error("2")); + var promise3 = RSVP.reject(new Error("3")); + var promises = [ promise1, promise2, promise3 ]; + + RSVP.Promise.all(promises).then(function(array){ + // Code here never runs because there are rejected promises! + }, function(error) { + // error.message === "2" }); - } + ``` + @method all + @for RSVP.Promise + @param {Array} entries array of promises + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} promise that is fulfilled when all `promises` have been + fulfilled, or rejected if any of them become rejected. + @static + */ + __exports__["default"] = function all(entries, label) { - __exports__.resolve = resolve; + /*jshint validthis:true */ + var Constructor = this; + + return new Constructor(function(resolve, reject) { + if (!isArray(entries)) { + throw new TypeError('You must pass an array to all.'); + } + + var remaining = entries.length; + var results = new Array(remaining); + var entry, pending = true; + + if (remaining === 0) { + resolve(results); + return; + } + + function fulfillmentAt(index) { + return function(value) { + results[index] = value; + if (--remaining === 0) { + resolve(results); + } + }; + } + + function onRejection(reason) { + remaining = 0; + reject(reason); + } + + for (var index = 0; index < entries.length; index++) { + entry = entries[index]; + if (isNonThenable(entry)) { + results[index] = entry; + if (--remaining === 0) { + resolve(results); + } + } else { + Constructor.cast(entry).then(fulfillmentAt(index), onRejection); + } + } + }, label); + }; }); -define("rsvp/rethrow", +define("rsvp/promise/cast", ["exports"], function(__exports__) { "use strict"; - var local = (typeof global === "undefined") ? this : global; + /** + `RSVP.Promise.cast` coerces its argument to a promise, or returns the + argument if it is already a promise which shares a constructor with the caster. - function rethrow(reason) { - local.setTimeout(function() { + Example: + + ```javascript + var promise = RSVP.Promise.resolve(1); + var casted = RSVP.Promise.cast(promise); + + console.log(promise === casted); // true + ``` + + In the case of a promise whose constructor does not match, it is assimilated. + The resulting promise will fulfill or reject based on the outcome of the + promise being casted. + + Example: + + ```javascript + var thennable = $.getJSON('/api/foo'); + var casted = RSVP.Promise.cast(thennable); + + console.log(thennable === casted); // false + console.log(casted instanceof RSVP.Promise) // true + + casted.then(function(data) { + // data is the value getJSON fulfills with + }); + ``` + + In the case of a non-promise, a promise which will fulfill with that value is + returned. + + Example: + + ```javascript + var value = 1; // could be a number, boolean, string, undefined... + var casted = RSVP.Promise.cast(value); + + console.log(value === casted); // false + console.log(casted instanceof RSVP.Promise) // true + + casted.then(function(val) { + val === value // => true + }); + ``` + + `RSVP.Promise.cast` is similar to `RSVP.Promise.resolve`, but `RSVP.Promise.cast` differs in the + following ways: + + * `RSVP.Promise.cast` serves as a memory-efficient way of getting a promise, when you + have something that could either be a promise or a value. RSVP.resolve + will have the same effect but will create a new promise wrapper if the + argument is a promise. + * `RSVP.Promise.cast` is a way of casting incoming thenables or promise subclasses to + promises of the exact class specified, so that the resulting object's `then` is + ensured to have the behavior of the constructor you are calling cast on (i.e., RSVP.Promise). + + @method cast + @param {Object} object to be casted + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} promise + @static + */ + + __exports__["default"] = function cast(object, label) { + /*jshint validthis:true */ + var Constructor = this; + + if (object && typeof object === 'object' && object.constructor === Constructor) { + return object; + } + + return new Constructor(function(resolve) { + resolve(object); + }, label); + }; + }); +define("rsvp/promise/race", + ["../utils","exports"], + function(__dependency1__, __exports__) { + "use strict"; + /* global toString */ + + var isArray = __dependency1__.isArray; + var isFunction = __dependency1__.isFunction; + var isNonThenable = __dependency1__.isNonThenable; + + /** + `RSVP.Promise.race` returns a new promise which is settled in the same way as the + first passed promise to settle. + + Example: + + ```javascript + var promise1 = new RSVP.Promise(function(resolve, reject){ + setTimeout(function(){ + resolve("promise 1"); + }, 200); + }); + + var promise2 = new RSVP.Promise(function(resolve, reject){ + setTimeout(function(){ + resolve("promise 2"); + }, 100); + }); + + RSVP.Promise.race([promise1, promise2]).then(function(result){ + // result === "promise 2" because it was resolved before promise1 + // was resolved. + }); + ``` + + `RSVP.Promise.race` is deterministic in that only the state of the first + settled promise matters. For example, even if other promises given to the + `promises` array argument are resolved, but the first settled promise has + become rejected before the other promises became fulfilled, the returned + promise will become rejected: + + ```javascript + var promise1 = new RSVP.Promise(function(resolve, reject){ + setTimeout(function(){ + resolve("promise 1"); + }, 200); + }); + + var promise2 = new RSVP.Promise(function(resolve, reject){ + setTimeout(function(){ + reject(new Error("promise 2")); + }, 100); + }); + + RSVP.Promise.race([promise1, promise2]).then(function(result){ + // Code here never runs + }, function(reason){ + // reason.message === "promise2" because promise 2 became rejected before + // promise 1 became fulfilled + }); + ``` + + An example real-world use case is implementing timeouts: + + ```javascript + RSVP.Promise.race([ajax('foo.json'), timeout(5000)]) + ``` + + @method race + @param {Array} promises array of promises to observe + @param {String} label optional string for describing the promise returned. + Useful for tooling. + @return {Promise} a promise which settles in the same way as the first passed + promise to settle. + @static + */ + __exports__["default"] = function race(entries, label) { + /*jshint validthis:true */ + var Constructor = this, entry; + + return new Constructor(function(resolve, reject) { + if (!isArray(entries)) { + throw new TypeError('You must pass an array to race.'); + } + + var pending = true; + + function onFulfillment(value) { if (pending) { pending = false; resolve(value); } } + function onRejection(reason) { if (pending) { pending = false; reject(reason); } } + + for (var i = 0; i < entries.length; i++) { + entry = entries[i]; + if (isNonThenable(entry)) { + pending = false; + resolve(entry); + return; + } else { + Constructor.cast(entry).then(onFulfillment, onRejection); + } + } + }, label); + }; + }); +define("rsvp/promise/reject", + ["exports"], + function(__exports__) { + "use strict"; + /** + `RSVP.Promise.reject` returns a promise rejected with the passed `reason`. + It is shorthand for the following: + + ```javascript + var promise = new RSVP.Promise(function(resolve, reject){ + reject(new Error('WHOOPS')); + }); + + promise.then(function(value){ + // Code here doesn't run because the promise is rejected! + }, function(reason){ + // reason.message === 'WHOOPS' + }); + ``` + + Instead of writing the above, your code now simply becomes the following: + + ```javascript + var promise = RSVP.Promise.reject(new Error('WHOOPS')); + + promise.then(function(value){ + // Code here doesn't run because the promise is rejected! + }, function(reason){ + // reason.message === 'WHOOPS' + }); + ``` + + @method reject + @param {Any} reason value that the returned promise will be rejected with. + @param {String} label optional string for identifying the returned promise. + Useful for tooling. + @return {Promise} a promise rejected with the given `reason`. + @static + */ + __exports__["default"] = function reject(reason, label) { + /*jshint validthis:true */ + var Constructor = this; + + return new Constructor(function (resolve, reject) { + reject(reason); + }, label); + }; + }); +define("rsvp/promise/resolve", + ["exports"], + function(__exports__) { + "use strict"; + /** + `RSVP.Promise.resolve` returns a promise that will become resolved with the + passed `value`. It is shorthand for the following: + + ```javascript + var promise = new RSVP.Promise(function(resolve, reject){ + resolve(1); + }); + + promise.then(function(value){ + // value === 1 + }); + ``` + + Instead of writing the above, your code now simply becomes the following: + + ```javascript + var promise = RSVP.Promise.resolve(1); + + promise.then(function(value){ + // value === 1 + }); + ``` + + @method resolve + @param {Any} value value that the returned promise will be resolved with + @param {String} label optional string for identifying the returned promise. + Useful for tooling. + @return {Promise} a promise that will become fulfilled with the given + `value` + @static + */ + __exports__["default"] = function resolve(value, label) { + /*jshint validthis:true */ + var Constructor = this; + + return new Constructor(function(resolve, reject) { + resolve(value); + }, label); + }; + }); +define("rsvp/race", + ["./promise","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Promise = __dependency1__["default"]; + + /** + This is a convenient alias for `RSVP.Promise.race`. + + @method race + @param {Array} array Array of promises. + @param {String} label An optional label. This is useful + for tooling. + @static + */ + __exports__["default"] = function race(array, label) { + return Promise.race(array, label); + }; + }); +define("rsvp/reject", + ["./promise","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Promise = __dependency1__["default"]; + + /** + This is a convenient alias for `RSVP.Promise.reject`. + + @method reject + @for RSVP + @param {Any} reason value that the returned promise will be rejected with. + @param {String} label optional string for identifying the returned promise. + Useful for tooling. + @return {Promise} a promise rejected with the given `reason`. + @static + */ + __exports__["default"] = function reject(reason, label) { + return Promise.reject(reason, label); + }; + }); +define("rsvp/resolve", + ["./promise","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var Promise = __dependency1__["default"]; + + /** + This is a convenient alias for `RSVP.Promise.resolve`. + + @method resolve + @for RSVP + @param {Any} value value that the returned promise will be resolved with + @param {String} label optional string for identifying the returned promise. + Useful for tooling. + @return {Promise} a promise that will become fulfilled with the given + `value` + @static + */ + __exports__["default"] = function resolve(value, label) { + return Promise.resolve(value, label); + }; + }); +define("rsvp/rethrow", + ["exports"], + function(__exports__) { + "use strict"; + /** + `RSVP.rethrow` will rethrow an error on the next turn of the JavaScript event + loop in order to aid debugging. + + Promises A+ specifies that any exceptions that occur with a promise must be + caught by the promises implementation and bubbled to the last handler. For + this reason, it is recommended that you always specify a second rejection + handler function to `then`. However, `RSVP.rethrow` will throw the exception + outside of the promise, so it bubbles up to your console if in the browser, + or domain/cause uncaught exception in Node. `rethrow` will also throw the + error again so the error can be handled by the promise per the spec. + + ```javascript + function throws(){ + throw new Error('Whoops!'); + } + + var promise = new RSVP.Promise(function(resolve, reject){ + throws(); + }); + + promise.catch(RSVP.rethrow).then(function(){ + // Code here doesn't run because the promise became rejected due to an + // error! + }, function (err){ + // handle the error here + }); + ``` + + The 'Whoops' error will be thrown on the next turn of the event loop + and you can watch for it in your console. You can also handle it using a + rejection handler given to `.then` or `.catch` on the returned promise. + + @method rethrow + @for RSVP + @param {Error} reason reason the promise became rejected. + @throws Error + @static + */ + __exports__["default"] = function rethrow(reason) { + setTimeout(function() { throw reason; }); throw reason; - } - - - __exports__.rethrow = rethrow; + }; }); -define("rsvp", - ["rsvp/events","rsvp/promise","rsvp/node","rsvp/all","rsvp/hash","rsvp/rethrow","rsvp/defer","rsvp/config","rsvp/resolve","rsvp/reject","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) { +define("rsvp/utils", + ["exports"], + function(__exports__) { "use strict"; - var EventTarget = __dependency1__.EventTarget; - var Promise = __dependency2__.Promise; - var denodeify = __dependency3__.denodeify; - var all = __dependency4__.all; - var hash = __dependency5__.hash; - var rethrow = __dependency6__.rethrow; - var defer = __dependency7__.defer; - var config = __dependency8__.config; - var resolve = __dependency9__.resolve; - var reject = __dependency10__.reject; - - function configure(name, value) { - config[name] = value; + function objectOrFunction(x) { + return typeof x === "function" || (typeof x === "object" && x !== null); } + __exports__.objectOrFunction = objectOrFunction;function isFunction(x) { + return typeof x === "function"; + } + + __exports__.isFunction = isFunction;function isNonThenable(x) { + return !objectOrFunction(x); + } + + __exports__.isNonThenable = isNonThenable;function isArray(x) { + return Object.prototype.toString.call(x) === "[object Array]"; + } + + __exports__.isArray = isArray;// Date.now is not available in browsers < IE9 + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now#Compatibility + var now = Date.now || function() { return new Date().getTime(); }; + __exports__.now = now; + var keysOf = Object.keys || function(object) { + var result = []; + + for (var prop in object) { + result.push(prop); + } + + return result; + }; + __exports__.keysOf = keysOf; + }); +define("rsvp", + ["./rsvp/promise","./rsvp/events","./rsvp/node","./rsvp/all","./rsvp/all_settled","./rsvp/race","./rsvp/hash","./rsvp/rethrow","./rsvp/defer","./rsvp/config","./rsvp/map","./rsvp/resolve","./rsvp/reject","./rsvp/filter","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __exports__) { + "use strict"; + var Promise = __dependency1__["default"]; + var EventTarget = __dependency2__["default"]; + var denodeify = __dependency3__["default"]; + var all = __dependency4__["default"]; + var allSettled = __dependency5__["default"]; + var race = __dependency6__["default"]; + var hash = __dependency7__["default"]; + var rethrow = __dependency8__["default"]; + var defer = __dependency9__["default"]; + var config = __dependency10__.config; + var configure = __dependency10__.configure; + var map = __dependency11__["default"]; + var resolve = __dependency12__["default"]; + var reject = __dependency13__["default"]; + var filter = __dependency14__["default"]; + + function async(callback, arg) { + config.async(callback, arg); + } + + function on() { + config.on.apply(config, arguments); + } + + function off() { + config.off.apply(config, arguments); + } + + // Set up instrumentation through `window.__PROMISE_INTRUMENTATION__` + if (typeof window !== 'undefined' && typeof window.__PROMISE_INSTRUMENTATION__ === 'object') { + var callbacks = window.__PROMISE_INSTRUMENTATION__; + configure('instrument', true); + for (var eventName in callbacks) { + if (callbacks.hasOwnProperty(eventName)) { + on(eventName, callbacks[eventName]); + } + } + } __exports__.Promise = Promise; __exports__.EventTarget = EventTarget; __exports__.all = all; + __exports__.allSettled = allSettled; + __exports__.race = race; __exports__.hash = hash; __exports__.rethrow = rethrow; __exports__.defer = defer; __exports__.denodeify = denodeify; __exports__.configure = configure; + __exports__.on = on; + __exports__.off = off; __exports__.resolve = resolve; __exports__.reject = reject; + __exports__.async = async; + __exports__.map = map; + __exports__.filter = filter; }); + })(); (function() { /** -@private Public api for the container is still in flux. The public api, specified on the application namespace should be considered the stable api. // @module container + @private */ /* @@ -8555,6 +10167,7 @@ define("container", no matching key is found, return undefined. @method get + @param {String} key @return {any} */ get: function(key) { @@ -8852,6 +10465,8 @@ define("container", to find the `fullName`. @method describe + @param {String} fullName + @return {string} described fullName */ describe: function(fullName) { return fullName; @@ -8895,7 +10510,7 @@ define("container", twitter instanceof Twitter; // => true // by default the container will return singletons - twitter2 = container.lookup('api:twitter'); + var twitter2 = container.lookup('api:twitter'); twitter instanceof Twitter; // => true twitter === twitter2; //=> true @@ -8962,7 +10577,7 @@ define("container", return true; } - return !!factoryFor(this, fullName); + return !!this.resolve(fullName); }, /** @@ -9008,8 +10623,6 @@ define("container", }, /** - @private - Used only via `injection`. Provides a specialized form of injection, specifically enabling @@ -9038,6 +10651,7 @@ define("container", user.router === post.router; //=> true ``` + @private @method typeInjection @param {String} type @param {String} property @@ -9057,8 +10671,8 @@ define("container", Two forms of injections are possible: - * Injecting one fullName on another fullName - * Injecting one fullName on a type + * Injecting one fullName on another fullName + * Injecting one fullName on a type Example: @@ -9105,8 +10719,6 @@ define("container", /** - @private - Used only via `factoryInjection`. Provides a specialized form of injection, specifically enabling @@ -9119,7 +10731,6 @@ define("container", ```javascript var container = new Container(); - container.registerFactory('model:user', User); container.register('store:main', SomeStore); container.factoryTypeInjection('model', 'store', 'store:main'); @@ -9130,6 +10741,7 @@ define("container", UserFactory.store instanceof SomeStore; //=> true ``` + @private @method factoryTypeInjection @param {String} type @param {String} property @@ -9207,7 +10819,6 @@ define("container", @method destroy */ destroy: function() { - this.isDestroyed = true; for (var i=0, l=this.children.length; i= 0) { var baseValue = this[keyName]; @@ -10902,11 +12519,10 @@ CoreObject.PrototypeMixin = Mixin.create({ willDestroy: Ember.K, /** - @private - Invoked by the run loop to actually destroy the object. This is scheduled for execution by the `destroy` method. + @private @method _scheduledDestroy */ _scheduledDestroy: function() { @@ -10926,27 +12542,33 @@ CoreObject.PrototypeMixin = Mixin.create({ than Javascript's `toString` typically does, in a generic way for all Ember objects. - App.Person = Em.Object.extend() - person = App.Person.create() - person.toString() //=> "" + ```javascript + App.Person = Em.Object.extend() + person = App.Person.create() + person.toString() //=> "" + ``` If the object's class is not defined on an Ember namespace, it will indicate it is a subclass of the registered superclass: - Student = App.Person.extend() - student = Student.create() - student.toString() //=> "<(subclass of App.Person):ember1025>" + ```javascript + Student = App.Person.extend() + student = Student.create() + student.toString() //=> "<(subclass of App.Person):ember1025>" + ``` If the method `toStringExtension` is defined, its return value will be included in the output. - App.Teacher = App.Person.extend({ - toStringExtension: function() { - return this.get('fullName'); - } - }); - teacher = App.Teacher.create() - teacher.toString(); //=> "" + ```javascript + App.Teacher = App.Person.extend({ + toStringExtension: function() { + return this.get('fullName'); + } + }); + teacher = App.Teacher.create() + teacher.toString(); //=> "" + ``` @method toString @return {String} string representation @@ -11142,7 +12764,6 @@ var ClassMixin = Mixin.create({ }, /** - Augments a constructor's prototype with additional properties and functions: @@ -11185,7 +12806,6 @@ var ClassMixin = Mixin.create({ name: 'an object' }); - MyObject.reopenClass({ canBuild: false }); @@ -11653,7 +13273,7 @@ function contentPropertyDidChange(content, contentKey) { @namespace Ember @extends Ember.Object */ -Ember.ObjectProxy = Ember.Object.extend(/** @scope Ember.ObjectProxy.prototype */ { +Ember.ObjectProxy = Ember.Object.extend({ /** The object whose properties will be forwarded. @@ -11763,7 +13383,7 @@ function iter(key, value) { with an `Ember.Object` subclass, you should be sure to change the length property using `set().` - 2. If you must implement `nextObject().` See documentation. + 2. You must implement `nextObject().` See documentation. Once you have these two methods implement, apply the `Ember.Enumerable` mixin to your class and you will be able to enumerate the contents of your object @@ -12068,7 +13688,9 @@ Ember.Enumerable = Ember.Mixin.create({ The callback method you provide should have the following signature (all parameters are optional): - function(item, index, enumerable); + ```javascript + function(item, index, enumerable); + ``` - *item* is the current item in the iteration. - *index* is the current index in the iteration @@ -12263,29 +13885,35 @@ Ember.Enumerable = Ember.Mixin.create({ }, /** - Returns `true` if the passed property resolves to `true` for all items in - the enumerable. This method is often simpler/faster than using a callback. - @method everyBy @param {String} key the property to test @param {String} [value] optional value to test against. + @deprecated Use `isEvery` instead @return {Boolean} */ - everyBy: function(key, value) { - return this.every(iter.apply(this, arguments)); - }, + everyBy: Ember.aliasMethod('isEvery'), + + /** + @method everyProperty + @param {String} key the property to test + @param {String} [value] optional value to test against. + @deprecated Use `isEvery` instead + @return {Boolean} + */ + everyProperty: Ember.aliasMethod('isEvery'), /** Returns `true` if the passed property resolves to `true` for all items in the enumerable. This method is often simpler/faster than using a callback. - @method everyProperty + @method isEvery @param {String} key the property to test @param {String} [value] optional value to test against. @return {Boolean} - @deprecated Use `everyBy` instead */ - everyProperty: Ember.aliasMethod('everyBy'), + isEvery: function(key, value) { + return this.every(iter.apply(this, arguments)); + }, /** Returns `true` if the passed function returns true for any item in the @@ -12368,26 +13996,32 @@ Ember.Enumerable = Ember.Mixin.create({ Returns `true` if the passed property resolves to `true` for any item in the enumerable. This method is often simpler/faster than using a callback. - @method anyBy + @method isAny @param {String} key the property to test @param {String} [value] optional value to test against. @return {Boolean} `true` if the passed function returns `true` for any item */ - anyBy: function(key, value) { + isAny: function(key, value) { return this.any(iter.apply(this, arguments)); }, /** - Returns `true` if the passed property resolves to `true` for any item in - the enumerable. This method is often simpler/faster than using a callback. + @method anyBy + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Boolean} `true` if the passed function returns `true` for any item + @deprecated Use `isAny` instead + */ + anyBy: Ember.aliasMethod('isAny'), + /** @method someProperty @param {String} key the property to test @param {String} [value] optional value to test against. @return {Boolean} `true` if the passed function returns `true` for any item - @deprecated Use `anyBy` instead + @deprecated Use `isAny` instead */ - someProperty: Ember.aliasMethod('anyBy'), + someProperty: Ember.aliasMethod('isAny'), /** This will combine the values of the enumerator into a single value. It @@ -12675,38 +14309,33 @@ Ember.Enumerable = Ember.Mixin.create({ Ember.propertyDidChange(this, '[]'); return this ; - } + }, -}); + /** + Converts the enumerable into an array and sorts by the keys + specified in the argument. + You may provide multiple arguments to sort by multiple properties. - Ember.Enumerable.reopen({ - /** - Converts the enumerable into an array and sorts by the keys - specified in the argument. - - You may provide multiple arguments to sort by multiple properties. - - @method sortBy - @param {String} property name(s) to sort on - @return {Array} The sorted array. + @method sortBy + @param {String} property name(s) to sort on + @return {Array} The sorted array. */ - sortBy: function() { - var sortKeys = arguments; - return this.toArray().sort(function(a, b){ - for(var i = 0; i < sortKeys.length; i++) { - var key = sortKeys[i], - propA = get(a, key), - propB = get(b, key); - // return 1 or -1 else continue to the next sortKey - var compareValue = Ember.compare(propA, propB); - if (compareValue) { return compareValue; } - } - return 0; - }); - } - }); - + sortBy: function() { + var sortKeys = arguments; + return this.toArray().sort(function(a, b){ + for(var i = 0; i < sortKeys.length; i++) { + var key = sortKeys[i], + propA = get(a, key), + propB = get(b, key); + // return 1 or -1 else continue to the next sortKey + var compareValue = Ember.compare(propA, propB); + if (compareValue) { return compareValue; } + } + return 0; + }); + } +}); })(); @@ -12759,7 +14388,7 @@ var get = Ember.get, set = Ember.set, isNone = Ember.isNone, map = Ember.Enumera @uses Ember.Enumerable @since Ember 0.9.0 */ -Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.prototype */ { +Ember.Array = Ember.Mixin.create(Ember.Enumerable, { /** Your array must support the `length` property. Your replace methods should @@ -13159,14 +14788,14 @@ var e_get = Ember.get, // Here we explicitly don't allow `@each.foo`; it would require some special // testing, but there's no particular reason why it should be disallowed. eachPropertyPattern = /^(.*)\.@each\.(.*)/, - doubleEachPropertyPattern = /(.*\.@each){2,}/; + doubleEachPropertyPattern = /(.*\.@each){2,}/, + arrayBracketPattern = /\.\[\]$/; + function get(obj, key) { - - if (key === '@this') { - return obj; - } - + if (key === '@this') { + return obj; + } return e_get(obj, key); } @@ -13530,6 +15159,15 @@ function reset(cp, propertyName) { } } +function partiallyRecomputeFor(obj, dependentKey) { + if (arrayBracketPattern.test(dependentKey)) { + return false; + } + + var value = get(obj, dependentKey); + return Ember.Array.detect(value); +} + function ReduceComputedPropertyInstanceMeta(context, propertyName, initialValue) { this.context = context; this.propertyName = propertyName; @@ -13610,6 +15248,8 @@ function ReduceComputedProperty(options) { meta.dependentArraysObserver.suspendArrayObservers(function () { forEach(cp._dependentKeys, function (dependentKey) { + if (!partiallyRecomputeFor(this, dependentKey)) { return; } + var dependentArray = get(this, dependentKey), previousDependentArray = meta.dependentArrays[dependentKey]; @@ -13637,6 +15277,8 @@ function ReduceComputedProperty(options) { }, this); forEach(cp._dependentKeys, function(dependentKey) { + if (!partiallyRecomputeFor(this, dependentKey)) { return; } + var dependentArray = get(this, dependentKey); if (dependentArray) { addItems.call(this, dependentArray, callbacks, cp, propertyName, meta); @@ -13729,8 +15371,11 @@ ReduceComputedProperty.prototype.property = function () { throw new Ember.Error("Nested @each properties not supported: " + dependentKey); } else if (match = eachPropertyPattern.exec(dependentKey)) { dependentArrayKey = match[1]; - itemPropertyKey = match[2]; - cp.itemPropertyKey(dependentArrayKey, itemPropertyKey); + + + itemPropertyKey = match[2]; + cp.itemPropertyKey(dependentArrayKey, itemPropertyKey); + propertyArgs.add(dependentArrayKey); } else { propertyArgs.add(dependentKey); @@ -13738,6 +15383,7 @@ ReduceComputedProperty.prototype.property = function () { }); return ComputedProperty.prototype.property.apply(this, propertyArgs.toArray()); + }; /** @@ -13887,6 +15533,34 @@ ReduceComputedProperty.prototype.property = function () { }) ``` + Dependent keys whose values are not arrays are treated as regular + dependencies: when they change, the computed property is completely + recalculated. It is sometimes useful to have dependent arrays with similar + semantics. Dependent keys which end in `.[]` do not use "one at a time" + semantics. When an item is added or removed from such a dependency, the + computed property is completely recomputed. + + Example + + ```javascript + Ember.Object.extend({ + // When `string` is changed, `computed` is completely recomputed. + string: 'a string', + + // When an item is added to `array`, `addedItem` is called. + array: [], + + // When an item is added to `anotherArray`, `computed` is completely + // recomputed. + anotherArray: [], + + computed: Ember.reduceComputed('string', 'array', 'anotherArray.[]', { + addedItem: addedItemCallback, + removedItem: removedItemCallback + }) + }); + ``` + @method reduceComputed @for Ember @param {String} [dependentKeys*] @@ -13962,6 +15636,11 @@ ArrayComputedProperty.prototype.resetValue = function (array) { return array; }; +// This is a stopgap to keep the reference counts correct with lazy CPs. +ArrayComputedProperty.prototype.didChange = function (obj, keyName) { + return; +}; + /** Creates a computed property which operates on dependent arrays and is updated with "one at a time" semantics. When items are added or @@ -14121,8 +15800,6 @@ var get = Ember.get, dependent array. This will return `-Infinity` when the dependent array is empty. - Example - ```javascript App.Person = Ember.Object.extend({ childAges: Ember.computed.mapBy('children', 'age'), @@ -14131,9 +15808,17 @@ var get = Ember.get, var lordByron = App.Person.create({children: []}); lordByron.get('maxChildAge'); // -Infinity - lordByron.get('children').pushObject({name: 'Augusta Ada Byron', age: 7}); + lordByron.get('children').pushObject({ + name: 'Augusta Ada Byron', age: 7 + }); lordByron.get('maxChildAge'); // 7 - lordByron.get('children').pushObjects([{name: 'Allegra Byron', age: 5}, {name: 'Elizabeth Medora Leigh', age: 8}]); + lordByron.get('children').pushObjects([{ + name: 'Allegra Byron', + age: 5 + }, { + name: 'Elizabeth Medora Leigh', + age: 8 + }]); lordByron.get('maxChildAge'); // 8 ``` @@ -14163,8 +15848,6 @@ Ember.computed.max = function (dependentKey) { dependent array. This will return `Infinity` when the dependent array is empty. - Example - ```javascript App.Person = Ember.Object.extend({ childAges: Ember.computed.mapBy('children', 'age'), @@ -14173,9 +15856,17 @@ Ember.computed.max = function (dependentKey) { var lordByron = App.Person.create({children: []}); lordByron.get('minChildAge'); // Infinity - lordByron.get('children').pushObject({name: 'Augusta Ada Byron', age: 7}); + lordByron.get('children').pushObject({ + name: 'Augusta Ada Byron', age: 7 + }); lordByron.get('minChildAge'); // 7 - lordByron.get('children').pushObjects([{name: 'Allegra Byron', age: 5}, {name: 'Elizabeth Medora Leigh', age: 8}]); + lordByron.get('children').pushObjects([{ + name: 'Allegra Byron', + age: 5 + }, { + name: 'Elizabeth Medora Leigh', + age: 8 + }]); lordByron.get('minChildAge'); // 5 ``` @@ -14203,14 +15894,13 @@ Ember.computed.min = function (dependentKey) { /** Returns an array mapped via the callback - The callback method you provide should have the following signature: + The callback method you provide should have the following signature. + `item` is the current item in the iteration. ```javascript function(item); ``` - - `item` is the current item in the iteration. - Example ```javascript @@ -14220,8 +15910,10 @@ Ember.computed.min = function (dependentKey) { }) }); - var hamster = App.Hamster.create({chores: ['cook', 'clean', 'write more unit tests']}); - hamster.get('excitingChores'); // ['COOK!', 'CLEAN!', 'WRITE MORE UNIT TESTS!'] + var hamster = App.Hamster.create({ + chores: ['clean', 'write more unit tests'] + }); + hamster.get('excitingChores'); // ['CLEAN!', 'WRITE MORE UNIT TESTS!'] ``` @method computed.map @@ -14249,8 +15941,6 @@ Ember.computed.map = function(dependentKey, callback) { /** Returns an array mapped to the specified key. - Example - ```javascript App.Person = Ember.Object.extend({ childAges: Ember.computed.mapBy('children', 'age') @@ -14260,7 +15950,13 @@ Ember.computed.map = function(dependentKey, callback) { lordByron.get('childAges'); // [] lordByron.get('children').pushObject({name: 'Augusta Ada Byron', age: 7}); lordByron.get('childAges'); // [7] - lordByron.get('children').pushObjects([{name: 'Allegra Byron', age: 5}, {name: 'Elizabeth Medora Leigh', age: 8}]); + lordByron.get('children').pushObjects([{ + name: 'Allegra Byron', + age: 5 + }, { + name: 'Elizabeth Medora Leigh', + age: 8 + }]); lordByron.get('childAges'); // [7, 5, 8] ``` @@ -14287,16 +15983,13 @@ Ember.computed.mapProperty = Ember.computed.mapBy; /** Filters the array by the callback. - The callback method you provide should have the following signature: + The callback method you provide should have the following signature. + `item` is the current item in the iteration. ```javascript function(item); ``` - - `item` is the current item in the iteration. - - Example - ```javascript App.Hamster = Ember.Object.extend({ remainingChores: Ember.computed.filter('chores', function(chore) { @@ -14352,8 +16045,6 @@ Ember.computed.filter = function(dependentKey, callback) { /** Filters the array by the property and value - Example - ```javascript App.Hamster = Ember.Object.extend({ remainingChores: Ember.computed.filterBy('chores', 'done', false) @@ -14810,17 +16501,25 @@ Ember.computed.sort = function (itemsKey, sortDefinition) { (function() { -/** - Expose RSVP implementation - - Documentation can be found here: https://github.com/tildeio/rsvp.js/blob/master/README.md - - @class RSVP - @namespace Ember - @constructor -*/ Ember.RSVP = requireModule('rsvp'); +Ember.RSVP.onerrorDefault = function(error) { + if (error instanceof Error) { + if (Ember.testing) { + if (Ember.Test && Ember.Test.adapter) { + Ember.Test.adapter.exception(error); + } else { + throw error; + } + } else { + Ember.Logger.error(error.stack); + Ember.assert(error, false); + } + } +}; + +Ember.RSVP.on('error', Ember.RSVP.onerrorDefault); + })(); @@ -14833,6 +16532,7 @@ Ember.RSVP = requireModule('rsvp'); var a_slice = Array.prototype.slice; + if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) { /** @@ -14897,6 +16597,8 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) { */ Function.prototype.property = function() { var ret = Ember.computed(this); + // ComputedProperty.prototype.property expands properties; no need for us to + // do so here. return ret.property.apply(ret, arguments); }; @@ -14926,7 +16628,10 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) { @for Function */ Function.prototype.observes = function() { - this.__ember_observes__ = a_slice.call(arguments); + + this.__ember_observes__ = a_slice.call(arguments); + + return this; }; @@ -14961,6 +16666,7 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) { Ember.assert("Immediate observers must observe internal properties only, not properties on other objects.", arg.indexOf('.') === -1); } + // observes handles property expansion return this.observes.apply(this, arguments); }; @@ -14987,7 +16693,10 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) { @for Function */ Function.prototype.observesBefore = function() { - this.__ember_observesBefore__ = a_slice.call(arguments); + + this.__ember_observesBefore__ = a_slice.call(arguments); + + return this; }; @@ -15047,7 +16756,7 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) { @namespace Ember @since Ember 0.9 */ -Ember.Comparable = Ember.Mixin.create( /** @scope Ember.Comparable.prototype */{ +Ember.Comparable = Ember.Mixin.create({ /** Override to return the result of the comparison of the two parameters. The @@ -15098,7 +16807,7 @@ var get = Ember.get, set = Ember.set; @namespace Ember @since Ember 0.9 */ -Ember.Copyable = Ember.Mixin.create(/** @scope Ember.Copyable.prototype */ { +Ember.Copyable = Ember.Mixin.create({ /** Override to return a copy of the receiver. Default implementation raises @@ -15202,7 +16911,7 @@ var get = Ember.get, set = Ember.set; @namespace Ember @since Ember 0.9 */ -Ember.Freezable = Ember.Mixin.create(/** @scope Ember.Freezable.prototype */ { +Ember.Freezable = Ember.Mixin.create({ /** Set to `true` when the object is frozen. Use this property to detect @@ -15381,7 +17090,7 @@ var get = Ember.get, set = Ember.set; @uses Ember.Array @uses Ember.MutableEnumerable */ -Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable,/** @scope Ember.MutableArray.prototype */ { +Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable, { /** __Required.__ You must implement this method to apply this mixin. @@ -15447,7 +17156,7 @@ Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable,/** method. You can pass either a single index, or a start and a length. If you pass a start and length that is beyond the - length this method will throw an `OUT_OF_RANGE_EXCEPTION` + length this method will throw an `OUT_OF_RANGE_EXCEPTION`. ```javascript var colors = ["red", "green", "blue", "yellow", "orange"]; @@ -15481,18 +17190,18 @@ Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable,/** is KVO-compliant. ```javascript - var colors = ["red", "green", "blue"]; - colors.pushObject("black"); // ["red", "green", "blue", "black"] - colors.pushObject(["yellow", "orange"]); // ["red", "green", "blue", "black", ["yellow", "orange"]] + var colors = ["red", "green"]; + colors.pushObject("black"); // ["red", "green", "black"] + colors.pushObject(["yellow"]); // ["red", "green", ["yellow"]] ``` @method pushObject @param {*} obj object to push - @return {*} the same obj passed as param + @return The same obj passed as param */ pushObject: function(obj) { this.insertAt(get(this, 'length'), obj) ; - return obj ; + return obj; }, /** @@ -15500,9 +17209,8 @@ Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable,/** notifying observers of the change until all objects are added. ```javascript - var colors = ["red", "green", "blue"]; - colors.pushObjects(["black"]); // ["red", "green", "blue", "black"] - colors.pushObjects(["yellow", "orange"]); // ["red", "green", "blue", "black", "yellow", "orange"] + var colors = ["red"]; + colors.pushObjects(["yellow", "orange"]); // ["red", "yellow", "orange"] ``` @method pushObjects @@ -15564,14 +17272,14 @@ Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable,/** KVO-compliant. ```javascript - var colors = ["red", "green", "blue"]; - colors.unshiftObject("yellow"); // ["yellow", "red", "green", "blue"] - colors.unshiftObject(["black", "white"]); // [["black", "white"], "yellow", "red", "green", "blue"] + var colors = ["red"]; + colors.unshiftObject("yellow"); // ["yellow", "red"] + colors.unshiftObject(["black"]); // [["black"], "yellow", "red"] ``` @method unshiftObject @param {*} obj object to unshift - @return {*} the same obj passed as param + @return The same obj passed as param */ unshiftObject: function(obj) { this.insertAt(0, obj) ; @@ -15583,9 +17291,9 @@ Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable,/** observers until all objects have been added. ```javascript - var colors = ["red", "green", "blue"]; - colors.unshiftObjects(["black", "white"]); // ["black", "white", "red", "green", "blue"] - colors.unshiftObjects("yellow"); // Type Error: 'undefined' is not a function + var colors = ["red"]; + colors.unshiftObjects(["black", "white"]); // ["black", "white", "red"] + colors.unshiftObjects("yellow"); // Type Error: 'undefined' is not a function ``` @method unshiftObjects @@ -15958,6 +17666,11 @@ RSVP.configure('async', function(callback, promise) { Ember.run.schedule('actions', promise, callback, promise); }); +RSVP.Promise.prototype.fail = function(callback, label){ + Ember.deprecate('RSVP.Promise.fail has been renamed as RSVP.Promise.catch'); + return this['catch'](callback, label); +}; + /** @module ember @submodule ember-runtime @@ -15977,7 +17690,7 @@ Ember.DeferredMixin = Ember.Mixin.create({ @param {Function} resolve a callback function to be called when done @param {Function} reject a callback function to be called when failed */ - then: function(resolve, reject) { + then: function(resolve, reject, label) { var deferred, promise, entity; entity = this; @@ -15992,7 +17705,7 @@ Ember.DeferredMixin = Ember.Mixin.create({ } } - return promise.then(resolve && fulfillmentHandler, reject); + return promise.then(resolve && fulfillmentHandler, reject, label); }, /** @@ -16023,7 +17736,7 @@ Ember.DeferredMixin = Ember.Mixin.create({ }, _deferred: Ember.computed(function() { - return RSVP.defer(); + return RSVP.defer('Ember: DeferredMixin - ' + this); }) }); @@ -16055,12 +17768,131 @@ Ember.ActionHandler = Ember.Mixin.create({ mergedProperties: ['_actions'], /** - @private + The collection of functions, keyed by name, available on this + `ActionHandler` as action targets. + These functions will be invoked when a matching `{{action}}` is triggered + from within a template and the application's current route is this route. + + Actions can also be invoked from other parts of your application + via `ActionHandler#send`. + + The `actions` hash will inherit action handlers from + the `actions` hash defined on extended parent classes + or mixins rather than just replace the entire hash, e.g.: + + ```js + App.CanDisplayBanner = Ember.Mixin.create({ + actions: { + displayBanner: function(msg) { + // ... + } + } + }); + + App.WelcomeRoute = Ember.Route.extend(App.CanDisplayBanner, { + actions: { + playMusic: function() { + // ... + } + } + }); + + // `WelcomeRoute`, when active, will be able to respond + // to both actions, since the actions hash is merged rather + // then replaced when extending mixins / parent classes. + this.send('displayBanner'); + this.send('playMusic'); + ``` + + Within a Controller, Route, View or Component's action handler, + the value of the `this` context is the Controller, Route, View or + Component object: + + ```js + App.SongRoute = Ember.Route.extend({ + actions: { + myAction: function() { + this.controllerFor("song"); + this.transitionTo("other.route"); + ... + } + } + }); + ``` + + It is also possible to call `this._super()` from within an + action handler if it overrides a handler defined on a parent + class or mixin: + + Take for example the following routes: + + ```js + App.DebugRoute = Ember.Mixin.create({ + actions: { + debugRouteInformation: function() { + console.debug("trololo"); + } + } + }); + + App.AnnoyingDebugRoute = Ember.Route.extend(App.DebugRoute, { + actions: { + debugRouteInformation: function() { + // also call the debugRouteInformation of mixed in App.DebugRoute + this._super(); + + // show additional annoyance + window.alert(...); + } + } + }); + ``` + + ## Bubbling + + By default, an action will stop bubbling once a handler defined + on the `actions` hash handles it. To continue bubbling the action, + you must return `true` from the handler: + + ```js + App.Router.map(function() { + this.resource("album", function() { + this.route("song"); + }); + }); + + App.AlbumRoute = Ember.Route.extend({ + actions: { + startPlaying: function() { + } + } + }); + + App.AlbumSongRoute = Ember.Route.extend({ + actions: { + startPlaying: function() { + // ... + + if (actionShouldAlsoBeTriggeredOnParentRoute) { + return true; + } + } + } + }); + ``` + + @property actions + @type Hash + @default null + */ + + /** Moves `actions` to `_actions` at extend time. Note that this currently modifies the mixin themselves, which is technically dubious but is practically of little consequence. This may change in the future. + @private @method willMergeMixin */ willMergeMixin: function(props) { @@ -16131,7 +17963,7 @@ function observePromise(proxy, promise) { set(proxy, 'isRejected', true); set(proxy, 'reason', reason); // don't re-throw, as we are merely observing - }); + }, "Ember: PromiseProxy"); } /** @@ -16186,7 +18018,7 @@ function observePromise(proxy, promise) { controller.get('lastName') //=> 'Penner' ``` - If the controller is backing a template, the attributes are + If the controller is backing a template, the attributes are bindable from within that template ```handlebars @@ -16200,12 +18032,63 @@ function observePromise(proxy, promise) { @class Ember.PromiseProxyMixin */ Ember.PromiseProxyMixin = Ember.Mixin.create({ + /** + If the proxied promise is rejected this will contain the reason + provided. + + @property reason + @default null + */ reason: null, + + /** + Once the proxied promise has settled this will become `false`. + + @property isPending + @default true + */ isPending: not('isSettled').readOnly(), + + /** + Once the proxied promise has settled this will become `true`. + + @property isSettled + @default false + */ isSettled: or('isRejected', 'isFulfilled').readOnly(), + + /** + Will become `true` if the proxied promise is rejected. + + @property isRejected + @default false + */ isRejected: false, + + /** + Will become `true` if the proxied promise is fulfilled. + + @property isFullfilled + @default false + */ isFulfilled: false, + /** + The promise whose fulfillment value is being proxied by this object. + + This property must be specified upon creation, and should not be + changed once created. + + Example: + + ```javascript + Ember.ObjectController.extend(Ember.PromiseProxyMixin).create({ + promise: + }); + ``` + + @property promise + */ promise: Ember.computed(function(key, promise) { if (arguments.length === 2) { promise = resolve(promise); @@ -16216,12 +18099,47 @@ Ember.PromiseProxyMixin = Ember.Mixin.create({ } }), - then: function(fulfill, reject) { - return get(this, 'promise').then(fulfill, reject); - } + /** + An alias to the proxied promise's `then`. + + See RSVP.Promise.then. + + @method then + @param {Function} callback + @return {RSVP.Promise} + */ + then: promiseAlias('then'), + + /** + An alias to the proxied promise's `catch`. + + See RSVP.Promise.catch. + + @method catch + @param {Function} callback + @return {RSVP.Promise} + */ + 'catch': promiseAlias('catch'), + + /** + An alias to the proxied promise's `finally`. + + See RSVP.Promise.finally. + + @method finally + @param {Function} callback + @return {RSVP.Promise} + */ + 'finally': promiseAlias('finally') + }); - +function promiseAlias(name) { + return function () { + var promise = get(this, 'promise'); + return promise[name].apply(promise, arguments); + }; +} })(); @@ -16344,11 +18262,12 @@ Ember.TrackedArray.prototype = { items in the array. `callback` will be called for each operation and will be passed the following arguments: - * {array} items The items for the given operation - * {number} offset The computed offset of the items, ie the index in the - array of the first item for this operation. - * {string} operation The type of the operation. One of - `Ember.TrackedArray.{RETAIN, DELETE, INSERT}` + + * {array} items The items for the given operation + * {number} offset The computed offset of the items, ie the index in the + array of the first item for this operation. + * {string} operation The type of the operation. One of + `Ember.TrackedArray.{RETAIN, DELETE, INSERT}` @method apply @param {function} callback @@ -16370,7 +18289,7 @@ Ember.TrackedArray.prototype = { }, /** - Return an ArrayOperationMatch for the operation that contains the item at `index`. + Return an `ArrayOperationMatch` for the operation that contains the item at `index`. @method _findArrayOperation @@ -16532,10 +18451,10 @@ Ember.TrackedArray.prototype = { @method ArrayOperation @private - @property {string} type The type of the operation. One of + @param {string} type The type of the operation. One of `Ember.TrackedArray.{RETAIN, INSERT, DELETE}` - @property {number} count The number of items in this operation. - @property {array} items The items of the operation, if included. RETAIN and + @param {number} count The number of items in this operation. + @param {array} items The items of the operation, if included. RETAIN and INSERT include their items, DELETE does not. */ function ArrayOperation (operation, count, items) { @@ -16550,11 +18469,11 @@ function ArrayOperation (operation, count, items) { @method ArrayOperationMatch @private - @property {ArrayOperation} operation - @property {number} index The index of `operation` in the array of operations. - @property {boolean} split Whether or not the item index searched for would + @param {ArrayOperation} operation + @param {number} index The index of `operation` in the array of operations. + @param {boolean} split Whether or not the item index searched for would require a split for a new operation type. - @property {number} rangeStart The index of the first item in the operation, + @param {number} rangeStart The index of the first item in the operation, with respect to the tracked array. The index of the last item can be computed from `rangeStart` and `operation.count`. */ @@ -16815,7 +18734,7 @@ var get = Ember.get, set = Ember.set; @extends Ember.Object @uses Ember.MutableArray */ -Ember.ArrayProxy = Ember.Object.extend(Ember.MutableArray,/** @scope Ember.ArrayProxy.prototype */ { +Ember.ArrayProxy = Ember.Object.extend(Ember.MutableArray, { /** The content array. Must be an object that implements `Ember.Array` and/or @@ -16869,11 +18788,10 @@ Ember.ArrayProxy = Ember.Object.extend(Ember.MutableArray,/** @scope Ember.Array }, /** - @private - Invoked when the content property is about to change. Notifies observers that the entire array content will change. + @private @method _contentWillChange */ _contentWillChange: Ember.beforeObserver('content', function() { @@ -16895,11 +18813,10 @@ Ember.ArrayProxy = Ember.Object.extend(Ember.MutableArray,/** @scope Ember.Array contentArrayDidChange: Ember.K, /** - @private - Invoked when the content property changes. Notifies observers that the entire array content has changed. + @private @method _contentDidChange */ _contentDidChange: Ember.observer('content', function() { @@ -17598,7 +19515,7 @@ var get = Ember.get, set = Ember.set, guidFor = Ember.guidFor, isNone = Ember.is @since Ember 0.9 */ Ember.Set = Ember.CoreObject.extend(Ember.MutableEnumerable, Ember.Copyable, Ember.Freezable, - /** @scope Ember.Set.prototype */ { + { // .......................................................... // IMPLEMENT ENUMERABLE APIS @@ -17973,32 +19890,30 @@ Ember.Deferred = Deferred; var forEach = Ember.ArrayPolyfills.forEach; /** -@module ember -@submodule ember-runtime + @module ember + @submodule ember-runtime */ var loadHooks = Ember.ENV.EMBER_LOAD_HOOKS || {}; var loaded = {}; /** + Detects when a specific package of Ember (e.g. 'Ember.Handlebars') + has fully loaded and is available for extension. -Detects when a specific package of Ember (e.g. 'Ember.Handlebars') -has fully loaded and is available for extension. + The provided `callback` will be called with the `name` passed + resolved from a string into the object: -The provided `callback` will be called with the `name` passed -resolved from a string into the object: + ``` javascript + Ember.onLoad('Ember.Handlebars' function(hbars){ + hbars.registerHelper(...); + }); + ``` -```javascript -Ember.onLoad('Ember.Handlebars' function(hbars){ - hbars.registerHelper(...); -}); -``` - - -@method onLoad -@for Ember -@param name {String} name of hook -@param callback {Function} callback to be called + @method onLoad + @for Ember + @param name {String} name of hook + @param callback {Function} callback to be called */ Ember.onLoad = function(name, callback) { var object; @@ -18012,14 +19927,13 @@ Ember.onLoad = function(name, callback) { }; /** + Called when an Ember.js package (e.g Ember.Handlebars) has finished + loading. Triggers any callbacks registered for this event. -Called when an Ember.js package (e.g Ember.Handlebars) has finished -loading. Triggers any callbacks registered for this event. - -@method runLoadHooks -@for Ember -@param name {String} name of hook -@param object {Object} object to pass to callbacks + @method runLoadHooks + @for Ember + @param name {String} name of hook + @param object {Object} object to pass to callbacks */ Ember.runLoadHooks = function(name, object) { loaded[name] = object; @@ -18056,6 +19970,7 @@ var get = Ember.get; @class ControllerMixin @namespace Ember + @uses Ember.ActionHandler */ Ember.ControllerMixin = Ember.Mixin.create(Ember.ActionHandler, { /* ducktype as a controller */ @@ -18952,8 +20867,7 @@ Ember._RenderBuffer = function(tagName) { this.buffer = ""; }; -Ember._RenderBuffer.prototype = -/** @scope Ember.RenderBuffer.prototype */ { +Ember._RenderBuffer.prototype = { // The root view's element _element: null, @@ -18961,12 +20875,11 @@ Ember._RenderBuffer.prototype = _hasElement: true, /** - @private - An internal set used to de-dupe class names when `addClass()` is used. After each call to `addClass()`, the `classes` property will be updated. + @private @property elementClasses @type Array @default [] @@ -19411,7 +21324,7 @@ var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt; @private @extends Ember.Object */ -Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.prototype */{ +Ember.EventDispatcher = Ember.Object.extend({ /** The set of events names (and associated handler function names) to be setup @@ -19455,8 +21368,6 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro }, /** - @private - The root DOM element to which event listeners should be attached. Event listeners will be attached to the document unless this is overridden. @@ -19465,6 +21376,7 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro The default body is a string since this may be evaluated before document.body exists in the DOM. + @private @property rootElement @type DOMElement @default 'body' @@ -19472,8 +21384,6 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro rootElement: 'body', /** - @private - Sets up event listeners for standard browser events. This will be called after the browser sends a `DOMContentReady` event. By @@ -19481,6 +21391,7 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro would like to register the listeners on a different element, set the event dispatcher's `root` property. + @private @method setup @param addedEvents {Hash} */ @@ -19512,8 +21423,6 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro }, /** - @private - Registers an event listener on the document. If the given event is triggered, the provided event handler will be triggered on the target view. @@ -19528,6 +21437,7 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro setupHandler('mousedown', 'mouseDown'); ``` + @private @method setupHandler @param {Element} rootElement @param {String} event the browser-originated event to listen to @@ -19537,7 +21447,7 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro var self = this; rootElement.on(event + '.ember', '.ember-view', function(evt, triggeringManager) { - return Ember.handleErrors(function() { + return Ember.handleErrors(function handleViewEvent() { var view = Ember.View.views[this.id], result = true, manager = null; @@ -19556,7 +21466,7 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro }); rootElement.on(event + '.ember', '[data-ember-action]', function(evt) { - return Ember.handleErrors(function() { + return Ember.handleErrors(function handleActionEvent() { var actionId = Ember.$(evt.currentTarget).attr('data-ember-action'), action = Ember.Handlebars.ActionHelper.registeredActions[actionId]; @@ -19602,7 +21512,7 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro }, _bubbleEvent: function(view, evt, eventName) { - return Ember.run(function() { + return Ember.run(function bubbleEvent() { return view.handleEvent(eventName, evt); }); }, @@ -19751,6 +21661,7 @@ Ember.TEMPLATES = {}; @namespace Ember @extends Ember.Object @uses Ember.Evented + @uses Ember.ActionHandler */ Ember.CoreView = Ember.Object.extend(Ember.Evented, Ember.ActionHandler, { @@ -19798,8 +21709,6 @@ Ember.CoreView = Ember.Object.extend(Ember.Evented, Ember.ActionHandler, { }, /** - @private - Invoked by the view system when this view needs to produce an HTML representation. This method will create a new render buffer, if needed, then apply any default attributes, such as class names and visibility. @@ -19814,6 +21723,7 @@ Ember.CoreView = Ember.Object.extend(Ember.Evented, Ember.ActionHandler, { @param {Ember.RenderBuffer} buffer the render buffer. If no buffer is passed, a default buffer, using the current view's `tagName`, will be used. + @private */ renderToBuffer: function(parentBuffer, bufferOperation) { var name = 'render.' + this.instrumentName, @@ -19821,7 +21731,7 @@ Ember.CoreView = Ember.Object.extend(Ember.Evented, Ember.ActionHandler, { this.instrumentDetails(details); - return Ember.instrument(name, details, function() { + return Ember.instrument(name, details, function instrumentRenderToBuffer() { return this._renderToBuffer(parentBuffer, bufferOperation); }, this); }, @@ -19848,13 +21758,12 @@ Ember.CoreView = Ember.Object.extend(Ember.Evented, Ember.ActionHandler, { }, /** - @private - Override the default event firing from `Ember.Evented` to also call methods with the given name. @method trigger @param name {String} + @private */ trigger: function(name) { this._super.apply(this, arguments); @@ -20256,6 +22165,22 @@ var EMPTY_ARRAY = []; }); ``` + If you have nested resources, your Handlebars template will look like this: + + ```html + + ``` + + And `templateName` property: + + ```javascript + AView = Ember.View.extend({ + templateName: 'posts/new' + }); + ``` + Using a value for `templateName` that does not have a Handlebars template with a matching `data-template-name` attribute will throw an error. @@ -20524,8 +22449,7 @@ var EMPTY_ARRAY = []; @namespace Ember @extends Ember.CoreView */ -Ember.View = Ember.CoreView.extend( -/** @scope Ember.View.prototype */ { +Ember.View = Ember.CoreView.extend({ concatenatedProperties: ['classNames', 'classNameBindings', 'attributeBindings'], @@ -20533,7 +22457,7 @@ Ember.View = Ember.CoreView.extend( @property isView @type Boolean @default true - @final + @static */ isView: true, @@ -20658,8 +22582,6 @@ Ember.View = Ember.CoreView.extend( }).volatile(), /** - @private - Private copy of the view's template context. This can be set directly by Handlebars without triggering the observer that causes the view to be re-rendered. @@ -20675,6 +22597,7 @@ Ember.View = Ember.CoreView.extend( something of a hack and should be revisited. @property _context + @private */ _context: Ember.computed(function(key) { var parentView, controller; @@ -20692,12 +22615,11 @@ Ember.View = Ember.CoreView.extend( }), /** - @private - If a value that affects template rendering changes, the view should be re-rendered to reflect the new value. @method _contextDidChange + @private */ _contextDidChange: Ember.observer('context', function() { this.rerender(); @@ -20713,14 +22635,13 @@ Ember.View = Ember.CoreView.extend( isVisible: true, /** - @private - Array of child views. You should never edit this array directly. Instead, use `appendChild` and `removeFromParent`. @property childViews @type Array @default [] + @private */ childViews: childViewsProperty, @@ -20787,7 +22708,7 @@ Ember.View = Ember.CoreView.extend( /** Return the nearest ancestor that has a given property. - @property nearestWithProperty + @function nearestWithProperty @param {String} property A property name @return Ember.View */ @@ -20804,7 +22725,7 @@ Ember.View = Ember.CoreView.extend( Return the nearest ancestor whose parent is an instance of `klass`. - @property nearestChildOf + @method nearestChildOf @param {Class} klass Subclass of Ember.View (or Ember.View itself) @return Ember.View */ @@ -20818,11 +22739,10 @@ Ember.View = Ember.CoreView.extend( }, /** - @private - When the parent view changes, recursively invalidate `controller` @method _parentViewDidChange + @private */ _parentViewDidChange: Ember.observer('_parentView', function() { if (this.isDestroying) { return; } @@ -20937,14 +22857,13 @@ Ember.View = Ember.CoreView.extend( }, /** - @private - Iterates over the view's `classNameBindings` array, inserts the value of the specified property into the `classNames` array, then creates an observer to update the view's element if the bound property ever changes in the future. @method _applyClassNameBindings + @private */ _applyClassNameBindings: function(classBindings) { var classNames = this.classNames, @@ -21015,13 +22934,12 @@ Ember.View = Ember.CoreView.extend( }, /** - @private - Iterates through the view's attribute bindings, sets up observers for each, then applies the current value of the attributes to the passed render buffer. @method _applyAttributeBindings @param {Ember.RenderBuffer} buffer + @private */ _applyAttributeBindings: function(buffer, attributeBindings) { var attributeValue, elem; @@ -21051,8 +22969,6 @@ Ember.View = Ember.CoreView.extend( }, /** - @private - Given a property name, returns a dasherized version of that property name if the property evaluates to a non-falsy value. @@ -21061,6 +22977,7 @@ Ember.View = Ember.CoreView.extend( @method _classStringForProperty @param property + @private */ _classStringForProperty: function(property) { var parsedPath = Ember.View._parsePropertyPath(property); @@ -21179,7 +23096,7 @@ Ember.View = Ember.CoreView.extend( finished synchronizing @method replaceIn - @param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object + @param {String|DOMElement|jQuery} target A selector, element, HTML string, or jQuery object @return {Ember.View} received */ replaceIn: function(target) { @@ -21195,8 +23112,6 @@ Ember.View = Ember.CoreView.extend( }, /** - @private - Schedules a DOM operation to occur during the next render phase. This ensures that all bindings have finished synchronizing before the view is rendered. @@ -21216,6 +23131,7 @@ Ember.View = Ember.CoreView.extend( @method _insertElementLater @param {Function} fn the function that inserts the element into the DOM + @private */ _insertElementLater: function(fn) { this._scheduledInsert = Ember.run.scheduleOnce('render', this, '_insertElement', fn); @@ -21327,13 +23243,12 @@ Ember.View = Ember.CoreView.extend( willClearRender: Ember.K, /** - @private - Run this callback on the current view (unless includeSelf is false) and recursively on child views. @method invokeRecursively @param fn {Function} - @param includeSelf (optional, default true) + @param includeSelf {Boolean} Includes itself if true. + @private */ invokeRecursively: function(fn, includeSelf) { var childViews = (includeSelf === false) ? this._childViews : [this]; @@ -21418,8 +23333,6 @@ Ember.View = Ember.CoreView.extend( willDestroyElement: Ember.K, /** - @private - Triggers the `willDestroyElement` event (which invokes the `willDestroyElement()` method if it exists) on this view and all child views. @@ -21428,6 +23341,7 @@ Ember.View = Ember.CoreView.extend( `willClearRender` event recursively. @method _notifyWillDestroyElement + @private */ _notifyWillDestroyElement: function() { var viewCollection = this.viewHierarchyCollection(); @@ -21437,13 +23351,12 @@ Ember.View = Ember.CoreView.extend( }, /** - @private - If this view's element changes, we need to invalidate the caches of our child views so that we do not retain references to DOM elements that are no longer needed. @method _elementDidChange + @private */ _elementDidChange: Ember.observer('element', function() { this.forEachChildView(function(view) { @@ -21637,14 +23550,14 @@ Ember.View = Ember.CoreView.extend( // /** - @private - Setup a view, but do not finish waking it up. - - configure `childViews` - - register the view with the global views hash, which is used for event + + * configure `childViews` + * register the view with the global views hash, which is used for event dispatch @method init + @private */ init: function() { this.elementId = this.elementId || guidFor(this); @@ -21823,12 +23736,11 @@ Ember.View = Ember.CoreView.extend( becameHidden: Ember.K, /** - @private - When the view's `isVisible` property changes, toggle the visibility element of the actual DOM element. @method _isVisibleDidChange + @private */ _isVisibleDidChange: Ember.observer('isVisible', function() { var $el = this.$(); @@ -21909,13 +23821,12 @@ Ember.View = Ember.CoreView.extend( // /** - @private - Handle events from `Ember.EventDispatcher` @method handleEvent @param eventName {String} @param evt {Event} + @private */ handleEvent: function(eventName, evt) { return this.currentState.handleEvent(this, eventName, evt); @@ -22022,8 +23933,6 @@ Ember.View.reopen({ Ember.View.reopenClass({ /** - @private - Parse a path and return an object which holds the parsed properties. For example a path like "content.isEnabled:enabled:disabled" will return the @@ -22040,6 +23949,7 @@ Ember.View.reopenClass({ @method _parsePropertyPath @static + @private */ _parsePropertyPath: function(path) { var split = path.split(':'), @@ -22066,8 +23976,6 @@ Ember.View.reopenClass({ }, /** - @private - Get the class name for a given value, based on the path, optional `className` and optional `falsyClassName`. @@ -22089,6 +23997,7 @@ Ember.View.reopenClass({ @param className @param falsyClassName @static + @private */ _classStringForValue: function(path, val, className, falsyClassName) { // When using the colon syntax, evaluate the truthiness or falsiness @@ -22168,6 +24077,10 @@ Ember.View.applyAttributeBindings = function(elem, name, value) { // We can't set properties to undefined or null if (Ember.isNone(value)) { value = ''; } + if (!value) { + elem.removeAttr(name); + } + if (value !== elem.prop(name)) { // value and booleans should always be properties elem.prop(name, value); @@ -22335,7 +24248,10 @@ Ember.merge(inBuffer, { }, empty: function() { - Ember.assert("Emptying a view in the inBuffer state is not allowed and should not happen under normal circumstances. Most likely there is a bug in your application. This may be due to excessive property change notifications."); + Ember.assert("Emptying a view in the inBuffer state is not allowed and " + + "should not happen under normal circumstances. Most likely " + + "there is a bug in your application. This may be due to " + + "excessive property change notifications."); }, renderToBufferIfNeeded: function (view, buffer) { @@ -22563,7 +24479,7 @@ var ViewCollection = Ember._ViewCollection; /** A `ContainerView` is an `Ember.View` subclass that implements `Ember.MutableArray` - allowing programatic management of its child views. + allowing programmatic management of its child views. ## Setting Initial Child Views @@ -22603,7 +24519,7 @@ var ViewCollection = Ember._ViewCollection; ## Adding and Removing Child Views - The container view implements `Ember.MutableArray` allowing programatic management of its child views. + The container view implements `Ember.MutableArray` allowing programmatic management of its child views. To remove a view, pass that view into a `removeObject` call on the container view. @@ -22776,10 +24692,9 @@ Ember.ContainerView = Ember.View.extend(Ember.MutableArray, { }).volatile(), /** - @private - Instructs each child view to render to the passed render buffer. + @private @method render @param {Ember.RenderBuffer} buffer the buffer to render to */ @@ -22792,14 +24707,13 @@ Ember.ContainerView = Ember.View.extend(Ember.MutableArray, { instrumentName: 'container', /** - @private - When a child view is removed, destroy its element so that it is removed from the DOM. The array observer that triggers this action is set up in the `renderToBuffer` method. + @private @method childViewsWillChange @param {Ember.Array} views the child views array before mutation @param {Number} start the start position of the mutation @@ -22822,8 +24736,6 @@ Ember.ContainerView = Ember.View.extend(Ember.MutableArray, { }, /** - @private - When a child view is added, make sure the DOM gets updated appropriately. If the view has already rendered an element, we tell the child view to @@ -22832,6 +24744,7 @@ Ember.ContainerView = Ember.View.extend(Ember.MutableArray, { into an element, we insert the string representation of the child into the appropriate place in the buffer. + @private @method childViewsDidChange @param {Ember.Array} views the array of child views afte the mutation has occurred @param {Number} start the start position of the mutation @@ -23049,7 +24962,7 @@ var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt; Ember.CollectionView.CONTAINER_MAP['article'] = 'section' ``` - ## Programatic creation of child views + ## Programmatic creation of child views For cases where additional customization beyond the use of a single `itemViewClass` or `tagName` matching is required CollectionView's @@ -23109,7 +25022,7 @@ var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt; @extends Ember.ContainerView @since Ember 0.9 */ -Ember.CollectionView = Ember.ContainerView.extend(/** @scope Ember.CollectionView.prototype */ { +Ember.CollectionView = Ember.ContainerView.extend({ /** A list of items to be displayed by the `Ember.CollectionView`. @@ -23121,12 +25034,11 @@ Ember.CollectionView = Ember.ContainerView.extend(/** @scope Ember.CollectionVie content: null, /** - @private - This provides metadata about what kind of empty view class this collection would like if it is being instantiated from another system (like Handlebars) + @private @property emptyViewClass */ emptyViewClass: Ember.View, @@ -23159,11 +25071,10 @@ Ember.CollectionView = Ember.ContainerView.extend(/** @scope Ember.CollectionVie }, /** - @private - Invoked when the content property is about to change. Notifies observers that the entire array content will change. + @private @method _contentWillChange */ _contentWillChange: Ember.beforeObserver('content', function() { @@ -23175,13 +25086,12 @@ Ember.CollectionView = Ember.ContainerView.extend(/** @scope Ember.CollectionVie }), /** - @private - Check to make sure that the content has changed, and if so, update the children directly. This is always scheduled asynchronously, to allow the element to be created before bindings have synchronized and vice versa. + @private @method _contentDidChange */ _contentDidChange: Ember.observer('content', function() { @@ -23197,10 +25107,9 @@ Ember.CollectionView = Ember.ContainerView.extend(/** @scope Ember.CollectionVie }), /** - @private - Ensure that the content implements Ember.Array + @private @method _assertArrayLike */ _assertArrayLike: function(content) { @@ -23398,7 +25307,7 @@ var get = Ember.get, set = Ember.set, isNone = Ember.isNone, to the view object and actions are targeted at the view object. There is no access to the surrounding context or outer controller; all - contextual information is passed in. + contextual information must be passed in. The easiest way to create an `Ember.Component` is via a template. If you name a template @@ -23417,19 +25326,23 @@ var get = Ember.get, set = Ember.set, isNone = Ember.isNone,

{{person.signature}}

``` - You can also use `yield` inside a template to - include the **contents** of the custom tag: + You can use `yield` inside a template to + include the **contents** of any block attached to + the component. The block will be executed in the + context of the surrounding context or outer controller: - ```html + ```handlebars {{#app-profile person=currentUser}}

Admin mode

+ {{! Executed in the controllers context. }} {{/app-profile}} ``` - ```html + ```handlebars

{{person.title}}

- {{yield}} + {{! Executed in the components context. }} + {{yield}} {{! block contents }} ``` If you want to customize the component, in order to @@ -23480,6 +25393,11 @@ Ember.Component = Ember.View.extend(Ember.TargetActionSupport, { set(this, 'controller', this); }, + defaultLayout: function(options){ + options.data = {view: options._context}; + Ember.Handlebars.helpers['yield'].apply(this, [options]); + }, + // during render, isolate keywords cloneKeywords: function() { return { @@ -23608,10 +25526,15 @@ Ember.Component = Ember.View.extend(Ember.TargetActionSupport, { // Send the default action if (action === undefined) { actionName = get(this, 'action'); - Ember.assert("The default action was triggered on the component " + this.toString() + ", but the action name (" + actionName + ") was not a string.", isNone(actionName) || typeof actionName === 'string'); + Ember.assert("The default action was triggered on the component " + this.toString() + + ", but the action name (" + actionName + ") was not a string.", + isNone(actionName) || typeof actionName === 'string'); } else { actionName = get(this, action); - Ember.assert("The " + action + " action was triggered on the component " + this.toString() + ", but the action name (" + actionName + ") was not a string.", isNone(actionName) || typeof actionName === 'string'); + Ember.assert("The " + action + " action was triggered on the component " + + this.toString() + ", but the action name (" + actionName + + ") was not a string.", + isNone(actionName) || typeof actionName === 'string'); } // If no action name for that action could be found, just abort. @@ -23716,13 +25639,20 @@ define("metamorph", "use strict"; // ========================================================================== // Project: metamorph - // Copyright: ©2011 My Company Inc. All rights reserved. + // Copyright: ©2014 Tilde, Inc. All rights reserved. // ========================================================================== var K = function() {}, guid = 0, - document = this.document, - disableRange = ('undefined' === typeof ENV ? {} : ENV).DISABLE_RANGE_API, + 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) && document && ('createRange' in document) && (typeof Range !== 'undefined') && Range.prototype.createContextualFragment, @@ -23834,7 +25764,7 @@ define("metamorph", /** * @public - * + * * Remove this object (including starting and ending * placeholders). * @@ -24070,6 +26000,10 @@ define("metamorph", // 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) { @@ -24194,13 +26128,19 @@ var objectCreate = Object.create || function(parent) { return new F(); }; -var Handlebars = this.Handlebars || (Ember.imports && Ember.imports.Handlebars); +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); +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 @@ -24282,20 +26222,19 @@ Ember.Handlebars.helper = function(name, value) { }; /** - @private - Returns a helper function that renders the provided ViewClass. Used internally by Ember.Handlebars.helper and other methods involving helper/component registration. + @private @method helper @for Ember.Handlebars @param {Function} ViewClass view class constructor */ Ember.Handlebars.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", arguments.length < 2); + 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 Ember.Handlebars.helpers.view.call(this, ViewClass, options); }; }; @@ -24345,12 +26284,11 @@ Ember.Handlebars.JavaScriptCompiler.prototype.initializeBuffer = function() { }; /** - @private - 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} */ @@ -24368,7 +26306,7 @@ Ember.Handlebars.JavaScriptCompiler.prototype.appendToBuffer = function(string) // instead, as expected. // // This can go away once the following is closed: -// https://github.com/wycats/handlebars.js/issues/617 +// https://github.com/wycats/handlebars.js/issues/634 var DOT_LOOKUP_REGEX = /helpers\.(.*?)\)/, BRACKET_STRING_LOOKUP_REGEX = /helpers\['(.*?)'/, @@ -24398,12 +26336,11 @@ Ember.Handlebars.JavaScriptCompiler.prototype.ambiguousBlockValue = function() { var prefix = "ember" + (+new Date()), incr = 1; /** - @private - 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 @@ -24446,7 +26383,7 @@ Ember.Handlebars.precompile = function(string) { knownHelpers: { action: true, unbound: true, - bindAttr: true, + 'bind-attr': true, template: true, view: true, _triageMustache: true @@ -24493,11 +26430,10 @@ var slice = Array.prototype.slice, originalTemplate = Ember.Handlebars.template; /** - @private - If a path starts with a reserved keyword, returns the root that should be used. + @private @method normalizePath @for Ember @param root {Object} @@ -24551,19 +26487,17 @@ var handlebarsGet = Ember.Handlebars.get = function(root, path, options) { normalizedPath = normalizePath(root, path, data), value; - // In cases where the path begins with a keyword, change the - // root to the value represented by that keyword, and ensure - // the path is relative to it. - root = normalizedPath.root; - path = normalizedPath.path; + + root = normalizedPath.root; + path = normalizedPath.path; - value = Ember.get(root, path); + value = Ember.get(root, path); + + if (value === undefined && root !== Ember.lookup && Ember.isGlobalPath(path)) { + value = Ember.get(Ember.lookup, path); + } + - // If the path starts with a capital letter, look it up on Ember.lookup, - // which defaults to the `window` object in browsers. - if (value === undefined && root !== Ember.lookup && Ember.isGlobalPath(path)) { - value = Ember.get(Ember.lookup, path); - } return value; }; @@ -24603,8 +26537,6 @@ Ember.Handlebars.resolveHash = function(context, hash, options) { }; /** - @private - Registers a helper in Handlebars that will be called if no property with the given name can be found on the current context object, and no helper with that name is registered. @@ -24612,6 +26544,7 @@ Ember.Handlebars.resolveHash = function(context, hash, options) { This throws an exception with a more helpful error message so the user can track down where the problem is happening. + @private @method helperMissing @for Ember.Handlebars.helpers @param {String} path @@ -24622,14 +26555,11 @@ Ember.Handlebars.registerHelper('helperMissing', function(path) { var options = arguments[arguments.length - 1]; - + var helper = Ember.Handlebars.resolveHelper(options.data.view.container, path); - var helper = Ember.Handlebars.resolveHelper(options.data.view.container, path); - - if (helper) { - return helper.apply(this, slice.call(arguments, 1)); - } - + if (helper) { + return helper.apply(this, slice.call(arguments, 1)); + } error = "%@ Handlebars error: Could not find property '%@' on object %@."; if (options.data) { @@ -24639,8 +26569,6 @@ Ember.Handlebars.registerHelper('helperMissing', function(path) { }); /** - @private - Registers a helper in Handlebars that will be called if no property with the given name can be found on the current context object, and no helper with that name is registered. @@ -24648,6 +26576,7 @@ Ember.Handlebars.registerHelper('helperMissing', function(path) { This throws an exception with a more helpful error message so the user can track down where the problem is happening. + @private @method helperMissing @for Ember.Handlebars.helpers @param {String} path @@ -24657,16 +26586,19 @@ Ember.Handlebars.registerHelper('blockHelperMissing', function(path) { var options = arguments[arguments.length - 1]; - + Ember.assert("`blockHelperMissing` was invoked without a helper name, which " + + "is most likely due to a mismatch between the version of " + + "Ember.js you're running now and the one used to precompile your " + + "templates. Please make sure the version of " + + "`ember-handlebars-compiler` you're using is up to date.", path); - Ember.assert("`blockHelperMissing` was invoked without a helper name, which is most likely due to a mismatch between the version of Ember.js you're running now and the one used to precompile your templates. Please make sure the version of `ember-handlebars-compiler` you're using is up to date.", path); + var helper = Ember.Handlebars.resolveHelper(options.data.view.container, path); - var helper = Ember.Handlebars.resolveHelper(options.data.view.container, path); - - if (helper) { - return helper.apply(this, slice.call(arguments, 1)); - } - + if (helper) { + return helper.apply(this, slice.call(arguments, 1)); + } else { + return Handlebars.helpers.helperMissing.call(this, path); + } return Handlebars.helpers.blockHelperMissing.apply(this, arguments); }); @@ -24787,8 +26719,6 @@ Ember.Handlebars.registerBoundHelper = function(name, fn) { }; /** - @private - A (mostly) private helper function to `registerBoundHelper`. Takes the provided Handlebars helper function fn and returns it in wrapped bound helper form. @@ -24807,6 +26737,7 @@ Ember.Handlebars.registerBoundHelper = function(name, fn) { In the above example, if the helper function hadn't been wrapped in `makeBoundHelper`, the registered helper would be unbound. + @private @method makeBoundHelper @for Ember.Handlebars @param {Function} function @@ -24820,8 +26751,8 @@ Ember.Handlebars.makeBoundHelper = function(fn) { numProperties = properties.length, options = arguments[arguments.length - 1], normalizedProperties = [], - types = options.types, data = options.data, + types = data.isUnbound ? slice.call(options.types, 1) : options.types, hash = options.hash, view = data.view, contexts = options.contexts, @@ -24852,7 +26783,11 @@ Ember.Handlebars.makeBoundHelper = function(fn) { normalizedProperties.push(normalizedProp); watchedProperties.push(normalizedProp); } else { - normalizedProperties.push(null); + if(data.isUnbound) { + normalizedProperties.push({path: properties[loc]}); + }else { + normalizedProperties.push(null); + } } } @@ -24930,10 +26865,9 @@ Ember.Handlebars.makeBoundHelper = function(fn) { }; /** - @private - Renders the unbound form of an otherwise bound helper function. + @private @method evaluateUnboundHelper @param {Function} fn @param {Object} context @@ -24941,7 +26875,15 @@ Ember.Handlebars.makeBoundHelper = function(fn) { @param {String} options */ function evaluateUnboundHelper(context, fn, normalizedProperties, options) { - var args = [], hash = options.hash, boundOptions = hash.boundOptions, loc, len, property, boundOption; + var args = [], + hash = options.hash, + boundOptions = hash.boundOptions, + types = slice.call(options.types, 1), + loc, + len, + property, + propertyType, + boundOption; for (boundOption in boundOptions) { if (!boundOptions.hasOwnProperty(boundOption)) { continue; } @@ -24950,18 +26892,22 @@ function evaluateUnboundHelper(context, fn, normalizedProperties, options) { for(loc = 0, len = normalizedProperties.length; loc < len; ++loc) { property = normalizedProperties[loc]; - args.push(Ember.Handlebars.get(property.root, property.path, options)); + propertyType = types[loc]; + if(propertyType === "ID") { + args.push(Ember.Handlebars.get(property.root, property.path, options)); + } else { + args.push(property.path); + } } args.push(options); return fn.apply(context, args); } /** - @private - Overrides Handlebars.template so that we can distinguish user-created, top-level templates from inner contexts. + @private @method template @for Ember.Handlebars @param {String} spec @@ -25081,7 +27027,7 @@ var DOMManager = { view.transitionTo('preRender'); - Ember.run.schedule('render', this, function() { + Ember.run.schedule('render', this, function renderMetamorphView() { if (view.isDestroying) { return; } view.clearRenderedChildren(); @@ -25513,6 +27459,7 @@ Ember._HandlebarsBoundView = Ember._MetamorphView.extend({ var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt; var handlebarsGet = Ember.Handlebars.get, normalizePath = Ember.Handlebars.normalizePath; var forEach = Ember.ArrayPolyfills.forEach; +var o_create = Ember.create; var EmberHandlebars = Ember.Handlebars, helpers = EmberHandlebars.helpers; @@ -25643,8 +27590,6 @@ function simpleBind(currentContext, property, options) { } /** - @private - '_triageMustache' is used internally select between a binding, helper, or component for the given context. Until this point, it would be hard to determine if the mustache is a property reference or a regular helper reference. This triage @@ -25652,6 +27597,7 @@ function simpleBind(currentContext, property, options) { This would not be typically invoked by directly. + @private @method _triageMustache @for Ember.Handlebars.helpers @param {String} property Property/helperID to triage @@ -25665,13 +27611,10 @@ EmberHandlebars.registerHelper('_triageMustache', function(property, options) { return helpers[property].call(this, options); } - - - var helper = Ember.Handlebars.resolveHelper(options.data.view.container, property); - if (helper) { - return helper.call(this, options); - } - + var helper = Ember.Handlebars.resolveHelper(options.data.view.container, property); + if (helper) { + return helper.call(this, options); + } return helpers.bind.call(this, property, options); }); @@ -25697,8 +27640,6 @@ Ember.Handlebars.resolveHelper = function(container, name) { }; /** - @private - `bind` can be used to display a value, then update that value if it changes. For example, if you wanted to print the `title` property of `content`: @@ -25714,13 +27655,14 @@ Ember.Handlebars.resolveHelper = function(container, name) { relies on Ember's KVO system. For all other browsers this will be handled for you automatically. + @private @method bind @for Ember.Handlebars.helpers @param {String} property Property to bind @param {Function} fn Context to provide for rendering @return {String} HTML string */ -EmberHandlebars.registerHelper('bind', function(property, options) { +EmberHandlebars.registerHelper('bind', function bindHelper(property, options) { Ember.assert("You cannot pass more than one argument to the bind helper", arguments.length <= 2); var context = (options.contexts && options.contexts.length) ? options.contexts[0] : this; @@ -25733,8 +27675,6 @@ EmberHandlebars.registerHelper('bind', function(property, options) { }); /** - @private - Use the `boundIf` helper to create a conditional that re-evaluates whenever the truthiness of the bound value changes. @@ -25744,13 +27684,14 @@ EmberHandlebars.registerHelper('bind', function(property, options) { {{/boundIf}} ``` + @private @method boundIf @for Ember.Handlebars.helpers @param {String} property Property to bind @param {Function} fn Context to provide for rendering @return {String} HTML string */ -EmberHandlebars.registerHelper('boundIf', function(property, fn) { +EmberHandlebars.registerHelper('boundIf', function boundIfHelper(property, fn) { var context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this; var func = function(result) { var truthy = result && get(result, 'isTruthy'); @@ -25767,15 +27708,65 @@ EmberHandlebars.registerHelper('boundIf', function(property, fn) { }); /** + Use the `{{with}}` helper when you want to scope context. Take the following code as an example: + + ```handlebars +
{{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. + @method with @for Ember.Handlebars.helpers @param {Function} context @param {Hash} options @return {String} HTML string */ -EmberHandlebars.registerHelper('with', function(context, options) { +EmberHandlebars.registerHelper('with', function withHelper(context, options) { if (arguments.length === 4) { - var keywordName, path, rootPath, normalized; + 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]; @@ -25784,8 +27775,12 @@ EmberHandlebars.registerHelper('with', function(context, options) { 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 (Ember.isGlobalPath(path)) { - Ember.bind(options.data.keywords, keywordName, path); + contextPath = path; } else { normalized = normalizePath(this, path, options.data); path = normalized.path; @@ -25794,14 +27789,14 @@ EmberHandlebars.registerHelper('with', function(context, options) { // 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 = Ember.$.expando + Ember.guidFor(rootPath); - options.data.keywords[contextKey] = rootPath; - + localizedOptions.data.keywords[contextKey] = rootPath; // if the path is '' ("this"), just bind directly to the current context - var contextPath = path ? contextKey + '.' + path : contextKey; - Ember.bind(options.data.keywords, keywordName, contextPath); + contextPath = path ? contextKey + '.' + path : contextKey; } - return bind.call(this, path, options, true, exists); + Ember.bind(localizedOptions.data.keywords, keywordName, contextPath); + + return bind.call(this, path, localizedOptions, true, exists); } 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); @@ -25819,7 +27814,7 @@ EmberHandlebars.registerHelper('with', function(context, options) { @param {Hash} options @return {String} HTML string */ -EmberHandlebars.registerHelper('if', function(context, options) { +EmberHandlebars.registerHelper('if', 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); @@ -25833,7 +27828,7 @@ EmberHandlebars.registerHelper('if', function(context, options) { @param {Hash} options @return {String} HTML string */ -EmberHandlebars.registerHelper('unless', function(context, options) { +EmberHandlebars.registerHelper('unless', 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); @@ -25968,7 +27963,7 @@ EmberHandlebars.registerHelper('unless', function(context, options) { @param {Hash} options @return {String} HTML string */ -EmberHandlebars.registerHelper('bind-attr', function(options) { +EmberHandlebars.registerHelper('bind-attr', function bindAttrHelper(options) { var attrs = options.hash; @@ -26014,7 +28009,9 @@ EmberHandlebars.registerHelper('bind-attr', function(options) { 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'); + 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 + "']"); @@ -26064,11 +28061,12 @@ EmberHandlebars.registerHelper('bind-attr', function(options) { @param {Hash} options @return {String} HTML string */ -EmberHandlebars.registerHelper('bindAttr', EmberHandlebars.helpers['bind-attr']); +EmberHandlebars.registerHelper('bindAttr', function bindAttrHelper() { + Ember.warn("The 'bindAttr' view helper is deprecated in favor of 'bind-attr'"); + return EmberHandlebars.helpers['bind-attr'].apply(this, arguments); +}); /** - @private - 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 @@ -26080,6 +28078,7 @@ EmberHandlebars.registerHelper('bindAttr', EmberHandlebars.helpers['bind-attr']) "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 @@ -26327,7 +28326,7 @@ EmberHandlebars.ViewHelper = Ember.Object.create({ return 'templateData.keywords.' + path; } else if (Ember.isGlobalPath(path)) { return null; - } else if (path === 'this') { + } else if (path === 'this' || path === '') { return '_parentView.context'; } else { return '_parentView.context.' + path; @@ -26546,7 +28545,7 @@ EmberHandlebars.ViewHelper = Ember.Object.create({ @param {Hash} options @return {String} HTML string */ -EmberHandlebars.registerHelper('view', function(path, options) { +EmberHandlebars.registerHelper('view', function viewHelper(path, options) { Ember.assert("The view helper only takes a single argument", arguments.length <= 2); // If no path is provided, treat path param as options. @@ -26564,8 +28563,6 @@ EmberHandlebars.registerHelper('view', function(path, options) { (function() { -/*globals Handlebars */ - // TODO: Don't require all of this module /** @module ember @@ -26696,7 +28693,7 @@ var get = Ember.get, handlebarsGet = Ember.Handlebars.get, fmt = Ember.String.fm @return {String} HTML string @deprecated Use `{{each}}` helper instead. */ -Ember.Handlebars.registerHelper('collection', function(path, options) { +Ember.Handlebars.registerHelper('collection', 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. @@ -26727,10 +28724,17 @@ Ember.Handlebars.registerHelper('collection', function(path, options) { if (hash.itemView) { var 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); + 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); var container = controller.container; itemViewClass = container.resolve('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); + 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 { @@ -26764,7 +28768,7 @@ Ember.Handlebars.registerHelper('collection', function(path, options) { } var emptyViewClass; - if (inverse && inverse !== Handlebars.VM.noop) { + if (inverse && inverse !== Ember.Handlebars.VM.noop) { emptyViewClass = get(collectionPrototype, 'emptyViewClass'); emptyViewClass = emptyViewClass.extend({ template: inverse, @@ -26819,13 +28823,13 @@ var handlebarsGet = Ember.Handlebars.get; @param {String} property @return {String} HTML string */ -Ember.Handlebars.registerHelper('unbound', function(property, fn) { +Ember.Handlebars.registerHelper('unbound', function unboundHelper(property, fn) { var options = arguments[arguments.length - 1], helper, context, out; if (arguments.length > 2) { // Unbound helper call. options.data.isUnbound = true; - helper = Ember.Handlebars.helpers[arguments[0]] || Ember.Handlebars.helperMissing; + helper = Ember.Handlebars.helpers[arguments[0]] || Ember.Handlebars.helpers.helperMissing; out = helper.apply(this, Array.prototype.slice.call(arguments, 1)); delete options.data.isUnbound; return out; @@ -26846,7 +28850,7 @@ Ember.Handlebars.registerHelper('unbound', function(property, fn) { @submodule ember-handlebars */ -var handlebarsGet = Ember.Handlebars.get, normalizePath = Ember.Handlebars.normalizePath; +var get = Ember.get, handlebarsGet = Ember.Handlebars.get, normalizePath = Ember.Handlebars.normalizePath; /** `log` allows you to output the value of a variable in the current rendering @@ -26860,7 +28864,7 @@ var handlebarsGet = Ember.Handlebars.get, normalizePath = Ember.Handlebars.norma @for Ember.Handlebars.helpers @param {String} property */ -Ember.Handlebars.registerHelper('log', function(property, options) { +Ember.Handlebars.registerHelper('log', function logHelper(property, options) { var context = (options.contexts && options.contexts.length) ? options.contexts[0] : this, normalized = normalizePath(context, property, options.data), pathRoot = normalized.root, @@ -26876,14 +28880,44 @@ Ember.Handlebars.registerHelper('log', function(property, options) { {{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 */ -Ember.Handlebars.registerHelper('debugger', function(options) { +Ember.Handlebars.registerHelper('debugger', function debuggerHelper(options) { + + // These are helpful values you can inspect while debugging. + var templateContext = this; + var typeOfTemplateContext = Ember.inspect(templateContext); + debugger; }); + + })(); @@ -27262,7 +29296,7 @@ GroupedEach.prototype = { @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 */ -Ember.Handlebars.registerHelper('each', function(path, options) { +Ember.Handlebars.registerHelper('each', function eachHelper(path, options) { 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"); @@ -27410,7 +29444,7 @@ Ember.Handlebars.registerHelper('template', function(name, options) { @param {String} partialName the name of the template to render minus the leading underscore */ -Ember.Handlebars.registerHelper('partial', function(name, options) { +Ember.Handlebars.registerHelper('partial', function partialHelper(name, options) { var context = (options.contexts && options.contexts.length) ? options.contexts[0] : this; @@ -27546,7 +29580,7 @@ var get = Ember.get, set = Ember.set; @param {Hash} options @return {String} HTML string */ -Ember.Handlebars.registerHelper('yield', function(options) { +Ember.Handlebars.registerHelper('yield', function yieldHelper(options) { var view = options.data.view; while (view && !get(view, 'layout')) { @@ -27590,7 +29624,7 @@ Ember.Handlebars.registerHelper('yield', function(options) { @param {String} str The string to format */ -Ember.Handlebars.registerHelper('loc', function(str) { +Ember.Handlebars.registerHelper('loc', function locHelper(str) { return Ember.String.loc(str); }); @@ -27622,7 +29656,7 @@ var set = Ember.set, get = Ember.get; The internal class used to create text inputs when the `{{input}}` helper is used with `type` of `checkbox`. - See Handlebars.helpers.input for usage details. + See [handlebars.helpers.input](/api/classes/Ember.Handlebars.helpers.html#method_input) for usage details. ## Direct manipulation of `checked` @@ -27870,7 +29904,7 @@ var get = Ember.get, set = Ember.set; The internal class used to create text inputs when the `{{input}}` helper is used with `type` of `text`. - See [handlebars.helpers.input](Ember.Handlebars.helpers.html#method_input) for usage details. + See [Handlebars.helpers.input](/api/classes/Ember.Handlebars.helpers.html#method_input) for usage details. ## Layout and LayoutName properties @@ -27883,8 +29917,7 @@ var get = Ember.get, set = Ember.set; @extends Ember.Component @uses Ember.TextSupport */ -Ember.TextField = Ember.Component.extend(Ember.TextSupport, - /** @scope Ember.TextField.prototype */ { +Ember.TextField = Ember.Component.extend(Ember.TextSupport, { classNames: ['ember-text-field'], tagName: "input", @@ -28439,9 +30472,7 @@ Ember.SelectOptgroup = Ember.CollectionView.extend({ @namespace Ember @extends Ember.View */ -Ember.Select = Ember.View.extend( - /** @scope Ember.Select.prototype */ { - +Ember.Select = Ember.View.extend({ tagName: 'select', classNames: ['ember-select'], defaultTemplate: Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) { @@ -28451,11 +30482,12 @@ helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {}; function program1(depth0,data) { - var buffer = '', hashTypes, hashContexts; + var buffer = '', stack1, hashTypes, hashContexts; data.buffer.push(""); return buffer; } @@ -28855,6 +30887,7 @@ function program7(depth0,data) { if you are deploying to browsers where the `required` attribute is used, you can add this to the `TextField`'s `attributeBindings` property: + ```javascript Ember.TextField.reopen({ attributeBindings: ['required'] @@ -28919,6 +30952,7 @@ function program7(depth0,data) { 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'] @@ -29077,7 +31111,7 @@ Ember.Handlebars.registerHelper('input', function(options) { extend the capabilities of text areas in your application by reopening this class. For example, if you are deploying to browsers where the `required` attribute is used, you can globally add support for the `required` attribute - on all {{textarea}}'s' in your app by reopening `Ember.TextArea` or + on all `{{textarea}}`s' in your app by reopening `Ember.TextArea` or `Ember.TextSupport` and adding it to the `attributeBindings` concatenated property: @@ -29150,8 +31184,6 @@ Ember.ComponentLookup = Ember.Object.extend({ */ /** - @private - Find templates stored in the head tag as script tags and make them available to `Ember.CoreView` in the global `Ember.TEMPLATES` object. This will be run as as jQuery DOM-ready callback. @@ -29161,6 +31193,7 @@ Ember.ComponentLookup = Ember.Object.extend({ Those with type `text/x-raw-handlebars` will be compiled with regular Handlebars and are suitable for use in views' computed properties. + @private @method bootstrap @for Ember.Handlebars @static @@ -29200,35 +31233,6 @@ function bootstrap() { Ember.Handlebars.bootstrap( Ember.$(document) ); } -function registerComponents(container) { - var templates = Ember.TEMPLATES, match; - if (!templates) { return; } - - for (var prop in templates) { - if (match = prop.match(/^components\/(.*)$/)) { - registerComponent(container, match[1]); - } - } -} - - -function registerComponent(container, name) { - Ember.assert("You provided a template named 'components/" + name + "', but custom components must include a '-'", name.match(/-/)); - - var fullName = 'component:' + name; - - container.injection(fullName, 'layout', 'template:components/' + name); - - var Component = container.lookupFactory(fullName); - - if (!Component) { - container.register(fullName, Ember.Component); - Component = container.lookupFactory(fullName); - } - - Ember.Handlebars.helper(name, Component); -} - function registerComponentLookup(container) { container.register('component-lookup:main', Ember.ComponentLookup); } @@ -29250,13 +31254,12 @@ Ember.onLoad('Ember.Application', function(Application) { initialize: bootstrap }); - - Application.initializer({ - name: 'registerComponentLookup', - after: 'domTemplates', - initialize: registerComponentLookup - }); + Application.initializer({ + name: 'registerComponentLookup', + after: 'domTemplates', + initialize: registerComponentLookup }); +}); })(); @@ -30053,7 +32056,7 @@ define("router", // TODO: separate into module? Router.Transition = Transition; - __exports__['default'] = Router; + __exports__["default"] = Router; /** @@ -30283,7 +32286,7 @@ define("router", if (isParam(object)) { var name = recogHandler.names[0]; - if ("" + object !== this.currentParams[name]) { return false; } + if (!this.currentParams || "" + object !== this.currentParams[name]) { return false; } } else if (handlerInfo.context !== object) { return false; } @@ -31367,7 +33370,8 @@ DSL.prototype = { this.push(options.path, name, null, options.queryParams); } - }, + + }, push: function(url, name, callback, queryParams) { var parts = name.split('.'); @@ -31378,7 +33382,7 @@ DSL.prototype = { route: function(name, options) { route(this, name, options); - }, + }, generate: function() { var dslMatches = this.matches; @@ -31433,7 +33437,7 @@ var get = Ember.get; */ /** - + Finds a controller instance. @for Ember @@ -31445,17 +33449,22 @@ Ember.controllerFor = function(container, controllerName, lookupOptions) { }; /** - Generates a controller automatically if none was provided. - The type of generated controller depends on the context. + Generates a controller factory + + The type of the generated controller factory is derived + from the context. If the context is an array an array controller + is generated, if an object, an object controller otherwise, a basic + controller is generated. + You can customize your generated controllers by defining - `App.ObjectController` and `App.ArrayController` - + `App.ObjectController` or `App.ArrayController`. + @for Ember - @method generateController + @method generateControllerFactory @private */ -Ember.generateController = function(container, controllerName, context) { - var ControllerFactory, fullName, instance, name, factoryName, controllerType; +Ember.generateControllerFactory = function(container, controllerName, context) { + var Factory, fullName, instance, name, factoryName, controllerType; if (context && Ember.isArray(context)) { controllerType = 'array'; @@ -31467,7 +33476,7 @@ Ember.generateController = function(container, controllerName, context) { factoryName = 'controller:' + controllerType; - ControllerFactory = container.lookupFactory(factoryName).extend({ + Factory = container.lookupFactory(factoryName).extend({ isGenerated: true, toString: function() { return "(generated " + controllerName + " controller)"; @@ -31476,9 +33485,27 @@ Ember.generateController = function(container, controllerName, context) { fullName = 'controller:' + controllerName; - container.register(fullName, ControllerFactory); + container.register(fullName, Factory); - instance = container.lookup(fullName); + return Factory; +}; + +/** + Generates and instantiates a controller. + + The type of the generated controller factory is derived + from the context. If the context is an array an array controller + is generated, if an object, an object controller otherwise, a basic + controller is generated. + + @for Ember + @method generateController + @private +*/ +Ember.generateController = function(container, controllerName, context) { + Ember.generateControllerFactory(container, controllerName, context); + var fullName = 'controller:' + controllerName; + var instance = container.lookup(fullName); if (get(instance, 'namespace.LOG_ACTIVE_GENERATION')) { Ember.Logger.info("generated -> " + fullName, { fullName: fullName }); @@ -31500,6 +33527,7 @@ Ember.generateController = function(container, controllerName, context) { var Router = requireModule("router")['default']; var get = Ember.get, set = Ember.set; var defineProperty = Ember.defineProperty; +var slice = Array.prototype.slice; var DefaultView = Ember._MetamorphView; /** @@ -31511,18 +33539,49 @@ var DefaultView = Ember._MetamorphView; @extends Ember.Object */ Ember.Router = Ember.Object.extend(Ember.Evented, { + /** + The `location` property determines the type of URL's that your + application will use. + + The following location types are currently available: + + * `hash` + * `history` + * `none` + + @property location + @default 'hash' + @see {Ember.Location} + */ location: 'hash', init: function() { this.router = this.constructor.router || this.constructor.map(Ember.K); this._activeViews = {}; this._setupLocation(); + + if (get(this, 'namespace.LOG_TRANSITIONS_INTERNAL')) { + this.router.log = Ember.Logger.debug; + } }, + /** + Represents the current URL. + + @method url + @returns {String} The current URL. + */ url: Ember.computed(function() { return get(this, 'location').getURL(); }), + /** + Initializes the current router instance and sets up the change handling + event listeners used by the instances `location` implementation. + + @method startRouting + @private + */ startRouting: function() { this.router = this.router || this.constructor.map(Ember.K); @@ -31543,19 +33602,25 @@ Ember.Router = Ember.Object.extend(Ember.Evented, { this.handleURL(location.getURL()); }, + /** + Handles updating the paths and notifying any listeners of the URL + change. + + Triggers the router level `didTransition` hook. + + @method didTransition + @private + */ didTransition: function(infos) { updatePaths(this); - - this._cancelLoadingEvent(); - + this._cancelLoadingEvent(); + this.notifyPropertyChange('url'); - - // Put this in the runloop so url will be accurate. Seems - // less surprising than didTransition being out of sync. - Ember.run.once(this, this.trigger, 'didTransition'); - + // Put this in the runloop so url will be accurate. Seems + // less surprising than didTransition being out of sync. + Ember.run.once(this, this.trigger, 'didTransition'); if (get(this, 'namespace').LOG_TRANSITIONS) { Ember.Logger.log("Transitioned into '" + Ember.Router._routePath(infos) + "'"); @@ -31590,6 +33655,14 @@ Ember.Router = Ember.Object.extend(Ember.Evented, { return this.location.formatURL(url); }, + /** + Determines if the supplied route is currently active. + + @method isActive + @param routeName + @returns {Boolean} + @private + */ isActive: function(routeName) { var router = this.router; return router.isActive.apply(router, arguments); @@ -31599,16 +33672,22 @@ Ember.Router = Ember.Object.extend(Ember.Evented, { this.router.trigger.apply(this.router, arguments); }, + /** + Does this router instance have the given route. + + @method hasRoute + @returns {Boolean} + @private + */ hasRoute: function(route) { return this.router.hasRoute(route); }, /** - @private - Resets the state of the router by clearing the current route handlers and deactivating them. + @private @method reset */ reset: function() { @@ -31655,6 +33734,10 @@ Ember.Router = Ember.Object.extend(Ember.Evented, { options.implementation = location; location = set(this, 'location', Ember.Location.create(options)); } + + // ensure that initState is called AFTER the rootURL is set on + // the location instance + if (typeof location.initState === 'function') { location.initState(); } }, _getHandlerFunction: function() { @@ -31671,8 +33754,6 @@ Ember.Router = Ember.Object.extend(Ember.Evented, { seen[name] = true; if (!handler) { - - container.register(routeName, DefaultRoute.extend()); handler = container.lookup(routeName); @@ -31718,7 +33799,7 @@ Ember.Router = Ember.Object.extend(Ember.Evented, { _doTransition: function(method, args) { // Normalize blank route to root URL. - args = [].slice.call(args); + args = slice.call(args); args[0] = args[0] || '/'; var passedName = args[0], name, self = this, @@ -31739,14 +33820,11 @@ Ember.Router = Ember.Object.extend(Ember.Evented, { var transitionPromise = this.router[method].apply(this.router, args); - // Don't schedule loading state entry if user has already aborted the transition. - - transitionPromise.then(null, function(error) { if (error.name === "UnrecognizedURLError") { Ember.assert("The URL '" + error.message + "' did not match any routes in your application"); } - }); + }, 'Ember: Check for Router unrecognized URL error'); // We want to return the configurable promise object // so that callers of this function can use `.method()` on it, @@ -31756,21 +33834,18 @@ Ember.Router = Ember.Object.extend(Ember.Evented, { _scheduleLoadingEvent: function(transition, originRoute) { this._cancelLoadingEvent(); - - this._loadingStateTimer = Ember.run.scheduleOnce('routerTransitions', this, '_fireLoadingEvent', transition, originRoute); - + this._loadingStateTimer = Ember.run.scheduleOnce('routerTransitions', this, '_fireLoadingEvent', transition, originRoute); }, _fireLoadingEvent: function(transition, originRoute) { - - if (!this.router.activeTransition) { - // Don't fire an event if we've since moved on from - // the transition that put us in a loading state. - return; - } + if (!this.router.activeTransition) { + // Don't fire an event if we've since moved on from + // the transition that put us in a loading state. + return; + } - transition.trigger(true, 'loading', transition, originRoute); - }, + transition.trigger(true, 'loading', transition, originRoute); + }, _cancelLoadingEvent: function () { if (this._loadingStateTimer) { @@ -31781,13 +33856,13 @@ Ember.Router = Ember.Object.extend(Ember.Evented, { }); /** - @private - Helper function for iterating root-ward, starting from (but not including) the provided `originRoute`. Returns true if the last callback fired requested to bubble upward. + + @private */ function forEachRouteAbove(originRoute, transition, callback) { var handlerInfos = transition.handlerInfos, @@ -31820,63 +33895,57 @@ var defaultActionHandlers = { }, error: function(error, transition, originRoute) { - + // Attempt to find an appropriate error substate to enter. + var router = originRoute.router; - // Attempt to find an appropriate error substate to enter. - var router = originRoute.router; - - var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) { - var childErrorRouteName = findChildRouteName(route, childRoute, 'error'); - if (childErrorRouteName) { - router.intermediateTransitionTo(childErrorRouteName, error); - return; - } - return true; - }); - - if (tryTopLevel) { - // Check for top-level error state to enter. - if (routeHasBeenDefined(originRoute.router, 'application_error')) { - router.intermediateTransitionTo('application_error', error); - return; - } - } else { - // Don't fire an assertion if we found an error substate. + var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) { + var childErrorRouteName = findChildRouteName(route, childRoute, 'error'); + if (childErrorRouteName) { + router.intermediateTransitionTo(childErrorRouteName, error); return; } - + return true; + }); - Ember.Logger.assert(false, 'Error while loading route: ' + Ember.inspect(error)); + if (tryTopLevel) { + // Check for top-level error state to enter. + if (routeHasBeenDefined(originRoute.router, 'application_error')) { + router.intermediateTransitionTo('application_error', error); + return; + } + } else { + // Don't fire an assertion if we found an error substate. + return; + } + + Ember.Logger.error('Error while loading route: ' + error.stack); }, loading: function(transition, originRoute) { - + // Attempt to find an appropriate loading substate to enter. + var router = originRoute.router; - // Attempt to find an appropriate loading substate to enter. - var router = originRoute.router; + var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) { + var childLoadingRouteName = findChildRouteName(route, childRoute, 'loading'); - var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) { - var childLoadingRouteName = findChildRouteName(route, childRoute, 'loading'); - - if (childLoadingRouteName) { - router.intermediateTransitionTo(childLoadingRouteName); - return; - } - - // Don't bubble above pivot route. - if (transition.pivotHandler !== route) { - return true; - } - }); - - if (tryTopLevel) { - // Check for top-level loading state to enter. - if (routeHasBeenDefined(originRoute.router, 'application_loading')) { - router.intermediateTransitionTo('application_loading'); - return; - } + if (childLoadingRouteName) { + router.intermediateTransitionTo(childLoadingRouteName); + return; } - + + // Don't bubble above pivot route. + if (transition.pivotHandler !== route) { + return true; + } + }); + + if (tryTopLevel) { + // Check for top-level loading state to enter. + if (routeHasBeenDefined(originRoute.router, 'application_loading')) { + router.intermediateTransitionTo('application_loading'); + return; + } + } } }; @@ -31886,6 +33955,8 @@ function findChildRouteName(parentRoute, originatingChildRoute, name) { targetChildRouteName = originatingChildRoute.routeName.split('.').pop(), namespace = parentRoute.routeName === 'application' ? '' : parentRoute.routeName + '.'; + + // Second, try general loading state, e.g. 'loading' childName = namespace + name; if (routeHasBeenDefined(router, childName)) { return childName; @@ -31927,7 +33998,7 @@ function triggerEvent(handlerInfos, ignoreFailure, args) { } if (!eventWasHandled && !ignoreFailure) { - throw new Ember.Error("Nothing handled the action '" + name + "'."); + throw new Ember.Error("Nothing handled the action '" + name + "'. If you did handle the action, this error can be caused by returning true from an action handler in a controller, causing the action to bubble."); } } @@ -31957,41 +34028,6 @@ function updatePaths(router) { set(appController, 'currentRouteName', infos[infos.length - 1].name); } -function scheduleLegacyLoadingRouteEntry(router) { - cancelLegacyLoadingRouteEntry(router); - if (router.router.activeTransition) { - router._legacyLoadingStateTimer = Ember.run.scheduleOnce('routerTransitions', null, enterLegacyLoadingRoute, router); - } -} - -function enterLegacyLoadingRoute(router) { - var loadingRoute = router.router.getHandler('loading'); - if (loadingRoute && !loadingRoute._loadingStateActive) { - if (loadingRoute.enter) { loadingRoute.enter(); } - if (loadingRoute.setup) { loadingRoute.setup(); } - loadingRoute._loadingStateActive = true; - } -} - -function cancelLegacyLoadingRouteEntry(router) { - if (router._legacyLoadingStateTimer) { - Ember.run.cancel(router._legacyLoadingStateTimer); - } - router._legacyLoadingStateTimer = null; -} - -function exitLegacyLoadingRoute(router) { - - cancelLegacyLoadingRouteEntry(router); - - var loadingRoute = router.router.getHandler('loading'); - - if (loadingRoute && loadingRoute._loadingStateActive) { - if (loadingRoute.exit) { loadingRoute.exit(); } - loadingRoute._loadingStateActive = false; - } -} - Ember.Router.reopenClass({ router: null, map: function(callback) { @@ -32003,10 +34039,6 @@ Ember.Router.reopenClass({ this.reopenClass({ router: router }); } - if (get(this, 'namespace.LOG_TRANSITIONS_INTERNAL')) { - router.log = Ember.Logger.debug; - } - var dsl = Ember.RouterDSL.map(function() { this.resource('application', { path: "/" }, function() { for (var i=0; i < router.callbacks.length; i++) { @@ -32024,11 +34056,32 @@ Ember.Router.reopenClass({ _routePath: function(handlerInfos) { var path = []; + // We have to handle coalescing resource names that + // are prefixed with their parent's names, e.g. + // ['foo', 'foo.bar.baz'] => 'foo.bar.baz', not 'foo.foo.bar.baz' + + function intersectionMatches(a1, a2) { + for (var i = 0, len = a1.length; i < len; ++i) { + if (a1[i] !== a2[i]) { + return false; + } + } + return true; + } + for (var i=1, l=handlerInfos.length; i= 2); - var contextProvided = arguments.length === 3, + Ember.Handlebars.registerHelper('render', function renderHelper(name, contextString, options) { + var length = arguments.length; + Ember.assert("You must pass a template to render", length >= 2); + var contextProvided = length === 3, container, router, controller, view, context, lookupOptions; - if (arguments.length === 2) { - options = contextString; - contextString = undefined; - } - - if (typeof contextString === 'string') { - context = Ember.Handlebars.get(options.contexts[1], contextString, options); - lookupOptions = { singleton: false }; - } - - name = name.replace(/\//g, '.'); - container = options.data.keywords.controller.container; + container = (options || contextString).data.keywords.controller.container; router = container.lookup('router:main'); - Ember.assert("You can only use the {{render}} helper once without a model object as its second argument, as in {{render \"post\" post}}.", contextProvided || !router || !router._lookupActiveView(name)); + if (length === 2) { + // use the singleton controller + options = contextString; + contextString = undefined; + Ember.assert("You can only use the {{render}} helper once without a model object as its second argument, as in {{render \"post\" post}}.", !router || !router._lookupActiveView(name)); + } else if (length === 3) { + // create a new controller + context = Ember.Handlebars.get(options.contexts[1], contextString, options); + } else { + throw Ember.Error("You must pass a templateName to render"); + } + + // # legacy namespace + name = name.replace(/\//g, '.'); + // \ legacy slash as namespace support + view = container.lookup('view:' + name) || container.lookup('view:default'); - var controllerName = options.hash.controller; + // provide controller override + var controllerName = options.hash.controller || name; + var controllerFullName = 'controller:' + controllerName; - // Look up the controller by name, if provided. - if (controllerName) { - controller = container.lookup('controller:' + controllerName, lookupOptions); - Ember.assert("The controller name you supplied '" + controllerName + "' did not resolve to a controller.", !!controller); - } else { - controller = container.lookup('controller:' + name, lookupOptions) || - Ember.generateController(container, name, context); + if (options.hash.controller) { + Ember.assert("The controller name you supplied '" + controllerName + "' did not resolve to a controller.", container.has(controllerFullName)); } - if (controller && contextProvided) { - controller.set('model', context); + var parentController = options.data.keywords.controller; + + // choose name + if (length > 2) { + var factory = container.lookupFactory(controllerFullName) || + Ember.generateControllerFactory(container, controllerName, context); + + controller = factory.create({ + model: context, + parentController: parentController, + target: parentController + }); + + } else { + controller = container.lookup(controllerFullName) || + Ember.generateController(container, controllerName); + + controller.setProperties({ + target: parentController, + parentController: parentController + }); } var root = options.contexts[1]; @@ -34340,10 +36460,12 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) { }); } - controller.set('target', options.data.keywords.controller); - options.hash.viewName = Ember.String.camelize(name); - options.hash.template = container.lookup('template:' + name); + + var templateName = 'template:' + name; + Ember.assert("You used `{{render '" + name + "'}}`, but '" + name + "' can not be found as either a template or a view.", container.has("view:" + name) || container.has(templateName)); + options.hash.template = container.lookup(templateName); + options.hash.controller = controller; if (router && !context) { @@ -34352,7 +36474,6 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) { Ember.Handlebars.helpers.view.call(this, view, options); }); - }); })(); @@ -34399,7 +36520,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) { if (POINTER_EVENT_TYPE_REGEX.test(event.type)) { return isSimpleClick(event); } else { - allowedKeys = []; + allowedKeys = ''; } } @@ -34423,10 +36544,12 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) { ActionHelper.registeredActions[actionId] = { eventName: options.eventName, - handler: function(event) { + handler: function handleRegisteredAction(event) { if (!isAllowedEvent(event, allowedKeys)) { return true; } - event.preventDefault(); + if (options.preventDefault !== false) { + event.preventDefault(); + } if (options.bubbles === false) { event.stopPropagation(); @@ -34440,7 +36563,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) { target = target.root; } - Ember.run(function() { + Ember.run(function runRegisteredAction() { if (target.send) { target.send.apply(target, args(options.parameters, actionName)); } else { @@ -34483,8 +36606,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) { App.ApplicationController = Ember.Controller.extend({ actions: { anActionName: function() { - - } + } } }); ``` @@ -34515,9 +36637,17 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) { Events triggered through the action helper will automatically have `.preventDefault()` called on them. You do not need to do so in your event - handlers. + handlers. If you need to allow event propagation (to handle file inputs for + example) you can supply the `preventDefault=false` option to the `{{action}}` helper: - To also disable bubbling, pass `bubbles=false` to the helper: + ```handlebars +
    + + +
    + ``` + + To disable bubbling, pass `bubbles=false` to the helper: ```handlebars @@ -34619,7 +36749,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) { @param {Object} [context]* @param {Hash} options */ - EmberHandlebars.registerHelper('action', function(actionName) { + EmberHandlebars.registerHelper('action', function actionHelper(actionName) { var options = arguments[arguments.length - 1], contexts = a_slice.call(arguments, 1, -1); @@ -34650,6 +36780,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) { action.target = { root: root, target: target, options: options }; action.bubbles = hash.bubbles; + action.preventDefault = hash.preventDefault; var actionId = ActionHelper.registerAction(actionName, action, hash.allowedKeys); return new SafeString('data-ember-action="' + actionId + '"'); @@ -34661,109 +36792,6 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) { -(function() { -/** -@module ember -@submodule ember-routing -*/ - -if (Ember.ENV.EXPERIMENTAL_CONTROL_HELPER) { - var get = Ember.get, set = Ember.set; - - /** - `{{control}}` works like render, except it uses a new controller instance for every call, instead of reusing the singleton controller. - - The control helper is currently under development and is considered experimental. - To enable it, set `ENV.EXPERIMENTAL_CONTROL_HELPER = true` before requiring Ember. - - For example if you had this `author` template. - - ```handlebars -
    - Written by {{firstName}} {{lastName}}. - Total Posts: {{postCount}} -
    - ``` - - You could render it inside the `post` template using the `control` helper. - - ```handlebars -
    -

    {{title}}

    -
    {{body}}
    - {{control "author" author}} -
    - ``` - - @method control - @for Ember.Handlebars.helpers - @param {String} path - @param {String} modelPath - @param {Hash} options - @return {String} HTML string - */ - Ember.Handlebars.registerHelper('control', function(path, modelPath, options) { - if (arguments.length === 2) { - options = modelPath; - modelPath = undefined; - } - - var model; - - if (modelPath) { - model = Ember.Handlebars.get(this, modelPath, options); - } - - var controller = options.data.keywords.controller, - view = options.data.keywords.view, - children = get(controller, '_childContainers'), - controlID = options.hash.controlID, - container, subContainer; - - if (children.hasOwnProperty(controlID)) { - subContainer = children[controlID]; - } else { - container = get(controller, 'container'), - subContainer = container.child(); - children[controlID] = subContainer; - } - - var normalizedPath = path.replace(/\//g, '.'); - - var childView = subContainer.lookup('view:' + normalizedPath) || subContainer.lookup('view:default'), - childController = subContainer.lookup('controller:' + normalizedPath), - childTemplate = subContainer.lookup('template:' + path); - - Ember.assert("Could not find controller for path: " + normalizedPath, childController); - Ember.assert("Could not find view for path: " + normalizedPath, childView); - - set(childController, 'target', controller); - set(childController, 'model', model); - - options.hash.template = childTemplate; - options.hash.controller = childController; - - function observer() { - var model = Ember.Handlebars.get(this, modelPath, options); - set(childController, 'model', model); - childView.rerender(); - } - - if (modelPath) { - Ember.addObserver(this, modelPath, observer); - childView.one('willDestroyElement', this, function() { - Ember.removeObserver(this, modelPath, observer); - }); - } - - Ember.Handlebars.helpers.view.call(this, childView, options); - }); -} - -})(); - - - (function() { })(); @@ -34784,8 +36812,8 @@ Ember.ControllerMixin.reopen({ be either a single route or route path: ```javascript - aController.transitionToRoute('blogPosts'); - aController.transitionToRoute('blogPosts.recentEntries'); + aController.transitionToRoute('blogPosts'); + aController.transitionToRoute('blogPosts.recentEntries'); ``` Optionally supply a model for the route in question. The model @@ -34793,19 +36821,18 @@ Ember.ControllerMixin.reopen({ the route: ```javascript - aController.transitionToRoute('blogPost', aPost); + aController.transitionToRoute('blogPost', aPost); ``` Multiple models will be applied last to first recursively up the resource tree. ```javascript + this.resource('blogPost', {path:':blogPostId'}, function(){ + this.resource('blogComment', {path: ':blogCommentId'}); + }); - this.resource('blogPost', {path:':blogPostId'}, function(){ - this.resource('blogComment', {path: ':blogCommentId'}); - }); - - aController.transitionToRoute('blogComment', aPost, aComment); + aController.transitionToRoute('blogComment', aPost, aComment); ``` See also 'replaceRoute'. @@ -34839,8 +36866,8 @@ Ember.ControllerMixin.reopen({ Beside that, it is identical to `transitionToRoute` in all other respects. ```javascript - aController.replaceRoute('blogPosts'); - aController.replaceRoute('blogPosts.recentEntries'); + aController.replaceRoute('blogPosts'); + aController.replaceRoute('blogPosts.recentEntries'); ``` Optionally supply a model for the route in question. The model @@ -34848,19 +36875,18 @@ Ember.ControllerMixin.reopen({ the route: ```javascript - aController.replaceRoute('blogPost', aPost); + aController.replaceRoute('blogPost', aPost); ``` Multiple models will be applied last to first recursively up the resource tree. ```javascript + this.resource('blogPost', {path:':blogPostId'}, function(){ + this.resource('blogComment', {path: ':blogCommentId'}); + }); - this.resource('blogPost', {path:':blogPostId'}, function(){ - this.resource('blogComment', {path: ':blogCommentId'}); - }); - - aController.replaceRoute('blogComment', aPost, aComment); + aController.replaceRoute('blogComment', aPost, aComment); ``` @param {String} name the name of the route @@ -34926,9 +36952,11 @@ Ember.View.reopen({ // The html for myView now looks like: //
    Child view:
    - myView.connectOutlet('main', Ember.View.extend({ + var FooView = Ember.View.extend({ template: Ember.Handlebars.compile('

    Foo

    ') - })); + }); + var fooView = FooView.create(); + myView.connectOutlet('main', fooView); // The html for myView now looks like: //
    Child view: //

    Foo

    @@ -34961,12 +36989,11 @@ Ember.View.reopen({ }, /** - @private - Determines if the view has already been created by checking if the view has the same constructor, template, and context as the view in the `_outlets` object. + @private @method _hasEquivalentView @param {String} outletName The name of the outlet we are checking @param {Object} view An Ember.View @@ -34994,9 +37021,11 @@ Ember.View.reopen({ // myView's html: //
    Child view:
    - myView.connectOutlet('main', Ember.View.extend({ + var FooView = Ember.View.extend({ template: Ember.Handlebars.compile('

    Foo

    ') - })); + }); + var fooView = FooView.create(); + myView.connectOutlet('main', fooView); // myView's html: //
    Child view: //

    Foo

    @@ -35019,11 +37048,10 @@ Ember.View.reopen({ }, /** - @private - Gets an outlet that is pending disconnection and then nullifys the object on the `_outlet` object. + @private @method _finishDisconnections */ _finishDisconnections: function() { @@ -35068,28 +37096,76 @@ queues.splice(indexOf.call(queues, 'actions') + 1, 0, 'routerTransitions'); var get = Ember.get, set = Ember.set; -/* - This file implements the `location` API used by Ember's router. - - That API is: - - getURL: returns the current URL - setURL(path): sets the current URL - replaceURL(path): replace the current URL (optional) - onUpdateURL(callback): triggers the callback when the URL changes - formatURL(url): formats `url` to be placed into `href` attribute - - Calling setURL or replaceURL will not trigger onUpdateURL callbacks. - - TODO: This should perhaps be moved so that it's visible in the doc output. -*/ - /** Ember.Location returns an instance of the correct implementation of the `location` API. - You can pass it a `implementation` ('hash', 'history', 'none') to force a - particular implementation. + ## Implementations + + You can pass an implementation name (`hash`, `history`, `none`) to force a + particular implementation to be used in your application. + + ### HashLocation + + Using `HashLocation` results in URLs with a `#` (hash sign) separating the + server side URL portion of the URL from the portion that is used by Ember. + This relies upon the `hashchange` event existing in the browser. + + Example: + + ```javascript + App.Router.map(function() { + this.resource('posts', function() { + this.route('new'); + }); + }); + + App.Router.reopen({ + location: 'hash' + }); + ``` + + This will result in a posts.new url of `/#/posts/new`. + + ### HistoryLocation + + Using `HistoryLocation` results in URLs that are indistinguishable from a + standard URL. This relies upon the browser's `history` API. + + Example: + + ```javascript + App.Router.map(function() { + this.resource('posts', function() { + this.route('new'); + }); + }); + + App.Router.reopen({ + location: 'history' + }); + ``` + + This will result in a posts.new url of `/posts/new`. + + ### NoneLocation + + Using `NoneLocation` causes Ember to not store the applications URL state + in the actual URL. This is generally used for testing purposes, and is one + of the changes made when calling `App.setupForTesting()`. + + ## Location API + + Each location implementation must provide the following methods: + + * implementation: returns the string name used to reference the implementation. + * getURL: returns the current URL. + * setURL(path): sets the current URL. + * replaceURL(path): replace the current URL (optional). + * onUpdateURL(callback): triggers the callback when the URL changes. + * formatURL(url): formats `url` to be placed into `href` attribute. + + Calling setURL or replaceURL will not trigger onUpdateURL callbacks. @class Location @namespace Ember @@ -35097,20 +37173,24 @@ var get = Ember.get, set = Ember.set; */ Ember.Location = { /** - Create an instance of a an implementation of the `location` API. Requires - an options object with an `implementation` property. + This is deprecated in favor of using the container to lookup the location + implementation as desired. - Example + For example: ```javascript - var hashLocation = Ember.Location.create({implementation: 'hash'}); - var historyLocation = Ember.Location.create({implementation: 'history'}); - var noneLocation = Ember.Location.create({implementation: 'none'}); + // Given a location registered as follows: + container.register('location:history-test', HistoryTestLocation); + + // You could create a new instance via: + container.lookup('location:history-test'); ``` @method create @param {Object} options @return {Object} an instance of an implementation of the `location` API + @deprecated Use the container to lookup the location implementation that you + need. */ create: function(options) { var implementation = options && options.implementation; @@ -35123,23 +37203,26 @@ Ember.Location = { }, /** - Registers a class that implements the `location` API with an implementation - name. This implementation name can then be specified by the location property on - the application's router class. + This is deprecated in favor of using the container to register the + location implementation as desired. - Example + Example: ```javascript - Ember.Location.registerImplementation('history', Ember.HistoryLocation); + Application.initializer({ + name: "history-test-location", - App.Router.reopen({ - location: 'history' + initialize: function(container, application) { + application.register('location:history-test', HistoryTestLocation); + } }); ``` - @method registerImplementation - @param {String} name - @param {Object} implementation of the `location` API + @method registerImplementation + @param {String} name + @param {Object} implementation of the `location` API + @deprecated Register your custom location implementation with the + container directly. */ registerImplementation: function(name, implementation) { this.implementations[name] = implementation; @@ -35174,10 +37257,9 @@ Ember.NoneLocation = Ember.Object.extend({ path: '', /** - @private - Returns the current path. + @private @method getURL @return {String} path */ @@ -35186,11 +37268,10 @@ Ember.NoneLocation = Ember.Object.extend({ }, /** - @private - Set the path and remembers what was set. Using this method to change the path will not invoke the `updateURL` callback. + @private @method setURL @param path {String} */ @@ -35199,12 +37280,11 @@ Ember.NoneLocation = Ember.Object.extend({ }, /** - @private - Register a callback to be invoked when the path changes. These callbacks will execute when the user presses the back or forward button, but not after `setURL` is invoked. + @private @method onUpdateURL @param callback {Function} */ @@ -35213,10 +37293,9 @@ Ember.NoneLocation = Ember.Object.extend({ }, /** - @private - Sets the path and calls the `updateURL` callback. + @private @method handleURL @param callback {Function} */ @@ -35226,14 +37305,13 @@ Ember.NoneLocation = Ember.Object.extend({ }, /** - @private - Given a URL, formats it to be placed into the page as part of an element's `href` attribute. This is used, for example, when using the {{action}} helper to generate a URL based on an event. + @private @method formatURL @param url {String} @return {String} url @@ -35261,8 +37339,8 @@ Ember.Location.registerImplementation('none', Ember.NoneLocation); var get = Ember.get, set = Ember.set; /** - Ember.HashLocation implements the location API using the browser's - hash. At present, it relies on a hashchange event existing in the + `Ember.HashLocation` implements the location API using the browser's + hash. At present, it relies on a `hashchange` event existing in the browser. @class HashLocation @@ -35276,10 +37354,9 @@ Ember.HashLocation = Ember.Object.extend({ }, /** - @private - Returns the current `location.hash`, minus the '#' at the front. + @private @method getURL */ getURL: function() { @@ -35288,12 +37365,11 @@ Ember.HashLocation = Ember.Object.extend({ }, /** - @private - Set the `location.hash` and remembers what was set. This prevents `onUpdateURL` callbacks from triggering when the hash was set by `HashLocation`. + @private @method setURL @param path {String} */ @@ -35303,11 +37379,10 @@ Ember.HashLocation = Ember.Object.extend({ }, /** - @private - Uses location.replace to update the url without a page reload or history modification. + @private @method replaceURL @param path {String} */ @@ -35316,12 +37391,11 @@ Ember.HashLocation = Ember.Object.extend({ }, /** - @private - Register a callback to be invoked when the hash changes. These callbacks will execute when the user presses the back or forward button, but not after `setURL` is invoked. + @private @method onUpdateURL @param callback {Function} */ @@ -35342,14 +37416,13 @@ Ember.HashLocation = Ember.Object.extend({ }, /** - @private - Given a URL, formats it to be placed into the page as part of an element's `href` attribute. This is used, for example, when using the {{action}} helper to generate a URL based on an event. + @private @method formatURL @param url {String} */ @@ -35358,10 +37431,9 @@ Ember.HashLocation = Ember.Object.extend({ }, /** - @private - Cleans up the HashLocation event listener. + @private @method willDestroy */ willDestroy: function() { @@ -35399,14 +37471,12 @@ Ember.HistoryLocation = Ember.Object.extend({ init: function() { set(this, 'location', get(this, 'location') || window.location); - this.initState(); }, /** - @private - Used to set state on first call to setURL + @private @method initState */ initState: function() { @@ -35423,10 +37493,9 @@ Ember.HistoryLocation = Ember.Object.extend({ rootURL: '/', /** + Returns the current `location.pathname` without `rootURL`. + @private - - Returns the current `location.pathname` without rootURL - @method getURL @return url {String} */ @@ -35443,10 +37512,9 @@ Ember.HistoryLocation = Ember.Object.extend({ }, /** - @private - Uses `history.pushState` to update the url without a page reload. + @private @method setURL @param path {String} */ @@ -35460,11 +37528,10 @@ Ember.HistoryLocation = Ember.Object.extend({ }, /** - @private - Uses `history.replaceState` to update the url without a page reload or history modification. + @private @method replaceURL @param path {String} */ @@ -35478,12 +37545,11 @@ Ember.HistoryLocation = Ember.Object.extend({ }, /** - @private - Get the current `history.state` Polyfill checks for native browser support and falls back to retrieving from a private _historyState variable + @private @method getState @return state {Object} */ @@ -35492,10 +37558,9 @@ Ember.HistoryLocation = Ember.Object.extend({ }, /** + Pushes a new state. + @private - - Pushes a new state - @method pushState @param path {String} */ @@ -35514,10 +37579,9 @@ Ember.HistoryLocation = Ember.Object.extend({ }, /** + Replaces the current state. + @private - - Replaces the current state - @method replaceState @param path {String} */ @@ -35536,11 +37600,10 @@ Ember.HistoryLocation = Ember.Object.extend({ }, /** - @private - Register a callback to be invoked whenever the browser history changes, including using forward and back buttons. + @private @method onUpdateURL @param callback {Function} */ @@ -35559,10 +37622,9 @@ Ember.HistoryLocation = Ember.Object.extend({ }, /** - @private - Used when using `{{action}}` helper. The url is always appended to the rootURL. + @private @method formatURL @param url {String} @return formatted url {String} @@ -35578,10 +37640,9 @@ Ember.HistoryLocation = Ember.Object.extend({ }, /** - @private - Cleans up the HistoryLocation event listener. + @private @method willDestroy */ willDestroy: function() { @@ -35818,7 +37879,10 @@ Ember.DefaultResolver = Ember.Object.extend({ type = split[0], name = split[1]; - Ember.assert("Tried to normalize a container name without a colon (:) in it. You probably tried to lookup a name that did not contain a type, a colon, and a name. A proper lookup name would be `view:post`.", split.length === 2); + Ember.assert("Tried to normalize a container name without a colon (:) in " + + "it. You probably tried to lookup a name that did not contain " + + "a type, a colon, and a name. A proper lookup name would be " + + "`view:post`.", split.length === 2); if (type !== 'template') { var result = name; @@ -35852,7 +37916,7 @@ Ember.DefaultResolver = Ember.Object.extend({ typeSpecificResolveMethod = this[parsedName.resolveMethodName]; if (!parsedName.name || !parsedName.type) { - throw new TypeError("Invalid fullName: `" + fullName + "`, must of of the form `type:name` "); + throw new TypeError("Invalid fullName: `" + fullName + "`, must be of the form `type:name` "); } if (typeSpecificResolveMethod) { @@ -36124,7 +38188,7 @@ DeprecatedContainer.prototype = { App = Ember.Application.create({ customEvents: { // add support for the paste event - 'paste: "paste" + paste: "paste" } }); ``` @@ -36246,7 +38310,7 @@ var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin App = Ember.Application.create({ customEvents: { // add support for the paste event - 'paste: "paste" + paste: "paste" } }); ``` @@ -36288,13 +38352,12 @@ var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin }, /** - @private - Build the container for the current application. Also register a default application view in case the application itself does not. + @private @method buildContainer @return {Ember.Container} the configured container */ @@ -36305,8 +38368,6 @@ var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin }, /** - @private - If the application has not opted out of routing and has not explicitly defined a router, supply a default router for the application author to configure. @@ -36321,6 +38382,7 @@ var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin }); ``` + @private @method defaultRouter @return {Ember.Router} the default router */ @@ -36338,8 +38400,6 @@ var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin }, /** - @private - Automatically initialize the application once the DOM has become ready. @@ -36352,6 +38412,7 @@ var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin `advanceReadiness()` once all of your code has finished loading. + @private @method scheduleInitialize */ scheduleInitialize: function() { @@ -36360,7 +38421,7 @@ var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin if (!this.$ || this.$.isReady) { Ember.run.schedule('actions', self, '_initialize'); } else { - this.$().ready(function() { + this.$().ready(function runInitialize() { Ember.run(self, '_initialize'); }); } @@ -36423,12 +38484,12 @@ var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin App.Person = Ember.Object.extend({}); App.Orange = Ember.Object.extend({}); App.Email = Ember.Object.extend({}); - App.Session = Ember.Object.create({}); + App.session = Ember.Object.create({}); App.register('model:user', App.Person, {singleton: false }); App.register('fruit:favorite', App.Orange); App.register('communication:main', App.Email, {singleton: false}); - App.register('session', App.Session, {instantiate: false}); + App.register('session', App.session, {instantiate: false}); ``` @method register @@ -36462,28 +38523,26 @@ var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin }, /** - @private - @deprecated - Calling initialize manually is not supported. Please see Ember.Application#advanceReadiness and Ember.Application#deferReadiness. + @private + @deprecated @method initialize **/ initialize: function() { Ember.deprecate('Calling initialize manually is not supported. Please see Ember.Application#advanceReadiness and Ember.Application#deferReadiness'); }, /** - @private - Initialize the application. This happens automatically. Run any initializers and run the application load hook. These hooks may choose to defer readiness. For example, an authentication hook might want to defer readiness until the auth token has been retrieved. + @private @method _initialize */ _initialize: function() { @@ -36637,12 +38696,11 @@ var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin }, /** - @private - Setup up the event dispatcher to receive events on the application's `rootElement` with any registered `customEvents`. + @private @method setupEventDispatcher */ setupEventDispatcher: function() { @@ -36655,11 +38713,10 @@ var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin }, /** - @private - trigger a new call to `route` whenever the URL changes. If the application has a router, use it to route to the current URL, and + @private @method startRouting @property router {Ember.Router} */ @@ -36732,8 +38789,6 @@ Ember.Application.reopenClass({ }, /** - @private - This creates a container with the default Ember naming conventions. It also configures the container: @@ -36751,6 +38806,7 @@ Ember.Application.reopenClass({ * the application view receives the application template as its `defaultTemplate` property + @private @method buildContainer @static @param {Ember.Application} namespace the application to build the @@ -36771,10 +38827,7 @@ Ember.Application.reopenClass({ container.optionsForType('component', { singleton: false }); container.optionsForType('view', { singleton: false }); container.optionsForType('template', { instantiate: false }); - - - container.optionsForType('helper', { instantiate: false }); - + container.optionsForType('helper', { instantiate: false }); container.register('application:main', namespace, { instantiate: false }); @@ -36797,8 +38850,6 @@ Ember.Application.reopenClass({ }); /** - @private - This function defines the default lookup rules for container lookups: * templates are looked up on `Ember.TEMPLATES` @@ -36809,6 +38860,7 @@ Ember.Application.reopenClass({ This allows the application to register default injections in the container that could be overridden by the normal naming convention. + @private @method resolverFor @param {Ember.Namespace} namespace the namespace to look for classes @return {*} the resolved value for a given lookup @@ -36868,21 +38920,52 @@ Ember.runLoadHooks('Ember.Application', Ember.Application); var get = Ember.get, set = Ember.set; function verifyNeedsDependencies(controller, container, needs) { - var dependency, i, l; + var dependency, i, l, missing = []; for (i=0, l=needs.length; i 1 ? 'they' : 'it') + " could not be found"); + } } +var defaultControllersComputedProperty = Ember.computed(function() { + var controller = this; + + return { + needs: get(controller, 'needs'), + container: get(controller, 'container'), + unknownProperty: function(controllerName) { + var needs = this.needs, + dependency, i, l; + for (i=0, l=needs.length; i 0) { - Ember.assert(' `' + Ember.inspect(this) + ' specifies `needs`, but does not have a container. Please ensure this controller was instantiated with a container.', this.container); + Ember.assert(' `' + Ember.inspect(this) + ' specifies `needs`, but does ' + + "not have a container. Please ensure this controller was " + + "instantiated with a container.", + this.container || Ember.meta(this, false).descs.controllers !== defaultControllersComputedProperty); - verifyNeedsDependencies(this, this.container, needs); + if (this.container) { + verifyNeedsDependencies(this, this.container, needs); + } // if needs then initialize controllers proxy get(this, 'controllers'); @@ -36963,27 +39072,7 @@ Ember.ControllerMixin.reopen({ @property {Object} controllers @default null */ - controllers: Ember.computed(function() { - var controller = this; - - return { - needs: get(controller, 'needs'), - container: get(controller, 'container'), - unknownProperty: function(controllerName) { - var needs = this.needs, - dependency, i, l; - for (i=0, l=needs.length; i= 1.0.0' -}; - -Handlebars.helpers = {}; -Handlebars.partials = {}; - -var toString = Object.prototype.toString, - functionType = '[object Function]', - objectType = '[object Object]'; - -Handlebars.registerHelper = function(name, fn, inverse) { - if (toString.call(name) === objectType) { - if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); } - Handlebars.Utils.extend(this.helpers, name); - } else { - if (inverse) { fn.not = inverse; } - this.helpers[name] = fn; - } -}; - -Handlebars.registerPartial = function(name, str) { - if (toString.call(name) === objectType) { - Handlebars.Utils.extend(this.partials, name); - } else { - this.partials[name] = str; - } -}; - -Handlebars.registerHelper('helperMissing', function(arg) { - if(arguments.length === 2) { - return undefined; - } else { - throw new Error("Missing helper: '" + arg + "'"); - } -}); - -Handlebars.registerHelper('blockHelperMissing', function(context, options) { - var inverse = options.inverse || function() {}, fn = options.fn; - - var type = toString.call(context); - - if(type === functionType) { context = context.call(this); } - - if(context === true) { - return fn(this); - } else if(context === false || context == null) { - return inverse(this); - } else if(type === "[object Array]") { - if(context.length > 0) { - return Handlebars.helpers.each(context, options); - } else { - return inverse(this); - } - } else { - return fn(context); - } -}); - -Handlebars.K = function() {}; - -Handlebars.createFrame = Object.create || function(object) { - Handlebars.K.prototype = object; - var obj = new Handlebars.K(); - Handlebars.K.prototype = null; - return obj; -}; - -Handlebars.logger = { - DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3, - - methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'}, - - // can be overridden in the host environment - log: function(level, obj) { - if (Handlebars.logger.level <= level) { - var method = Handlebars.logger.methodMap[level]; - if (typeof console !== 'undefined' && console[method]) { - console[method].call(console, obj); - } - } - } -}; - -Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); }; - -Handlebars.registerHelper('each', function(context, options) { - var fn = options.fn, inverse = options.inverse; - var i = 0, ret = "", data; - - var type = toString.call(context); - if(type === functionType) { context = context.call(this); } - - if (options.data) { - data = Handlebars.createFrame(options.data); +/* exported Handlebars */ +var Handlebars = (function() { +// handlebars/safe-string.js +var __module4__ = (function() { + "use strict"; + var __exports__; + // Build out our basic SafeString type + function SafeString(string) { + this.string = string; } - if(context && typeof context === 'object') { - if(context instanceof Array){ - for(var j = context.length; i 2) { - expected.push("'" + this.terminals_[p] + "'"); - } - if (this.lexer.showPosition) { - errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; - } else { - errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'"); - } - this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); - } - } - if (action[0] instanceof Array && action.length > 1) { - throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); - } - switch (action[0]) { - case 1: - stack.push(symbol); - vstack.push(this.lexer.yytext); - lstack.push(this.lexer.yylloc); - stack.push(action[1]); - symbol = null; - if (!preErrorSymbol) { - yyleng = this.lexer.yyleng; - yytext = this.lexer.yytext; - yylineno = this.lexer.yylineno; - yyloc = this.lexer.yylloc; - if (recovering > 0) - recovering--; - } else { - symbol = preErrorSymbol; - preErrorSymbol = null; - } - break; - case 2: - len = this.productions_[action[1]][1]; - yyval.$ = vstack[vstack.length - len]; - yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column}; - if (ranges) { - yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; - } - r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); - if (typeof r !== "undefined") { - return r; - } - if (len) { - stack = stack.slice(0, -1 * len * 2); - vstack = vstack.slice(0, -1 * len); - lstack = lstack.slice(0, -1 * len); - } - stack.push(this.productions_[action[1]][0]); - vstack.push(yyval.$); - lstack.push(yyval._$); - newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; - stack.push(newState); - break; - case 3: - return true; - } - } - return true; -} -}; -/* Jison generated lexer */ -var lexer = (function(){ -var lexer = ({EOF:1, -parseError:function parseError(str, hash) { - if (this.yy.parser) { - this.yy.parser.parseError(str, hash); - } else { - throw new Error(str); - } - }, -setInput:function (input) { - this._input = input; - this._more = this._less = this.done = false; - this.yylineno = this.yyleng = 0; - this.yytext = this.matched = this.match = ''; - this.conditionStack = ['INITIAL']; - this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; - if (this.options.ranges) this.yylloc.range = [0,0]; - this.offset = 0; - return this; - }, -input:function () { - var ch = this._input[0]; - this.yytext += ch; - this.yyleng++; - this.offset++; - this.match += ch; - this.matched += ch; - var lines = ch.match(/(?:\r\n?|\n).*/g); - if (lines) { - this.yylineno++; - this.yylloc.last_line++; - } else { - this.yylloc.last_column++; - } - if (this.options.ranges) this.yylloc.range[1]++; - - this._input = this._input.slice(1); - return ch; - }, -unput:function (ch) { - var len = ch.length; - var lines = ch.split(/(?:\r\n?|\n)/g); - - this._input = ch + this._input; - this.yytext = this.yytext.substr(0, this.yytext.length-len-1); - //this.yyleng -= len; - this.offset -= len; - var oldLines = this.match.split(/(?:\r\n?|\n)/g); - this.match = this.match.substr(0, this.match.length-1); - this.matched = this.matched.substr(0, this.matched.length-1); - - if (lines.length-1) this.yylineno -= lines.length-1; - var r = this.yylloc.range; - - this.yylloc = {first_line: this.yylloc.first_line, - last_line: this.yylineno+1, - first_column: this.yylloc.first_column, - last_column: lines ? - (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length: - this.yylloc.first_column - len - }; - - if (this.options.ranges) { - this.yylloc.range = [r[0], r[0] + this.yyleng - len]; - } - return this; - }, -more:function () { - this._more = true; - return this; - }, -less:function (n) { - this.unput(this.match.slice(n)); - }, -pastInput:function () { - var past = this.matched.substr(0, this.matched.length - this.match.length); - return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); - }, -upcomingInput:function () { - var next = this.match; - if (next.length < 20) { - next += this._input.substr(0, 20-next.length); - } - return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); - }, -showPosition:function () { - var pre = this.pastInput(); - var c = new Array(pre.length + 1).join("-"); - return pre + this.upcomingInput() + "\n" + c+"^"; - }, -next:function () { - if (this.done) { - return this.EOF; - } - if (!this._input) this.done = true; - - var token, - match, - tempMatch, - index, - col, - lines; - if (!this._more) { - this.yytext = ''; - this.match = ''; - } - var rules = this._currentRules(); - for (var i=0;i < rules.length; i++) { - tempMatch = this._input.match(this.rules[rules[i]]); - if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { - match = tempMatch; - index = i; - if (!this.options.flex) break; - } - } - if (match) { - lines = match[0].match(/(?:\r\n?|\n).*/g); - if (lines) this.yylineno += lines.length; - this.yylloc = {first_line: this.yylloc.last_line, - last_line: this.yylineno+1, - first_column: this.yylloc.last_column, - last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length}; - this.yytext += match[0]; - this.match += match[0]; - this.matches = match; - this.yyleng = this.yytext.length; - if (this.options.ranges) { - this.yylloc.range = [this.offset, this.offset += this.yyleng]; - } - this._more = false; - this._input = this._input.slice(match[0].length); - this.matched += match[0]; - token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); - if (this.done && this._input) this.done = false; - if (token) return token; - else return; - } - if (this._input === "") { - return this.EOF; - } else { - return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), - {text: "", token: null, line: this.yylineno}); - } - }, -lex:function lex() { - var r = this.next(); - if (typeof r !== 'undefined') { - return r; - } else { - return this.lex(); - } - }, -begin:function begin(condition) { - this.conditionStack.push(condition); - }, -popState:function popState() { - return this.conditionStack.pop(); - }, -_currentRules:function _currentRules() { - return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; - }, -topState:function () { - return this.conditionStack[this.conditionStack.length-2]; - }, -pushState:function begin(condition) { - this.begin(condition); - }}); -lexer.options = {}; -lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { - -var YYSTATE=YY_START -switch($avoiding_name_collisions) { -case 0: yy_.yytext = "\\"; return 14; -break; -case 1: - if(yy_.yytext.slice(-1) !== "\\") this.begin("mu"); - if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu"); - if(yy_.yytext) return 14; - -break; -case 2: return 14; -break; -case 3: - if(yy_.yytext.slice(-1) !== "\\") this.popState(); - if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1); - return 14; - -break; -case 4: yy_.yytext = yy_.yytext.substr(0, yy_.yyleng-4); this.popState(); return 15; -break; -case 5: return 25; -break; -case 6: return 16; -break; -case 7: return 20; -break; -case 8: return 19; -break; -case 9: return 19; -break; -case 10: return 23; -break; -case 11: return 22; -break; -case 12: this.popState(); this.begin('com'); -break; -case 13: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15; -break; -case 14: return 22; -break; -case 15: return 37; -break; -case 16: return 36; -break; -case 17: return 36; -break; -case 18: return 40; -break; -case 19: /*ignore whitespace*/ -break; -case 20: this.popState(); return 24; -break; -case 21: this.popState(); return 18; -break; -case 22: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 31; -break; -case 23: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\'/g,"'"); return 31; -break; -case 24: return 38; -break; -case 25: return 33; -break; -case 26: return 33; -break; -case 27: return 32; -break; -case 28: return 36; -break; -case 29: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 36; -break; -case 30: return 'INVALID'; -break; -case 31: return 5; -break; -} -}; -lexer.rules = [/^(?:\\\\(?=(\{\{)))/,/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{>)/,/^(?:\{\{#)/,/^(?:\{\{\/)/,/^(?:\{\{\^)/,/^(?:\{\{\s*else\b)/,/^(?:\{\{\{)/,/^(?:\{\{&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{)/,/^(?:=)/,/^(?:\.(?=[}\/ ]))/,/^(?:\.\.)/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}\}\})/,/^(?:\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=[}\s]))/,/^(?:false(?=[}\s]))/,/^(?:-?[0-9]+(?=[}\s]))/,/^(?:[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.]))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/]; -lexer.conditions = {"mu":{"rules":[5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31],"inclusive":false},"emu":{"rules":[3],"inclusive":false},"com":{"rules":[4],"inclusive":false},"INITIAL":{"rules":[0,1,2,31],"inclusive":true}}; -return lexer;})() -parser.lexer = lexer; -function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; -return new Parser; -})();; -// lib/handlebars/compiler/base.js - -Handlebars.Parser = handlebars; - -Handlebars.parse = function(input) { - - // Just return if an already-compile AST was passed in. - if(input.constructor === Handlebars.AST.ProgramNode) { return input; } - - Handlebars.Parser.yy = Handlebars.AST; - return Handlebars.Parser.parse(input); -}; -; -// lib/handlebars/compiler/ast.js -Handlebars.AST = {}; - -Handlebars.AST.ProgramNode = function(statements, inverse) { - this.type = "program"; - this.statements = statements; - if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); } -}; - -Handlebars.AST.MustacheNode = function(rawParams, hash, unescaped) { - this.type = "mustache"; - this.escaped = !unescaped; - this.hash = hash; - - var id = this.id = rawParams[0]; - var params = this.params = rawParams.slice(1); - - // a mustache is an eligible helper if: - // * its id is simple (a single part, not `this` or `..`) - var eligibleHelper = this.eligibleHelper = id.isSimple; - - // a mustache is definitely a helper if: - // * it is an eligible helper, and - // * it has at least one parameter or hash segment - this.isHelper = eligibleHelper && (params.length || hash); - - // if a mustache is an eligible helper but not a definite - // helper, it is ambiguous, and will be resolved in a later - // pass or at runtime. -}; - -Handlebars.AST.PartialNode = function(partialName, context) { - this.type = "partial"; - this.partialName = partialName; - this.context = context; -}; - -Handlebars.AST.BlockNode = function(mustache, program, inverse, close) { - var verifyMatch = function(open, close) { - if(open.original !== close.original) { - throw new Handlebars.Exception(open.original + " doesn't match " + close.original); - } + SafeString.prototype.toString = function() { + return "" + this.string; }; - verifyMatch(mustache.id, close); - this.type = "block"; - this.mustache = mustache; - this.program = program; - this.inverse = inverse; + __exports__ = SafeString; + return __exports__; +})(); - if (this.inverse && !this.program) { - this.isInverse = true; - } -}; +// handlebars/utils.js +var __module3__ = (function(__dependency1__) { + "use strict"; + var __exports__ = {}; + /*jshint -W004 */ + var SafeString = __dependency1__; -Handlebars.AST.ContentNode = function(string) { - this.type = "content"; - this.string = string; -}; + var escape = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + "`": "`" + }; -Handlebars.AST.HashNode = function(pairs) { - this.type = "hash"; - this.pairs = pairs; -}; + var badChars = /[&<>"'`]/g; + var possible = /[&<>"'`]/; -Handlebars.AST.IdNode = function(parts) { - this.type = "ID"; - - var original = "", - dig = [], - depth = 0; - - for(var i=0,l=parts.length; i 0) { throw new Handlebars.Exception("Invalid path: " + original); } - else if (part === "..") { depth++; } - else { this.isScoped = true; } - } - else { dig.push(part); } + function escapeChar(chr) { + return escape[chr] || "&"; } - this.original = original; - this.parts = dig; - this.string = dig.join('.'); - this.depth = depth; - - // an ID is simple if it only has one part, and that part is not - // `..` or `this`. - this.isSimple = parts.length === 1 && !this.isScoped && depth === 0; - - this.stringModeValue = this.string; -}; - -Handlebars.AST.PartialNameNode = function(name) { - this.type = "PARTIAL_NAME"; - this.name = name.original; -}; - -Handlebars.AST.DataNode = function(id) { - this.type = "DATA"; - this.id = id; -}; - -Handlebars.AST.StringNode = function(string) { - this.type = "STRING"; - this.original = - this.string = - this.stringModeValue = string; -}; - -Handlebars.AST.IntegerNode = function(integer) { - this.type = "INTEGER"; - this.original = - this.integer = integer; - this.stringModeValue = Number(integer); -}; - -Handlebars.AST.BooleanNode = function(bool) { - this.type = "BOOLEAN"; - this.bool = bool; - this.stringModeValue = bool === "true"; -}; - -Handlebars.AST.CommentNode = function(comment) { - this.type = "comment"; - this.comment = comment; -}; -; -// lib/handlebars/utils.js - -var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; - -Handlebars.Exception = function(message) { - var tmp = Error.prototype.constructor.apply(this, arguments); - - // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. - for (var idx = 0; idx < errorProps.length; idx++) { - this[errorProps[idx]] = tmp[errorProps[idx]]; - } -}; -Handlebars.Exception.prototype = new Error(); - -// Build out our basic SafeString type -Handlebars.SafeString = function(string) { - this.string = string; -}; -Handlebars.SafeString.prototype.toString = function() { - return this.string.toString(); -}; - -var escape = { - "&": "&", - "<": "<", - ">": ">", - '"': """, - "'": "'", - "`": "`" -}; - -var badChars = /[&<>"'`]/g; -var possible = /[&<>"'`]/; - -var escapeChar = function(chr) { - return escape[chr] || "&"; -}; - -Handlebars.Utils = { - extend: function(obj, value) { + function extend(obj, value) { for(var key in value) { - if(value.hasOwnProperty(key)) { + if(Object.prototype.hasOwnProperty.call(value, key)) { obj[key] = value[key]; } } - }, + } - escapeExpression: function(string) { + __exports__.extend = extend;var toString = Object.prototype.toString; + __exports__.toString = toString; + // Sourced from lodash + // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt + var isFunction = function(value) { + return typeof value === 'function'; + }; + // fallback for older versions of Chrome and Safari + if (isFunction(/x/)) { + isFunction = function(value) { + return typeof value === 'function' && toString.call(value) === '[object Function]'; + }; + } + var isFunction; + __exports__.isFunction = isFunction; + var isArray = Array.isArray || function(value) { + return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false; + }; + __exports__.isArray = isArray; + + function escapeExpression(string) { // don't escape SafeStrings, since they're already safe - if (string instanceof Handlebars.SafeString) { + if (string instanceof SafeString) { return string.toString(); - } else if (string == null || string === false) { + } else if (!string && string !== 0) { return ""; } // Force a string conversion as this will be done by the append regardless and // the regex test will do this transparently behind the scenes, causing issues if // an object's to string has escaped characters in it. - string = string.toString(); + string = "" + string; if(!possible.test(string)) { return string; } return string.replace(badChars, escapeChar); - }, + } - isEmpty: function(value) { + __exports__.escapeExpression = escapeExpression;function isEmpty(value) { if (!value && value !== 0) { return true; - } else if(toString.call(value) === "[object Array]" && value.length === 0) { + } else if (isArray(value) && value.length === 0) { return true; } else { return false; } } -}; -; -// lib/handlebars/compiler/compiler.js -/*jshint eqnull:true*/ -var Compiler = Handlebars.Compiler = function() {}; -var JavaScriptCompiler = Handlebars.JavaScriptCompiler = function() {}; + __exports__.isEmpty = isEmpty; + return __exports__; +})(__module4__); -// the foundHelper register will disambiguate helper lookup from finding a -// function in a context. This is necessary for mustache compatibility, which -// requires that context functions in blocks are evaluated by blockHelperMissing, -// and then proceed as if the resulting value was provided to blockHelperMissing. +// handlebars/exception.js +var __module5__ = (function() { + "use strict"; + var __exports__; -Compiler.prototype = { - compiler: Compiler, + var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; - disassemble: function() { - var opcodes = this.opcodes, opcode, out = [], params, param; + function Exception(/* message */) { + var tmp = Error.prototype.constructor.apply(this, arguments); - for (var i=0, l=opcodes.length; i 0) { - this.source[1] = this.source[1] + ", " + locals.join(", "); - } - - // Generate minimizer alias mappings - if (!this.isChild) { - for (var alias in this.context.aliases) { - if (this.context.aliases.hasOwnProperty(alias)) { - this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; - } - } + // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. + for (var idx = 0; idx < errorProps.length; idx++) { + this[errorProps[idx]] = tmp[errorProps[idx]]; } - - if (this.source[1]) { - this.source[1] = "var " + this.source[1].substring(2) + ";"; - } - - // Merge children - if (!this.isChild) { - this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; - } - - if (!this.environment.isSimple) { - this.source.push("return buffer;"); - } - - var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"]; - - for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } - return this.topStackName(); - }, - topStackName: function() { - return "stack" + this.stackSlot; - }, - flushInline: function() { - var inlineStack = this.inlineStack; - if (inlineStack.length) { - this.inlineStack = []; - for (var i = 0, len = inlineStack.length; i < len; i++) { - var entry = inlineStack[i]; - if (entry instanceof Literal) { - this.compileStack.push(entry); - } else { - this.pushStack(entry); - } - } - } - }, - isInline: function() { - return this.inlineStack.length; - }, - - popStack: function(wrapped) { - var inline = this.isInline(), - item = (inline ? this.inlineStack : this.compileStack).pop(); - - if (!wrapped && (item instanceof Literal)) { - return item.value; - } else { - if (!inline) { - this.stackSlot--; - } - return item; - } - }, - - topStack: function(wrapped) { - var stack = (this.isInline() ? this.inlineStack : this.compileStack), - item = stack[stack.length - 1]; - - if (!wrapped && (item instanceof Literal)) { - return item.value; - } else { - return item; - } - }, - - quotedString: function(str) { - return '"' + str - .replace(/\\/g, '\\\\') - .replace(/"/g, '\\"') - .replace(/\n/g, '\\n') - .replace(/\r/g, '\\r') - .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 - .replace(/\u2029/g, '\\u2029') + '"'; - }, - - setupHelper: function(paramSize, name, missingParams) { - var params = []; - this.setupParams(paramSize, params, missingParams); - var foundHelper = this.nameLookup('helpers', name, 'helper'); - - return { - params: params, - name: foundHelper, - callParams: ["depth0"].concat(params).join(", "), - helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ") - }; - }, - - // the params and contexts arguments are passed in arrays - // to fill in - setupParams: function(paramSize, params, useRegister) { - var options = [], contexts = [], types = [], param, inverse, program; - - options.push("hash:" + this.popStack()); - - inverse = this.popStack(); - program = this.popStack(); - - // Avoid setting fn and inverse if neither are set. This allows - // helpers to do a check for `if (options.fn)` - if (program || inverse) { - if (!program) { - this.context.aliases.self = "this"; - program = "self.noop"; - } - - if (!inverse) { - this.context.aliases.self = "this"; - inverse = "self.noop"; - } - - options.push("inverse:" + inverse); - options.push("fn:" + program); - } - - for(var i=0; i= 1.0.0' }; -}; + __exports__.REVISION_CHANGES = REVISION_CHANGES; + var isArray = Utils.isArray, + isFunction = Utils.isFunction, + toString = Utils.toString, + objectType = '[object Object]'; -; -// lib/handlebars/runtime.js + function HandlebarsEnvironment(helpers, partials) { + this.helpers = helpers || {}; + this.partials = partials || {}; + + registerDefaultHelpers(this); + } + + __exports__.HandlebarsEnvironment = HandlebarsEnvironment;HandlebarsEnvironment.prototype = { + constructor: HandlebarsEnvironment, + + logger: logger, + log: log, + + registerHelper: function(name, fn, inverse) { + if (toString.call(name) === objectType) { + if (inverse || fn) { throw new Exception('Arg not supported with multiple helpers'); } + Utils.extend(this.helpers, name); + } else { + if (inverse) { fn.not = inverse; } + this.helpers[name] = fn; + } + }, + + registerPartial: function(name, str) { + if (toString.call(name) === objectType) { + Utils.extend(this.partials, name); + } else { + this.partials[name] = str; + } + } + }; + + function registerDefaultHelpers(instance) { + instance.registerHelper('helperMissing', function(arg) { + if(arguments.length === 2) { + return undefined; + } else { + throw new Error("Missing helper: '" + arg + "'"); + } + }); + + instance.registerHelper('blockHelperMissing', function(context, options) { + var inverse = options.inverse || function() {}, fn = options.fn; + + if (isFunction(context)) { context = context.call(this); } + + if(context === true) { + return fn(this); + } else if(context === false || context == null) { + return inverse(this); + } else if (isArray(context)) { + if(context.length > 0) { + return instance.helpers.each(context, options); + } else { + return inverse(this); + } + } else { + return fn(context); + } + }); + + instance.registerHelper('each', function(context, options) { + var fn = options.fn, inverse = options.inverse; + var i = 0, ret = "", data; + + if (isFunction(context)) { context = context.call(this); } + + if (options.data) { + data = createFrame(options.data); + } + + if(context && typeof context === 'object') { + if (isArray(context)) { + for(var j = context.length; i 0) { throw new Exception("Invalid path: " + original); } + else if (part === "..") { depth++; } + else { this.isScoped = true; } + } + else { dig.push(part); } + } + + this.original = original; + this.parts = dig; + this.string = dig.join('.'); + this.depth = depth; + + // an ID is simple if it only has one part, and that part is not + // `..` or `this`. + this.isSimple = parts.length === 1 && !this.isScoped && depth === 0; + + this.stringModeValue = this.string; + }, + + PartialNameNode: function(name) { + this.type = "PARTIAL_NAME"; + this.name = name.original; + }, + + DataNode: function(id) { + this.type = "DATA"; + this.id = id; + }, + + StringNode: function(string) { + this.type = "STRING"; + this.original = + this.string = + this.stringModeValue = string; + }, + + IntegerNode: function(integer) { + this.type = "INTEGER"; + this.original = + this.integer = integer; + this.stringModeValue = Number(integer); + }, + + BooleanNode: function(bool) { + this.type = "BOOLEAN"; + this.bool = bool; + this.stringModeValue = bool === "true"; + }, + + CommentNode: function(comment) { + this.type = "comment"; + this.comment = comment; + } + }; + + // Must be exported as an object rather than the root of the module as the jison lexer + // most modify the object to operate properly. + __exports__ = AST; + return __exports__; +})(__module5__); + +// handlebars/compiler/parser.js +var __module9__ = (function() { + "use strict"; + var __exports__; + /* jshint ignore:start */ + /* Jison generated parser */ + var handlebars = (function(){ + var parser = {trace: function trace() { }, + yy: {}, + symbols_: {"error":2,"root":3,"statements":4,"EOF":5,"program":6,"simpleInverse":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"inMustache":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"CLOSE_UNESCAPED":24,"OPEN_PARTIAL":25,"partialName":26,"partial_option0":27,"inMustache_repetition0":28,"inMustache_option0":29,"dataName":30,"param":31,"STRING":32,"INTEGER":33,"BOOLEAN":34,"hash":35,"hash_repetition_plus0":36,"hashSegment":37,"ID":38,"EQUALS":39,"DATA":40,"pathSegments":41,"SEP":42,"$accept":0,"$end":1}, + terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"CLOSE_UNESCAPED",25:"OPEN_PARTIAL",32:"STRING",33:"INTEGER",34:"BOOLEAN",38:"ID",39:"EQUALS",40:"DATA",42:"SEP"}, + productions_: [0,[3,2],[3,1],[6,2],[6,3],[6,2],[6,1],[6,1],[6,0],[4,1],[4,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,4],[7,2],[17,3],[17,1],[31,1],[31,1],[31,1],[31,1],[31,1],[35,1],[37,3],[26,1],[26,1],[26,1],[30,2],[21,1],[41,3],[41,1],[27,0],[27,1],[28,0],[28,2],[29,0],[29,1],[36,1],[36,2]], + performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { + + var $0 = $$.length - 1; + switch (yystate) { + case 1: return new yy.ProgramNode($$[$0-1]); + break; + case 2: return new yy.ProgramNode([]); + break; + case 3:this.$ = new yy.ProgramNode([], $$[$0-1], $$[$0]); + break; + case 4:this.$ = new yy.ProgramNode($$[$0-2], $$[$0-1], $$[$0]); + break; + case 5:this.$ = new yy.ProgramNode($$[$0-1], $$[$0], []); + break; + case 6:this.$ = new yy.ProgramNode($$[$0]); + break; + case 7:this.$ = new yy.ProgramNode([]); + break; + case 8:this.$ = new yy.ProgramNode([]); + break; + case 9:this.$ = [$$[$0]]; + break; + case 10: $$[$0-1].push($$[$0]); this.$ = $$[$0-1]; + break; + case 11:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0]); + break; + case 12:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0]); + break; + case 13:this.$ = $$[$0]; + break; + case 14:this.$ = $$[$0]; + break; + case 15:this.$ = new yy.ContentNode($$[$0]); + break; + case 16:this.$ = new yy.CommentNode($$[$0]); + break; + case 17:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0])); + break; + case 18:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0])); + break; + case 19:this.$ = {path: $$[$0-1], strip: stripFlags($$[$0-2], $$[$0])}; + break; + case 20:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0])); + break; + case 21:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0])); + break; + case 22:this.$ = new yy.PartialNode($$[$0-2], $$[$0-1], stripFlags($$[$0-3], $$[$0])); + break; + case 23:this.$ = stripFlags($$[$0-1], $$[$0]); + break; + case 24:this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]]; + break; + case 25:this.$ = [[$$[$0]], null]; + break; + case 26:this.$ = $$[$0]; + break; + case 27:this.$ = new yy.StringNode($$[$0]); + break; + case 28:this.$ = new yy.IntegerNode($$[$0]); + break; + case 29:this.$ = new yy.BooleanNode($$[$0]); + break; + case 30:this.$ = $$[$0]; + break; + case 31:this.$ = new yy.HashNode($$[$0]); + break; + case 32:this.$ = [$$[$0-2], $$[$0]]; + break; + case 33:this.$ = new yy.PartialNameNode($$[$0]); + break; + case 34:this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0])); + break; + case 35:this.$ = new yy.PartialNameNode(new yy.IntegerNode($$[$0])); + break; + case 36:this.$ = new yy.DataNode($$[$0]); + break; + case 37:this.$ = new yy.IdNode($$[$0]); + break; + case 38: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2]; + break; + case 39:this.$ = [{part: $$[$0]}]; + break; + case 42:this.$ = []; + break; + case 43:$$[$0-1].push($$[$0]); + break; + case 46:this.$ = [$$[$0]]; + break; + case 47:$$[$0-1].push($$[$0]); + break; + } + }, + table: [{3:1,4:2,5:[1,3],8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[3]},{5:[1,16],8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[2,2]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],25:[2,9]},{4:20,6:18,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{4:20,6:22,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{5:[2,13],14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],25:[2,13]},{5:[2,14],14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],25:[2,14]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],25:[2,15]},{5:[2,16],14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],25:[2,16]},{17:23,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:29,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:30,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:31,21:24,30:25,38:[1,28],40:[1,27],41:26},{21:33,26:32,32:[1,34],33:[1,35],38:[1,28],41:26},{1:[2,1]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],25:[2,10]},{10:36,20:[1,37]},{4:38,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,7],22:[1,13],23:[1,14],25:[1,15]},{7:39,8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,6],22:[1,13],23:[1,14],25:[1,15]},{17:23,18:[1,40],21:24,30:25,38:[1,28],40:[1,27],41:26},{10:41,20:[1,37]},{18:[1,42]},{18:[2,42],24:[2,42],28:43,32:[2,42],33:[2,42],34:[2,42],38:[2,42],40:[2,42]},{18:[2,25],24:[2,25]},{18:[2,37],24:[2,37],32:[2,37],33:[2,37],34:[2,37],38:[2,37],40:[2,37],42:[1,44]},{21:45,38:[1,28],41:26},{18:[2,39],24:[2,39],32:[2,39],33:[2,39],34:[2,39],38:[2,39],40:[2,39],42:[2,39]},{18:[1,46]},{18:[1,47]},{24:[1,48]},{18:[2,40],21:50,27:49,38:[1,28],41:26},{18:[2,33],38:[2,33]},{18:[2,34],38:[2,34]},{18:[2,35],38:[2,35]},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],25:[2,11]},{21:51,38:[1,28],41:26},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,3],22:[1,13],23:[1,14],25:[1,15]},{4:52,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,5],22:[1,13],23:[1,14],25:[1,15]},{14:[2,23],15:[2,23],16:[2,23],19:[2,23],20:[2,23],22:[2,23],23:[2,23],25:[2,23]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],25:[2,12]},{14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],25:[2,18]},{18:[2,44],21:56,24:[2,44],29:53,30:60,31:54,32:[1,57],33:[1,58],34:[1,59],35:55,36:61,37:62,38:[1,63],40:[1,27],41:26},{38:[1,64]},{18:[2,36],24:[2,36],32:[2,36],33:[2,36],34:[2,36],38:[2,36],40:[2,36]},{14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],25:[2,17]},{5:[2,20],14:[2,20],15:[2,20],16:[2,20],19:[2,20],20:[2,20],22:[2,20],23:[2,20],25:[2,20]},{5:[2,21],14:[2,21],15:[2,21],16:[2,21],19:[2,21],20:[2,21],22:[2,21],23:[2,21],25:[2,21]},{18:[1,65]},{18:[2,41]},{18:[1,66]},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,4],22:[1,13],23:[1,14],25:[1,15]},{18:[2,24],24:[2,24]},{18:[2,43],24:[2,43],32:[2,43],33:[2,43],34:[2,43],38:[2,43],40:[2,43]},{18:[2,45],24:[2,45]},{18:[2,26],24:[2,26],32:[2,26],33:[2,26],34:[2,26],38:[2,26],40:[2,26]},{18:[2,27],24:[2,27],32:[2,27],33:[2,27],34:[2,27],38:[2,27],40:[2,27]},{18:[2,28],24:[2,28],32:[2,28],33:[2,28],34:[2,28],38:[2,28],40:[2,28]},{18:[2,29],24:[2,29],32:[2,29],33:[2,29],34:[2,29],38:[2,29],40:[2,29]},{18:[2,30],24:[2,30],32:[2,30],33:[2,30],34:[2,30],38:[2,30],40:[2,30]},{18:[2,31],24:[2,31],37:67,38:[1,68]},{18:[2,46],24:[2,46],38:[2,46]},{18:[2,39],24:[2,39],32:[2,39],33:[2,39],34:[2,39],38:[2,39],39:[1,69],40:[2,39],42:[2,39]},{18:[2,38],24:[2,38],32:[2,38],33:[2,38],34:[2,38],38:[2,38],40:[2,38],42:[2,38]},{5:[2,22],14:[2,22],15:[2,22],16:[2,22],19:[2,22],20:[2,22],22:[2,22],23:[2,22],25:[2,22]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],25:[2,19]},{18:[2,47],24:[2,47],38:[2,47]},{39:[1,69]},{21:56,30:60,31:70,32:[1,57],33:[1,58],34:[1,59],38:[1,28],40:[1,27],41:26},{18:[2,32],24:[2,32],38:[2,32]}], + defaultActions: {3:[2,2],16:[2,1],50:[2,41]}, + parseError: function parseError(str, hash) { + throw new Error(str); + }, + parse: function parse(input) { + var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; + this.lexer.setInput(input); + this.lexer.yy = this.yy; + this.yy.lexer = this.lexer; + this.yy.parser = this; + if (typeof this.lexer.yylloc == "undefined") + this.lexer.yylloc = {}; + var yyloc = this.lexer.yylloc; + lstack.push(yyloc); + var ranges = this.lexer.options && this.lexer.options.ranges; + if (typeof this.yy.parseError === "function") + this.parseError = this.yy.parseError; + function popStack(n) { + stack.length = stack.length - 2 * n; + vstack.length = vstack.length - n; + lstack.length = lstack.length - n; + } + function lex() { + var token; + token = self.lexer.lex() || 1; + if (typeof token !== "number") { + token = self.symbols_[token] || token; + } + return token; + } + var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected; + while (true) { + state = stack[stack.length - 1]; + if (this.defaultActions[state]) { + action = this.defaultActions[state]; + } else { + if (symbol === null || typeof symbol == "undefined") { + symbol = lex(); + } + action = table[state] && table[state][symbol]; + } + if (typeof action === "undefined" || !action.length || !action[0]) { + var errStr = ""; + if (!recovering) { + expected = []; + for (p in table[state]) + if (this.terminals_[p] && p > 2) { + expected.push("'" + this.terminals_[p] + "'"); + } + if (this.lexer.showPosition) { + errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; + } else { + errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'"); + } + this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); + } + } + if (action[0] instanceof Array && action.length > 1) { + throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); + } + switch (action[0]) { + case 1: + stack.push(symbol); + vstack.push(this.lexer.yytext); + lstack.push(this.lexer.yylloc); + stack.push(action[1]); + symbol = null; + if (!preErrorSymbol) { + yyleng = this.lexer.yyleng; + yytext = this.lexer.yytext; + yylineno = this.lexer.yylineno; + yyloc = this.lexer.yylloc; + if (recovering > 0) + recovering--; + } else { + symbol = preErrorSymbol; + preErrorSymbol = null; + } + break; + case 2: + len = this.productions_[action[1]][1]; + yyval.$ = vstack[vstack.length - len]; + yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column}; + if (ranges) { + yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; + } + r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); + if (typeof r !== "undefined") { + return r; + } + if (len) { + stack = stack.slice(0, -1 * len * 2); + vstack = vstack.slice(0, -1 * len); + lstack = lstack.slice(0, -1 * len); + } + stack.push(this.productions_[action[1]][0]); + vstack.push(yyval.$); + lstack.push(yyval._$); + newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; + stack.push(newState); + break; + case 3: + return true; + } + } + return true; + } + }; + + + function stripFlags(open, close) { + return { + left: open.charAt(2) === '~', + right: close.charAt(0) === '~' || close.charAt(1) === '~' + }; + } + + /* Jison generated lexer */ + var lexer = (function(){ + var lexer = ({EOF:1, + parseError:function parseError(str, hash) { + if (this.yy.parser) { + this.yy.parser.parseError(str, hash); + } else { + throw new Error(str); + } + }, + setInput:function (input) { + this._input = input; + this._more = this._less = this.done = false; + this.yylineno = this.yyleng = 0; + this.yytext = this.matched = this.match = ''; + this.conditionStack = ['INITIAL']; + this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; + if (this.options.ranges) this.yylloc.range = [0,0]; + this.offset = 0; + return this; + }, + input:function () { + var ch = this._input[0]; + this.yytext += ch; + this.yyleng++; + this.offset++; + this.match += ch; + this.matched += ch; + var lines = ch.match(/(?:\r\n?|\n).*/g); + if (lines) { + this.yylineno++; + this.yylloc.last_line++; + } else { + this.yylloc.last_column++; + } + if (this.options.ranges) this.yylloc.range[1]++; + + this._input = this._input.slice(1); + return ch; + }, + unput:function (ch) { + var len = ch.length; + var lines = ch.split(/(?:\r\n?|\n)/g); + + this._input = ch + this._input; + this.yytext = this.yytext.substr(0, this.yytext.length-len-1); + //this.yyleng -= len; + this.offset -= len; + var oldLines = this.match.split(/(?:\r\n?|\n)/g); + this.match = this.match.substr(0, this.match.length-1); + this.matched = this.matched.substr(0, this.matched.length-1); + + if (lines.length-1) this.yylineno -= lines.length-1; + var r = this.yylloc.range; + + this.yylloc = {first_line: this.yylloc.first_line, + last_line: this.yylineno+1, + first_column: this.yylloc.first_column, + last_column: lines ? + (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length: + this.yylloc.first_column - len + }; + + if (this.options.ranges) { + this.yylloc.range = [r[0], r[0] + this.yyleng - len]; + } + return this; + }, + more:function () { + this._more = true; + return this; + }, + less:function (n) { + this.unput(this.match.slice(n)); + }, + pastInput:function () { + var past = this.matched.substr(0, this.matched.length - this.match.length); + return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); + }, + upcomingInput:function () { + var next = this.match; + if (next.length < 20) { + next += this._input.substr(0, 20-next.length); + } + return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); + }, + showPosition:function () { + var pre = this.pastInput(); + var c = new Array(pre.length + 1).join("-"); + return pre + this.upcomingInput() + "\n" + c+"^"; + }, + next:function () { + if (this.done) { + return this.EOF; + } + if (!this._input) this.done = true; + + var token, + match, + tempMatch, + index, + col, + lines; + if (!this._more) { + this.yytext = ''; + this.match = ''; + } + var rules = this._currentRules(); + for (var i=0;i < rules.length; i++) { + tempMatch = this._input.match(this.rules[rules[i]]); + if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { + match = tempMatch; + index = i; + if (!this.options.flex) break; + } + } + if (match) { + lines = match[0].match(/(?:\r\n?|\n).*/g); + if (lines) this.yylineno += lines.length; + this.yylloc = {first_line: this.yylloc.last_line, + last_line: this.yylineno+1, + first_column: this.yylloc.last_column, + last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length}; + this.yytext += match[0]; + this.match += match[0]; + this.matches = match; + this.yyleng = this.yytext.length; + if (this.options.ranges) { + this.yylloc.range = [this.offset, this.offset += this.yyleng]; + } + this._more = false; + this._input = this._input.slice(match[0].length); + this.matched += match[0]; + token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); + if (this.done && this._input) this.done = false; + if (token) return token; + else return; + } + if (this._input === "") { + return this.EOF; + } else { + return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), + {text: "", token: null, line: this.yylineno}); + } + }, + lex:function lex() { + var r = this.next(); + if (typeof r !== 'undefined') { + return r; + } else { + return this.lex(); + } + }, + begin:function begin(condition) { + this.conditionStack.push(condition); + }, + popState:function popState() { + return this.conditionStack.pop(); + }, + _currentRules:function _currentRules() { + return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; + }, + topState:function () { + return this.conditionStack[this.conditionStack.length-2]; + }, + pushState:function begin(condition) { + this.begin(condition); + }}); + lexer.options = {}; + lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { + + + function strip(start, end) { + return yy_.yytext = yy_.yytext.substr(start, yy_.yyleng-end); + } + + + var YYSTATE=YY_START + switch($avoiding_name_collisions) { + case 0: + if(yy_.yytext.slice(-2) === "\\\\") { + strip(0,1); + this.begin("mu"); + } else if(yy_.yytext.slice(-1) === "\\") { + strip(0,1); + this.begin("emu"); + } else { + this.begin("mu"); + } + if(yy_.yytext) return 14; + + break; + case 1:return 14; + break; + case 2: + this.popState(); + return 14; + + break; + case 3:strip(0,4); this.popState(); return 15; + break; + case 4:return 25; + break; + case 5:return 16; + break; + case 6:return 20; + break; + case 7:return 19; + break; + case 8:return 19; + break; + case 9:return 23; + break; + case 10:return 22; + break; + case 11:this.popState(); this.begin('com'); + break; + case 12:strip(3,5); this.popState(); return 15; + break; + case 13:return 22; + break; + case 14:return 39; + break; + case 15:return 38; + break; + case 16:return 38; + break; + case 17:return 42; + break; + case 18:// ignore whitespace + break; + case 19:this.popState(); return 24; + break; + case 20:this.popState(); return 18; + break; + case 21:yy_.yytext = strip(1,2).replace(/\\"/g,'"'); return 32; + break; + case 22:yy_.yytext = strip(1,2).replace(/\\'/g,"'"); return 32; + break; + case 23:return 40; + break; + case 24:return 34; + break; + case 25:return 34; + break; + case 26:return 33; + break; + case 27:return 38; + break; + case 28:yy_.yytext = strip(1,2); return 38; + break; + case 29:return 'INVALID'; + break; + case 30:return 5; + break; + } + }; + lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s])))/,/^(?:false(?=([~}\s])))/,/^(?:-?[0-9]+(?=([~}\s])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/]; + lexer.conditions = {"mu":{"rules":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"INITIAL":{"rules":[0,1,30],"inclusive":true}}; + return lexer;})() + parser.lexer = lexer; + function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; + return new Parser; + })();__exports__ = handlebars; + /* jshint ignore:end */ + return __exports__; +})(); + +// handlebars/compiler/base.js +var __module8__ = (function(__dependency1__, __dependency2__) { + "use strict"; + var __exports__ = {}; + var parser = __dependency1__; + var AST = __dependency2__; + + __exports__.parser = parser; + + function parse(input) { + // Just return if an already-compile AST was passed in. + if(input.constructor === AST.ProgramNode) { return input; } + + parser.yy = AST; + return parser.parse(input); + } + + __exports__.parse = parse; + return __exports__; +})(__module9__, __module7__); + +// handlebars/compiler/javascript-compiler.js +var __module11__ = (function(__dependency1__) { + "use strict"; + var __exports__; + var COMPILER_REVISION = __dependency1__.COMPILER_REVISION; + var REVISION_CHANGES = __dependency1__.REVISION_CHANGES; + var log = __dependency1__.log; + + function Literal(value) { + this.value = value; + } + + function JavaScriptCompiler() {} + + JavaScriptCompiler.prototype = { + // PUBLIC API: You can override these methods in a subclass to provide + // alternative compiled forms for name lookup and buffering semantics + nameLookup: function(parent, name /* , type*/) { + var wrap, + ret; + if (parent.indexOf('depth') === 0) { + wrap = true; + } + + if (/^[0-9]+$/.test(name)) { + ret = parent + "[" + name + "]"; + } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) { + ret = parent + "." + name; + } + else { + ret = parent + "['" + name + "']"; + } + + if (wrap) { + return '(' + parent + ' && ' + ret + ')'; + } else { + return ret; + } + }, + + compilerInfo: function() { + var revision = COMPILER_REVISION, + versions = REVISION_CHANGES[revision]; + return "this.compilerInfo = ["+revision+",'"+versions+"'];\n"; + }, + + appendToBuffer: function(string) { + if (this.environment.isSimple) { + return "return " + string + ";"; + } else { + return { + appendToBuffer: true, + content: string, + toString: function() { return "buffer += " + string + ";"; } + }; + } + }, + + initializeBuffer: function() { + return this.quotedString(""); + }, + + namespace: "Handlebars", + // END PUBLIC API + + compile: function(environment, options, context, asObject) { + this.environment = environment; + this.options = options || {}; + + log('debug', this.environment.disassemble() + "\n\n"); + + this.name = this.environment.name; + this.isChild = !!context; + this.context = context || { + programs: [], + environments: [], + aliases: { } + }; + + this.preamble(); + + this.stackSlot = 0; + this.stackVars = []; + this.registers = { list: [] }; + this.compileStack = []; + this.inlineStack = []; + + this.compileChildren(environment, options); + + var opcodes = environment.opcodes, opcode; + + this.i = 0; + + for(var l=opcodes.length; this.i 0) { + this.source[1] = this.source[1] + ", " + locals.join(", "); + } + + // Generate minimizer alias mappings + if (!this.isChild) { + for (var alias in this.context.aliases) { + if (this.context.aliases.hasOwnProperty(alias)) { + this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias]; + } + } + } + + if (this.source[1]) { + this.source[1] = "var " + this.source[1].substring(2) + ";"; + } + + // Merge children + if (!this.isChild) { + this.source[1] += '\n' + this.context.programs.join('\n') + '\n'; + } + + if (!this.environment.isSimple) { + this.pushSource("return buffer;"); + } + + var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"]; + + for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); } + return this.topStackName(); + }, + topStackName: function() { + return "stack" + this.stackSlot; + }, + flushInline: function() { + var inlineStack = this.inlineStack; + if (inlineStack.length) { + this.inlineStack = []; + for (var i = 0, len = inlineStack.length; i < len; i++) { + var entry = inlineStack[i]; + if (entry instanceof Literal) { + this.compileStack.push(entry); + } else { + this.pushStack(entry); + } + } + } + }, + isInline: function() { + return this.inlineStack.length; + }, + + popStack: function(wrapped) { + var inline = this.isInline(), + item = (inline ? this.inlineStack : this.compileStack).pop(); + + if (!wrapped && (item instanceof Literal)) { + return item.value; + } else { + if (!inline) { + this.stackSlot--; + } + return item; + } + }, + + topStack: function(wrapped) { + var stack = (this.isInline() ? this.inlineStack : this.compileStack), + item = stack[stack.length - 1]; + + if (!wrapped && (item instanceof Literal)) { + return item.value; + } else { + return item; + } + }, + + quotedString: function(str) { + return '"' + str + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4 + .replace(/\u2029/g, '\\u2029') + '"'; + }, + + setupHelper: function(paramSize, name, missingParams) { + var params = []; + this.setupParams(paramSize, params, missingParams); + var foundHelper = this.nameLookup('helpers', name, 'helper'); + + return { + params: params, + name: foundHelper, + callParams: ["depth0"].concat(params).join(", "), + helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ") + }; + }, + + // the params and contexts arguments are passed in arrays + // to fill in + setupParams: function(paramSize, params, useRegister) { + var options = [], contexts = [], types = [], param, inverse, program; + + options.push("hash:" + this.popStack()); + + inverse = this.popStack(); + program = this.popStack(); + + // Avoid setting fn and inverse if neither are set. This allows + // helpers to do a check for `if (options.fn)` + if (program || inverse) { + if (!program) { + this.context.aliases.self = "this"; + program = "self.noop"; + } + + if (!inverse) { + this.context.aliases.self = "this"; + inverse = "self.noop"; + } + + options.push("inverse:" + inverse); + options.push("fn:" + program); + } + + for(var i=0; i Date: Tue, 7 Jan 2014 20:59:27 -0500 Subject: [PATCH 11/28] Use bind-attr instead of deprecated bindAttr. --- NOTES.txt | 2 +- assets/scripts/app/templates/builds/list.hbs | 8 ++++---- assets/scripts/app/templates/builds/show.hbs | 10 +++++----- assets/scripts/app/templates/jobs/list.hbs | 4 ++-- assets/scripts/app/templates/jobs/pre.hbs | 4 ++-- assets/scripts/app/templates/jobs/show.hbs | 12 ++++++------ assets/scripts/app/templates/layouts/top.hbs | 4 ++-- assets/scripts/app/templates/profile/accounts.hbs | 2 +- assets/scripts/app/templates/profile/tabs.hbs | 4 ++-- assets/scripts/app/templates/profile/tabs/hooks.hbs | 10 +++++----- assets/scripts/app/templates/profile/tabs/user.hbs | 4 ++-- assets/scripts/app/templates/queues/list.hbs | 2 +- assets/scripts/app/templates/repos/list.hbs | 4 ++-- assets/scripts/app/templates/repos/list/tabs.hbs | 6 +++--- assets/scripts/app/templates/repos/show.hbs | 4 ++-- assets/scripts/app/templates/repos/show/actions.hbs | 12 ++++++------ assets/scripts/app/templates/repos/show/tabs.hbs | 12 ++++++------ assets/scripts/app/templates/repos/show/tools.hbs | 6 +++--- assets/scripts/app/templates/status_images.hbs | 12 ++++++------ assets/scripts/app/views/repo/show.coffee | 2 +- assets/scripts/app/views/top.coffee | 2 +- 21 files changed, 63 insertions(+), 63 deletions(-) diff --git a/NOTES.txt b/NOTES.txt index 577dc5a8..1b2fc3b7 100644 --- a/NOTES.txt +++ b/NOTES.txt @@ -10,7 +10,7 @@ # Handlebars -* Can't {{bindAttr}} be just {{attr}}? Who cares it's "bound" in that context? +* Can't {{bind-attr}} be just {{attr}}? Who cares it's "bound" in that context? {{#each}} isn't {{#bindEach}} either. * Why is {{#collection contentBinding="foo"}} not just {{#collection foo}}? diff --git a/assets/scripts/app/templates/builds/list.hbs b/assets/scripts/app/templates/builds/list.hbs index 9e1baff4..51547030 100644 --- a/assets/scripts/app/templates/builds/list.hbs +++ b/assets/scripts/app/templates/builds/list.hbs @@ -35,7 +35,7 @@ {{{formatMessage commit.message short="true" repoBinding=build.repo}}} - + {{formatCommit commit}} @@ -44,15 +44,15 @@ {{#if view.isPullRequestsList}} - + #{{pullRequestNumber}} {{/if}} - + {{formatDuration duration}} - + {{formatTime finishedAt}} {{/view}} diff --git a/assets/scripts/app/templates/builds/show.hbs b/assets/scripts/app/templates/builds/show.hbs index 5096dd60..37f2f1f0 100644 --- a/assets/scripts/app/templates/builds/show.hbs +++ b/assets/scripts/app/templates/builds/show.hbs @@ -15,22 +15,22 @@
    {{t builds.state}}
    {{capitalize build.state}}
    {{t builds.finished_at}}
    -
    {{formatTime build.finishedAt}}
    +
    {{formatTime build.finishedAt}}
    {{t builds.duration}}
    -
    {{formatDuration build.duration}}
    +
    {{formatDuration build.duration}}
    {{#with build}}
    {{t builds.commit}}
    -
    {{formatCommit commit}}
    +
    {{formatCommit commit}}
    {{#if pullRequest}}
    {{t builds.pull_request}}
    -
    #{{pullRequestNumber}} {{pullRequestTitle}}
    +
    #{{pullRequestNumber}} {{pullRequestTitle}}
    {{else}} {{#if commit.compareUrl}}
    {{t builds.compare}}
    -
    {{pathFrom commit.compareUrl}}
    +
    {{pathFrom commit.compareUrl}}
    {{/if}} {{/if}} {{#if commit.authorName}} diff --git a/assets/scripts/app/templates/jobs/list.hbs b/assets/scripts/app/templates/jobs/list.hbs index 4429a4d9..591d55ea 100644 --- a/assets/scripts/app/templates/jobs/list.hbs +++ b/assets/scripts/app/templates/jobs/list.hbs @@ -29,10 +29,10 @@ {{/if}} {{/if}} - + {{formatDuration duration}} - + {{formatTime finishedAt}} {{#each value in configValues}} diff --git a/assets/scripts/app/templates/jobs/pre.hbs b/assets/scripts/app/templates/jobs/pre.hbs index 457d9699..c3aee48e 100644 --- a/assets/scripts/app/templates/jobs/pre.hbs +++ b/assets/scripts/app/templates/jobs/pre.hbs @@ -17,14 +17,14 @@ {{#if view.job.sponsor.name}} {{/if}} {{#if view.limited}}

    This log is too long to be displayed. Please reduce the verbosity of your - build or download the the raw log. + build or download the the raw log.

    {{/if}}
    diff --git a/assets/scripts/app/templates/jobs/show.hbs b/assets/scripts/app/templates/jobs/show.hbs index 0d8fa914..06051c7b 100644 --- a/assets/scripts/app/templates/jobs/show.hbs +++ b/assets/scripts/app/templates/jobs/show.hbs @@ -1,5 +1,5 @@ {{#if job.isLoaded}} -
    +
    Job
    @@ -14,22 +14,22 @@
    {{t jobs.state}}
    {{capitalize job.state}}
    {{t jobs.finished_at}}
    -
    {{formatTime job.finishedAt}}
    +
    {{formatTime job.finishedAt}}
    {{t jobs.duration}}
    -
    {{formatDuration job.duration}}
    +
    {{formatDuration job.duration}}
    {{#with job}}
    {{t jobs.commit}}
    -
    {{formatCommit commit}}
    +
    {{formatCommit commit}}
    {{#if build.pullRequest}}
    {{t builds.pull_request}}
    -
    #{{build.pullRequestNumber}} {{build.pullRequestTitle}}
    +
    #{{build.pullRequestNumber}} {{build.pullRequestTitle}}
    {{else}} {{#if commit.compareUrl}}
    {{t jobs.compare}}
    -
    {{pathFrom commit.compareUrl}}
    +
    {{pathFrom commit.compareUrl}}
    {{/if}} {{/if}} {{#if commit.authorName}} diff --git a/assets/scripts/app/templates/layouts/top.hbs b/assets/scripts/app/templates/layouts/top.hbs index e883d6aa..d7085cce 100644 --- a/assets/scripts/app/templates/layouts/top.hbs +++ b/assets/scripts/app/templates/layouts/top.hbs @@ -29,13 +29,13 @@
  • Travis CI for Private Repositories
  • -
  • +
  • {{#if signedOut}} {{t layouts.top.github_login}} {{/if}} {{#if signedIn}} - {{#linkTo "profile" class="signed-in"}}{{userName}}{{/linkTo}} + {{#linkTo "profile" class="signed-in"}}{{userName}}{{/linkTo}} {{/if}} {{#if signingIn}} {{t layouts.top.signing_in}} diff --git a/assets/scripts/app/templates/profile/accounts.hbs b/assets/scripts/app/templates/profile/accounts.hbs index 56f5610b..c58f4ae0 100644 --- a/assets/scripts/app/templates/profile/accounts.hbs +++ b/assets/scripts/app/templates/profile/accounts.hbs @@ -2,7 +2,7 @@

  • diff --git a/assets/scripts/app/templates/profile/tabs.hbs b/assets/scripts/app/templates/profile/tabs.hbs index 8f770575..9ded1ba0 100644 --- a/assets/scripts/app/templates/profile/tabs.hbs +++ b/assets/scripts/app/templates/profile/tabs.hbs @@ -1,5 +1,5 @@
      -
    • +
    • {{#with view.account}} {{#if login}} @@ -9,7 +9,7 @@
    • {{#if view.displayUser}} -
    • +
    • {{#linkTo "account.profile" view.account}}Profile{{/linkTo}}
      diff --git a/assets/scripts/app/templates/profile/tabs/hooks.hbs b/assets/scripts/app/templates/profile/tabs/hooks.hbs index dc920183..8aec75ac 100644 --- a/assets/scripts/app/templates/profile/tabs/hooks.hbs +++ b/assets/scripts/app/templates/profile/tabs/hooks.hbs @@ -17,13 +17,13 @@
        {{#each hook in hooks}} -
      • - {{hook.slug}} +
      • + {{hook.slug}} {{#if hook.isSaving}}{{/if}}

        {{hook.description}}

        - + {{#if hook.active}} ON @@ -54,8 +54,8 @@
          {{#each hook in unAdminisetableHooks}} -
        • - {{hook.slug}} +
        • + {{hook.slug}}

          {{hook.description}}

        • {{/each}} diff --git a/assets/scripts/app/templates/profile/tabs/user.hbs b/assets/scripts/app/templates/profile/tabs/user.hbs index b42239c8..d926d3f2 100644 --- a/assets/scripts/app/templates/profile/tabs/user.hbs +++ b/assets/scripts/app/templates/profile/tabs/user.hbs @@ -1,4 +1,4 @@ - +
          @@ -6,7 +6,7 @@ {{t profiles.show.github}}:
          - {{user.login}} + {{user.login}}
          diff --git a/assets/scripts/app/templates/queues/list.hbs b/assets/scripts/app/templates/queues/list.hbs index fe08ffd2..d781c586 100644 --- a/assets/scripts/app/templates/queues/list.hbs +++ b/assets/scripts/app/templates/queues/list.hbs @@ -2,7 +2,7 @@ {{#each queue in controller}}
        • {{t queue}}: {{queue.name}}

          -
            +
              {{#each job in queue}} {{#view Travis.QueueItemView jobBinding="job"}} {{#if job.repo.slug}} diff --git a/assets/scripts/app/templates/repos/list.hbs b/assets/scripts/app/templates/repos/list.hbs index 378891fc..ddbfe251 100644 --- a/assets/scripts/app/templates/repos/list.hbs +++ b/assets/scripts/app/templates/repos/list.hbs @@ -22,11 +22,11 @@

              {{t repositories.duration}}: - {{formatDuration lastBuildDuration}} + {{formatDuration lastBuildDuration}}

              {{t repositories.finished_at}}: - {{formatTime lastBuildFinishedAt}} + {{formatTime lastBuildFinishedAt}}

              diff --git a/assets/scripts/app/templates/repos/list/tabs.hbs b/assets/scripts/app/templates/repos/list/tabs.hbs index 3511bef4..04d03fa3 100644 --- a/assets/scripts/app/templates/repos/list/tabs.hbs +++ b/assets/scripts/app/templates/repos/list/tabs.hbs @@ -1,13 +1,13 @@ diff --git a/assets/scripts/app/templates/repos/show.hbs b/assets/scripts/app/templates/repos/show.hbs index 7d7d3cf7..5addbe79 100644 --- a/assets/scripts/app/templates/repos/show.hbs +++ b/assets/scripts/app/templates/repos/show.hbs @@ -1,4 +1,4 @@ -
              +
              {{#if view.isEmpty}} {{view Travis.ReposEmptyView}} {{else}} @@ -6,7 +6,7 @@ {{#with repo}}

              {{#linkTo "repo" this}}{{slug}}{{/linkTo}}

              -
              +
              {{view Travis.RepoShowToolsView}}
              diff --git a/assets/scripts/app/templates/repos/show/actions.hbs b/assets/scripts/app/templates/repos/show/actions.hbs index 181d23e6..362c9536 100644 --- a/assets/scripts/app/templates/repos/show/actions.hbs +++ b/assets/scripts/app/templates/repos/show/actions.hbs @@ -3,39 +3,39 @@ {{#if view.displayCancelBuild}}
            • + {{bind-attr class="view.canCancelBuild::disabled"}}>
            • {{/if}} {{#if view.displayCancelJob}}
            • + {{bind-attr class="view.canCancelJob::disabled"}}>
            • {{/if}} {{#if view.displayRequeueBuild}}
            • + {{bind-attr class="view.canRequeueBuild::disabled"}}>
            • {{/if}} {{#if view.displayRequeueJob}}
            • + {{bind-attr class="view.canRequeueJob::disabled"}}>
            • {{/if}} {{!TODO: for some reason showDownloadLog, which just delegates to jobIdForLog does not refresh 'if' properly, need further investigation}} {{#if view.jobIdForLog}}
            • - +
            • {{/if}} {{#if view.displayCodeClimate}}
            • + {{bind-attr class=":open-popup"}}>
            • diff --git a/assets/scripts/app/templates/repos/show/tabs.hbs b/assets/scripts/app/templates/repos/show/tabs.hbs index b1671c2f..2b3ef740 100644 --- a/assets/scripts/app/templates/repos/show/tabs.hbs +++ b/assets/scripts/app/templates/repos/show/tabs.hbs @@ -1,5 +1,5 @@
                -
              • +
              • {{#if repo.slug}} {{#linkTo "repo" repo currentWhen="repo.index"}} @@ -8,7 +8,7 @@ {{/if}}
              • -
              • +
              • {{#if repo.slug}} {{#linkTo "builds" repo}} @@ -17,7 +17,7 @@ {{/if}}
              • -
              • +
              • {{#if repo.slug}} {{#linkTo "pullRequests" repo}} @@ -26,7 +26,7 @@ {{/if}}
              • -
              • +
              • {{#if repo.slug}} {{#linkTo "branches" repo}} @@ -35,7 +35,7 @@ {{/if}}
              • -
              • +
              • {{#if build.id}} {{#if repo.slug}} @@ -46,7 +46,7 @@ {{/if}}
              • -
              • +
              • {{#if job.id}} {{#if repo.slug}} diff --git a/assets/scripts/app/templates/repos/show/tools.hbs b/assets/scripts/app/templates/repos/show/tools.hbs index f6368253..5cc706df 100644 --- a/assets/scripts/app/templates/repos/show/tools.hbs +++ b/assets/scripts/app/templates/repos/show/tools.hbs @@ -6,7 +6,7 @@
              • + {{bind-attr class=":open-popup view.canRegenerateKey::disabled"}}> Regenerate Key
              • @@ -14,7 +14,7 @@
              {{#if view.displayStatusImages}} - + {{/if}} @@ -58,7 +58,7 @@

              Integrating Code Climate's test coverage reporting with your test suite on Travis CI allows to track changes in coverage over time. If you haven't tried it out already, sign + {{bind-attr href="Travis.config.code_climate_url"}}" target="_blank">sign up today to improve your code's quality. New customers get 20% off for the first three months!

              diff --git a/assets/scripts/app/templates/status_images.hbs b/assets/scripts/app/templates/status_images.hbs index f5dc5c0a..0f832f2b 100644 --- a/assets/scripts/app/templates/status_images.hbs +++ b/assets/scripts/app/templates/status_images.hbs @@ -9,25 +9,25 @@

              - +

              - +

              - +

              - +

              - +

              - +

              diff --git a/assets/scripts/app/views/repo/show.coffee b/assets/scripts/app/views/repo/show.coffee index a27a0b29..5deaf914 100644 --- a/assets/scripts/app/views/repo/show.coffee +++ b/assets/scripts/app/views/repo/show.coffee @@ -37,7 +37,7 @@ Travis.reopen tabBinding: 'controller.tab' contextBinding: 'controller' - # hrm. how to parametrize bindAttr? + # hrm. how to parametrize bind-attr? classCurrent: (-> 'active' if @get('tab') == 'current' ).property('tab') diff --git a/assets/scripts/app/views/top.coffee b/assets/scripts/app/views/top.coffee index ac9e8d24..fe5d7aea 100644 --- a/assets/scripts/app/views/top.coffee +++ b/assets/scripts/app/views/top.coffee @@ -4,7 +4,7 @@ tabBinding: 'controller.tab' - # hrm. how to parametrize bindAttr? + # hrm. how to parametrize bind-attr? classHome: (-> 'active' if @get('tab') == 'home' ).property('tab') From 8d681e85affc5abcb283d95de783e9fe14383fba Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Tue, 7 Jan 2014 21:02:24 -0500 Subject: [PATCH 12/28] Use link-to instead of deprecated linkTo. --- assets/scripts/app/templates/builds/list.hbs | 4 ++-- assets/scripts/app/templates/builds/show.hbs | 2 +- assets/scripts/app/templates/jobs/list.hbs | 2 +- assets/scripts/app/templates/jobs/show.hbs | 2 +- assets/scripts/app/templates/layouts/top.hbs | 10 ++++---- .../scripts/app/templates/no_owned_repos.hbs | 2 +- .../app/templates/profile/accounts.hbs | 2 +- assets/scripts/app/templates/profile/tabs.hbs | 4 ++-- assets/scripts/app/templates/queues/list.hbs | 4 ++-- assets/scripts/app/templates/repos/list.hbs | 4 ++-- assets/scripts/app/templates/repos/show.hbs | 2 +- .../scripts/app/templates/repos/show/tabs.hbs | 24 +++++++++---------- assets/scripts/app/templates/workers/list.hbs | 4 ++-- 13 files changed, 33 insertions(+), 33 deletions(-) diff --git a/assets/scripts/app/templates/builds/list.hbs b/assets/scripts/app/templates/builds/list.hbs index 51547030..5d73ca69 100644 --- a/assets/scripts/app/templates/builds/list.hbs +++ b/assets/scripts/app/templates/builds/list.hbs @@ -26,9 +26,9 @@ {{#if id}} - {{#linkTo "build" repo this}} + {{#link-to "build" repo this}} {{number}} - {{/linkTo}} + {{/link-to}} {{/if}} diff --git a/assets/scripts/app/templates/builds/show.hbs b/assets/scripts/app/templates/builds/show.hbs index 37f2f1f0..7c3d8f81 100644 --- a/assets/scripts/app/templates/builds/show.hbs +++ b/assets/scripts/app/templates/builds/show.hbs @@ -8,7 +8,7 @@ {{#if build.id}} {{#if build.repo.slug}} - {{#linkTo "build" repo build}}{{build.number}}{{/linkTo}} + {{#link-to "build" repo build}}{{build.number}}{{/link-to}} {{/if}} {{/if}} diff --git a/assets/scripts/app/templates/jobs/list.hbs b/assets/scripts/app/templates/jobs/list.hbs index 591d55ea..22314a35 100644 --- a/assets/scripts/app/templates/jobs/list.hbs +++ b/assets/scripts/app/templates/jobs/list.hbs @@ -25,7 +25,7 @@ {{#if job.id}} {{#if job.repo.slug}} - {{#linkTo "job" repo job}}{{number}}{{/linkTo}} + {{#link-to "job" repo job}}{{number}}{{/link-to}} {{/if}} {{/if}} diff --git a/assets/scripts/app/templates/jobs/show.hbs b/assets/scripts/app/templates/jobs/show.hbs index 06051c7b..0d3bd35e 100644 --- a/assets/scripts/app/templates/jobs/show.hbs +++ b/assets/scripts/app/templates/jobs/show.hbs @@ -7,7 +7,7 @@ {{#if job.id}} {{#if job.repo.slug}} - {{#linkTo "job" repo job}}{{job.number}}{{/linkTo}} + {{#link-to "job" repo job}}{{job.number}}{{/link-to}} {{/if}} {{/if}} diff --git a/assets/scripts/app/templates/layouts/top.hbs b/assets/scripts/app/templates/layouts/top.hbs index d7085cce..0f583cf6 100644 --- a/assets/scripts/app/templates/layouts/top.hbs +++ b/assets/scripts/app/templates/layouts/top.hbs @@ -1,10 +1,10 @@ -{{#linkTo "index.current"}} +{{#link-to "index.current"}}

              Travis

              -{{/linkTo}} +{{/link-to}}