From 51dd55c5b9e862d8921c7c99b20af0bf1973b27d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2020 19:14:38 +0000 Subject: [PATCH 001/130] [Security] Bump jquery-rails from 3.0.4 to 3.1.5 Bumps [jquery-rails](https://github.com/rails/jquery-rails) from 3.0.4 to 3.1.5. **This update includes a security fix.** - [Release notes](https://github.com/rails/jquery-rails/releases) - [Changelog](https://github.com/rails/jquery-rails/blob/v3.1.5/CHANGELOG.md) - [Commits](https://github.com/rails/jquery-rails/compare/v3.0.4...v3.1.5) Signed-off-by: dependabot-preview[bot] --- Gemfile | 2 +- Gemfile.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index aa468612b3..2e0b4a1f77 100644 --- a/Gemfile +++ b/Gemfile @@ -118,7 +118,7 @@ gem "foundation-rails" gem 'foundation_rails_helper', github: 'willrjmarshall/foundation_rails_helper', branch: "rails3" gem 'jquery-migrate-rails' -gem 'jquery-rails', '3.0.4' +gem 'jquery-rails', '3.1.5' gem 'jquery-ui-rails', '~> 4.0.0' gem 'select2-rails', '~> 3.4.7' diff --git a/Gemfile.lock b/Gemfile.lock index 7a0d3c5f64..23eb3cf194 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -433,7 +433,7 @@ GEM jaro_winkler (1.5.4) journey (1.0.4) jquery-migrate-rails (1.2.1) - jquery-rails (3.0.4) + jquery-rails (3.1.5) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) jquery-ui-rails (4.0.5) @@ -516,7 +516,7 @@ GEM rabl (0.8.4) activesupport (>= 2.3.14) rack (1.4.7) - rack-cache (1.9.0) + rack-cache (1.11.0) rack (>= 0.4) rack-mini-profiler (1.1.6) rack (>= 1.2.0) @@ -548,7 +548,7 @@ GEM thor (>= 0.14.6, < 2.0) rainbow (3.0.0) raindrops (0.19.1) - rake (13.0.0) + rake (13.0.1) ransack (0.7.2) actionpack (~> 3.0) activerecord (~> 3.0) @@ -742,7 +742,7 @@ DEPENDENCIES i18n-js (~> 3.6.0) immigrant jquery-migrate-rails - jquery-rails (= 3.0.4) + jquery-rails (= 3.1.5) jquery-ui-rails (~> 4.0.0) json_spec (~> 1.1.4) jwt (~> 2.2) From 6b6cdf07fb5dd0f9f80d578eee13e8ca34863835 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Tue, 25 Feb 2020 11:56:23 +0000 Subject: [PATCH 002/130] Remove inactive confirm The new version of jquery is picking up a confirm in the ship action that was not being triggered in the previous version The orders_spec was not expecting this confirm to appear so we are removing here to keep the behaviour and the spec green --- app/views/spree/admin/orders/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/spree/admin/orders/index.html.haml b/app/views/spree/admin/orders/index.html.haml index 244b2e8f1c..5359422dd0 100644 --- a/app/views/spree/admin/orders/index.html.haml +++ b/app/views/spree/admin/orders/index.html.haml @@ -84,7 +84,7 @@ %i.error.icon-remove-sign.with-tip{ng: {show: 'rowStatus[order.id] == "error"'}, 'ofn-with-tip' => t('.order_not_updated')} %a.icon_link.with-tip.icon-edit.no-text{'ng-href' => '{{order.edit_path}}', 'data-action' => 'edit', 'ofn-with-tip' => t('.edit')} %div{'ng-if' => 'order.ready_to_ship'} - %button.icon-road.icon_link.with-tip.no-text{'ng-click' => 'shipOrder(order)', 'data-confirm' => t(:are_you_sure), rel: 'nofollow', 'ofn-with-tip' => t('.ship')} + %button.icon-road.icon_link.with-tip.no-text{'ng-click' => 'shipOrder(order)', rel: 'nofollow', 'ofn-with-tip' => t('.ship')} %div{'ng-if' => 'order.ready_to_capture'} %button.icon-capture.icon_link.no-text{'ng-click' => 'capturePayment(order)', rel: 'nofollow', 'ofn-with-tip' => t('.capture')} From 777754f8a9200f4c5ff2b0f15f8e5f3552b65c06 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Mon, 2 Mar 2020 19:41:51 +0000 Subject: [PATCH 003/130] Remove dead code that creates products in product_set --- app/models/spree/product_set.rb | 10 ++++------ spec/models/spree/product_set_spec.rb | 6 ++---- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/app/models/spree/product_set.rb b/app/models/spree/product_set.rb index c1d387c787..5de24ddc52 100644 --- a/app/models/spree/product_set.rb +++ b/app/models/spree/product_set.rb @@ -1,6 +1,6 @@ class Spree::ProductSet < ModelSet def initialize(attributes = {}) - super(Spree::Product, [], attributes, proc { |attrs| attrs[:product_id].blank? }) + super(Spree::Product, [], attributes) end def save @@ -34,11 +34,9 @@ class Spree::ProductSet < ModelSet split_taxon_ids!(attributes) product = find_model(@collection, attributes[:id]) - if product.nil? - @klass.new(attributes).save unless @reject_if.andand.call(attributes) - else - update_product(product, attributes) - end + return if product.nil? + + update_product(product, attributes) end def split_taxon_ids!(attributes) diff --git a/spec/models/spree/product_set_spec.rb b/spec/models/spree/product_set_spec.rb index ee5364bbd2..d81f139908 100644 --- a/spec/models/spree/product_set_spec.rb +++ b/spec/models/spree/product_set_spec.rb @@ -12,7 +12,6 @@ describe Spree::ProductSet do let(:collection_hash) do { 0 => { - product_id: 11, name: 'a product', price: 2.0, supplier_id: create(:enterprise).id, @@ -25,11 +24,10 @@ describe Spree::ProductSet do } end - it 'creates it with the specified attributes' do + it 'does not create a new product' do product_set.save - expect(Spree::Product.last.attributes) - .to include('name' => 'a product') + expect(Spree::Product.last).to be nil end end From 1382bb3c6bd0c3144ab71b40bd851737502ccfc6 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Tue, 3 Mar 2020 15:04:51 +0000 Subject: [PATCH 004/130] For some reason in rails 4/spree 2.1 setting the line item as adjustable on the adjustment is not enough to populate line_item.adjustments. Here we make this assignment explicit fixing the spec in rails 4 --- spec/models/spree/line_item_spec.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/spec/models/spree/line_item_spec.rb b/spec/models/spree/line_item_spec.rb index 49edecb14b..0578d90017 100644 --- a/spec/models/spree/line_item_spec.rb +++ b/spec/models/spree/line_item_spec.rb @@ -40,10 +40,13 @@ module Spree describe "finding line items with and without tax" do let(:tax_rate) { create(:tax_rate, calculator: Spree::Calculator::DefaultTax.new) } - let!(:adjustment1) { create(:adjustment, adjustable: li1, originator: tax_rate, label: "TR", amount: 123, included_tax: 10.00) } - let!(:adjustment2) { create(:adjustment, adjustable: li1, originator: tax_rate, label: "TR", amount: 123, included_tax: 10.00) } + let!(:adjustment1) { create(:adjustment, originator: tax_rate, label: "TR", amount: 123, included_tax: 10.00) } - before { li1; li2 } + before do + li1 + li2 + li1.adjustments << adjustment1 + end it "finds line items with tax" do expect(LineItem.with_tax).to eq([li1]) From 05ccd1ecbfb3cec5add37c6d8154cfaaa90ff3a4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2020 18:49:51 +0000 Subject: [PATCH 005/130] Bump compass-rails from 3.1.0 to 4.0.0 Bumps [compass-rails](https://github.com/Compass/compass-rails) from 3.1.0 to 4.0.0. - [Release notes](https://github.com/Compass/compass-rails/releases) - [Changelog](https://github.com/Compass/compass-rails/blob/master/CHANGELOG.md) - [Commits](https://github.com/Compass/compass-rails/commits) Signed-off-by: dependabot-preview[bot] --- Gemfile.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 292a9e3448..e33bf3e206 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -173,7 +173,7 @@ GEM xpath (>= 2.0, < 4.0) childprocess (3.0.0) chronic (0.10.2) - chunky_png (1.3.10) + chunky_png (1.3.11) climate_control (0.2.0) cocaine (0.5.8) climate_control (>= 0.0.3, < 1.0) @@ -200,7 +200,7 @@ GEM sass (>= 3.3.0, < 3.5) compass-import-once (1.0.5) sass (>= 3.2, < 3.5) - compass-rails (3.1.0) + compass-rails (4.0.0) compass (~> 1.0.0) sass-rails (< 5.1) sprockets (< 4.0) @@ -257,7 +257,7 @@ GEM faraday (1.0.0) multipart-post (>= 1.2, < 3) ffaker (1.22.1) - ffi (1.11.3) + ffi (1.12.2) figaro (1.1.1) thor (~> 0.14) fission (0.5.0) @@ -559,8 +559,8 @@ GEM activerecord (~> 3.0) polyamorous (~> 0.5.0) rb-fsevent (0.10.3) - rb-inotify (0.9.10) - ffi (>= 0.5.0, < 2) + rb-inotify (0.10.1) + ffi (~> 1.0) rbvmomi (1.13.0) builder (~> 3.0) json (>= 1.8) From 0c6fe20e82aeefc9867149fc9bf416ce90415025 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2020 19:19:22 +0000 Subject: [PATCH 006/130] Bump rack-mini-profiler from 2.0.0 to 2.0.1 Bumps [rack-mini-profiler](https://github.com/MiniProfiler/rack-mini-profiler) from 2.0.0 to 2.0.1. - [Release notes](https://github.com/MiniProfiler/rack-mini-profiler/releases) - [Changelog](https://github.com/MiniProfiler/rack-mini-profiler/blob/master/CHANGELOG.md) - [Commits](https://github.com/MiniProfiler/rack-mini-profiler/compare/v2.0.0...v2.0.1) Signed-off-by: dependabot-preview[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2870003606..fc7005decb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -523,7 +523,7 @@ GEM rack (1.4.7) rack-cache (1.11.0) rack (>= 0.4) - rack-mini-profiler (2.0.0) + rack-mini-profiler (2.0.1) rack (>= 1.2.0) rack-protection (1.5.5) rack From 8aa892136e07fc97d9505fe009ffce3b7ba4027d Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 4 Mar 2020 14:47:40 +1100 Subject: [PATCH 007/130] Duplicate shop tab code for rewrite The old code is still used on the user page. --- .../darkswarm/directives/page.js.coffee | 12 ++++++++ .../darkswarm/directives/page_view.js.coffee | 15 ++++++++++ .../directives/pageset_ctrl.js.coffee | 30 +++++++++++++++++++ .../stylesheets/darkswarm/shop_tabs.css.scss | 6 ++-- app/views/shopping_shared/_tabs.html.haml | 6 ++-- 5 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 app/assets/javascripts/darkswarm/directives/page.js.coffee create mode 100644 app/assets/javascripts/darkswarm/directives/page_view.js.coffee create mode 100644 app/assets/javascripts/darkswarm/directives/pageset_ctrl.js.coffee diff --git a/app/assets/javascripts/darkswarm/directives/page.js.coffee b/app/assets/javascripts/darkswarm/directives/page.js.coffee new file mode 100644 index 0000000000..9ae6f8267c --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/page.js.coffee @@ -0,0 +1,12 @@ +Darkswarm.directive "page", -> + restrict: "C" + require: "^^pagesetCtrl" + scope: + name: "@" + link: (scope, element, attrs, ctrl) -> + element.on "click", -> + scope.$apply -> + ctrl.toggle(scope.name) + + ctrl.registerSelectionListener (prefix, selection) -> + element.toggleClass('selected', selection == scope.name) diff --git a/app/assets/javascripts/darkswarm/directives/page_view.js.coffee b/app/assets/javascripts/darkswarm/directives/page_view.js.coffee new file mode 100644 index 0000000000..76f9402300 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/page_view.js.coffee @@ -0,0 +1,15 @@ +Darkswarm.directive "pageView", -> + restrict: "C" + require: "^^pagesetCtrl" + template: "
" + scope: + templates: "=" + link: (scope, element, attrs, ctrl) -> + scope.template = null + + ctrl.registerSelectionListener (prefix, selection) -> + if selection? + selection = "#{prefix}/#{selection}" if prefix? + scope.template = "#{selection}.html" + else + scope.template = null diff --git a/app/assets/javascripts/darkswarm/directives/pageset_ctrl.js.coffee b/app/assets/javascripts/darkswarm/directives/pageset_ctrl.js.coffee new file mode 100644 index 0000000000..b880356245 --- /dev/null +++ b/app/assets/javascripts/darkswarm/directives/pageset_ctrl.js.coffee @@ -0,0 +1,30 @@ +Darkswarm.directive "pagesetCtrl", (Tabsets, $location) -> + restrict: "C" + scope: + id: "@" + selected: "@" + navigate: "=" + prefix: "@?" + alwaysopen: "=" + controller: ($scope, $element) -> + if $scope.navigate + path = $location.path()?.match(/^\/\w+$/)?[0] + $scope.selected = path[1..] if path + + this.toggle = (name) -> + state = if $scope.alwaysopen then 'open' else null + Tabsets.toggle($scope.id, name, state) + + this.select = (selection) -> + $scope.$broadcast("selection:changed", selection) + $element.toggleClass("expanded", selection?) + $location.path(selection) if $scope.navigate + + this.registerSelectionListener = (callback) -> + $scope.$on "selection:changed", (event, selection) -> + callback($scope.prefix, selection) + + this + + link: (scope, element, attrs, ctrl) -> + Tabsets.register(ctrl, scope.id, scope.selected) diff --git a/app/assets/stylesheets/darkswarm/shop_tabs.css.scss b/app/assets/stylesheets/darkswarm/shop_tabs.css.scss index 40007e2b6a..a913096417 100644 --- a/app/assets/stylesheets/darkswarm/shop_tabs.css.scss +++ b/app/assets/stylesheets/darkswarm/shop_tabs.css.scss @@ -3,7 +3,7 @@ @import "branding"; // Tabs styling -.tabset-ctrl#shop-tabs { +.pageset-ctrl#shop-tabs { .tab-buttons { color: $dark-grey; @@ -23,7 +23,7 @@ } } - .tab { + .page { text-align: center; border-top: 4px solid transparent; display: inline-block; @@ -75,7 +75,7 @@ // content revealed in accordion - .tab-view { + .page-view { margin-bottom: 5em; background: none; border: none; diff --git a/app/views/shopping_shared/_tabs.html.haml b/app/views/shopping_shared/_tabs.html.haml index 434c5070a2..8b80141f5a 100644 --- a/app/views/shopping_shared/_tabs.html.haml +++ b/app/views/shopping_shared/_tabs.html.haml @@ -3,12 +3,12 @@ - shop_tabs.each do |tab| = render "shopping_shared/tabs/#{tab[:name]}" - .tabset-ctrl#shop-tabs{ navigate: 'true', alwaysopen: 'true', selected: shop_tabs.first[:name], prefix: 'shop', ng: { cloak: true } } + .pageset-ctrl#shop-tabs{ navigate: 'true', alwaysopen: 'true', selected: shop_tabs.first[:name], prefix: 'shop', ng: { cloak: true } } .tab-buttons .row .columns.small-12.large-8 - shop_tabs.each do |tab| - .tab{ id: "tab_#{tab[:name]}", name: tab[:name] } + .page{ id: "tab_#{tab[:name]}", name: tab[:name] } %a{ href: 'javascript:void(0)' }=tab[:title] - .tab-view + .page-view From 42ca7888c07bac6663476099c6370640b949c625 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 4 Mar 2020 14:50:57 +1100 Subject: [PATCH 008/130] Simplify by reducing unused options --- .../darkswarm/directives/pageset_ctrl.js.coffee | 12 ++++-------- app/views/shopping_shared/_tabs.html.haml | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/darkswarm/directives/pageset_ctrl.js.coffee b/app/assets/javascripts/darkswarm/directives/pageset_ctrl.js.coffee index b880356245..926b97ee28 100644 --- a/app/assets/javascripts/darkswarm/directives/pageset_ctrl.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/pageset_ctrl.js.coffee @@ -3,22 +3,18 @@ Darkswarm.directive "pagesetCtrl", (Tabsets, $location) -> scope: id: "@" selected: "@" - navigate: "=" prefix: "@?" - alwaysopen: "=" controller: ($scope, $element) -> - if $scope.navigate - path = $location.path()?.match(/^\/\w+$/)?[0] - $scope.selected = path[1..] if path + path = $location.path()?.match(/^\/\w+$/)?[0] + $scope.selected = path[1..] if path this.toggle = (name) -> - state = if $scope.alwaysopen then 'open' else null - Tabsets.toggle($scope.id, name, state) + Tabsets.toggle($scope.id, name, "open") this.select = (selection) -> $scope.$broadcast("selection:changed", selection) $element.toggleClass("expanded", selection?) - $location.path(selection) if $scope.navigate + $location.path(selection) this.registerSelectionListener = (callback) -> $scope.$on "selection:changed", (event, selection) -> diff --git a/app/views/shopping_shared/_tabs.html.haml b/app/views/shopping_shared/_tabs.html.haml index 8b80141f5a..95b7e0dcee 100644 --- a/app/views/shopping_shared/_tabs.html.haml +++ b/app/views/shopping_shared/_tabs.html.haml @@ -3,7 +3,7 @@ - shop_tabs.each do |tab| = render "shopping_shared/tabs/#{tab[:name]}" - .pageset-ctrl#shop-tabs{ navigate: 'true', alwaysopen: 'true', selected: shop_tabs.first[:name], prefix: 'shop', ng: { cloak: true } } + .pageset-ctrl#shop-tabs{ selected: shop_tabs.first[:name], prefix: 'shop', ng: { cloak: true } } .tab-buttons .row .columns.small-12.large-8 From e3f840f48cbd165048cc1e3d2c2d1039e4f91c51 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 4 Mar 2020 15:18:00 +1100 Subject: [PATCH 009/130] Remove dependency to Tabsets --- .../javascripts/darkswarm/directives/pageset_ctrl.js.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/darkswarm/directives/pageset_ctrl.js.coffee b/app/assets/javascripts/darkswarm/directives/pageset_ctrl.js.coffee index 926b97ee28..c0ed9cbcf8 100644 --- a/app/assets/javascripts/darkswarm/directives/pageset_ctrl.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/pageset_ctrl.js.coffee @@ -1,4 +1,4 @@ -Darkswarm.directive "pagesetCtrl", (Tabsets, $location) -> +Darkswarm.directive "pagesetCtrl", ($location) -> restrict: "C" scope: id: "@" @@ -9,7 +9,7 @@ Darkswarm.directive "pagesetCtrl", (Tabsets, $location) -> $scope.selected = path[1..] if path this.toggle = (name) -> - Tabsets.toggle($scope.id, name, "open") + this.select(name) this.select = (selection) -> $scope.$broadcast("selection:changed", selection) @@ -23,4 +23,4 @@ Darkswarm.directive "pagesetCtrl", (Tabsets, $location) -> this link: (scope, element, attrs, ctrl) -> - Tabsets.register(ctrl, scope.id, scope.selected) + ctrl.select(scope.selected) if scope.selected? From bf26a26743bebd47e4f44f5646589f2829226049 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 4 Mar 2020 15:39:30 +1100 Subject: [PATCH 010/130] Reduce complexity by removing unused code branch --- .../javascripts/darkswarm/directives/page_view.js.coffee | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/assets/javascripts/darkswarm/directives/page_view.js.coffee b/app/assets/javascripts/darkswarm/directives/page_view.js.coffee index 76f9402300..2153611d73 100644 --- a/app/assets/javascripts/darkswarm/directives/page_view.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/page_view.js.coffee @@ -8,8 +8,4 @@ Darkswarm.directive "pageView", -> scope.template = null ctrl.registerSelectionListener (prefix, selection) -> - if selection? - selection = "#{prefix}/#{selection}" if prefix? - scope.template = "#{selection}.html" - else - scope.template = null + scope.template = "#{prefix}/#{selection}.html" From ea80ae3832cb81c8fc92648fdcbcf31b39e3721f Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 5 Mar 2020 17:17:06 +1100 Subject: [PATCH 011/130] Enable page navigation via URL fragment The broadcasting of notifications didn't update properly and I couldn't find a way to listen to $location updates. I replaced the three intertwined directives with one controller and a bit more HTML code. Now we have only one scope that listens to $location and all browser actions like the back button is reflected in the page. As nice side-effect, the menu links have now the right destination so that you can copy the link and paste it into another browser window. 40 lines less code. --- .../controllers/pageset_ctrl.js.coffee | 10 +++++++ .../darkswarm/directives/page.js.coffee | 12 --------- .../darkswarm/directives/page_view.js.coffee | 11 -------- .../directives/pageset_ctrl.js.coffee | 26 ------------------- .../stylesheets/darkswarm/shop_tabs.css.scss | 2 +- app/views/shopping_shared/_tabs.html.haml | 8 +++--- .../consumer/shopping/shopping_spec.rb | 8 ++++-- 7 files changed, 21 insertions(+), 56 deletions(-) create mode 100644 app/assets/javascripts/darkswarm/controllers/pageset_ctrl.js.coffee delete mode 100644 app/assets/javascripts/darkswarm/directives/page.js.coffee delete mode 100644 app/assets/javascripts/darkswarm/directives/page_view.js.coffee delete mode 100644 app/assets/javascripts/darkswarm/directives/pageset_ctrl.js.coffee diff --git a/app/assets/javascripts/darkswarm/controllers/pageset_ctrl.js.coffee b/app/assets/javascripts/darkswarm/controllers/pageset_ctrl.js.coffee new file mode 100644 index 0000000000..2417469135 --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/pageset_ctrl.js.coffee @@ -0,0 +1,10 @@ +Darkswarm.controller "PagesetCtrl", ($scope, $location) -> + $scope.selected = -> + path = $location.path()?.match(/^\/\w+$/)?[0] + if path + path[1..] + else + $scope.defaultPage + + $scope.selectDefault = (selection) -> + $scope.defaultPage = selection diff --git a/app/assets/javascripts/darkswarm/directives/page.js.coffee b/app/assets/javascripts/darkswarm/directives/page.js.coffee deleted file mode 100644 index 9ae6f8267c..0000000000 --- a/app/assets/javascripts/darkswarm/directives/page.js.coffee +++ /dev/null @@ -1,12 +0,0 @@ -Darkswarm.directive "page", -> - restrict: "C" - require: "^^pagesetCtrl" - scope: - name: "@" - link: (scope, element, attrs, ctrl) -> - element.on "click", -> - scope.$apply -> - ctrl.toggle(scope.name) - - ctrl.registerSelectionListener (prefix, selection) -> - element.toggleClass('selected', selection == scope.name) diff --git a/app/assets/javascripts/darkswarm/directives/page_view.js.coffee b/app/assets/javascripts/darkswarm/directives/page_view.js.coffee deleted file mode 100644 index 2153611d73..0000000000 --- a/app/assets/javascripts/darkswarm/directives/page_view.js.coffee +++ /dev/null @@ -1,11 +0,0 @@ -Darkswarm.directive "pageView", -> - restrict: "C" - require: "^^pagesetCtrl" - template: "
" - scope: - templates: "=" - link: (scope, element, attrs, ctrl) -> - scope.template = null - - ctrl.registerSelectionListener (prefix, selection) -> - scope.template = "#{prefix}/#{selection}.html" diff --git a/app/assets/javascripts/darkswarm/directives/pageset_ctrl.js.coffee b/app/assets/javascripts/darkswarm/directives/pageset_ctrl.js.coffee deleted file mode 100644 index c0ed9cbcf8..0000000000 --- a/app/assets/javascripts/darkswarm/directives/pageset_ctrl.js.coffee +++ /dev/null @@ -1,26 +0,0 @@ -Darkswarm.directive "pagesetCtrl", ($location) -> - restrict: "C" - scope: - id: "@" - selected: "@" - prefix: "@?" - controller: ($scope, $element) -> - path = $location.path()?.match(/^\/\w+$/)?[0] - $scope.selected = path[1..] if path - - this.toggle = (name) -> - this.select(name) - - this.select = (selection) -> - $scope.$broadcast("selection:changed", selection) - $element.toggleClass("expanded", selection?) - $location.path(selection) - - this.registerSelectionListener = (callback) -> - $scope.$on "selection:changed", (event, selection) -> - callback($scope.prefix, selection) - - this - - link: (scope, element, attrs, ctrl) -> - ctrl.select(scope.selected) if scope.selected? diff --git a/app/assets/stylesheets/darkswarm/shop_tabs.css.scss b/app/assets/stylesheets/darkswarm/shop_tabs.css.scss index a913096417..4da15cbe1b 100644 --- a/app/assets/stylesheets/darkswarm/shop_tabs.css.scss +++ b/app/assets/stylesheets/darkswarm/shop_tabs.css.scss @@ -3,7 +3,7 @@ @import "branding"; // Tabs styling -.pageset-ctrl#shop-tabs { +#shop-tabs { .tab-buttons { color: $dark-grey; diff --git a/app/views/shopping_shared/_tabs.html.haml b/app/views/shopping_shared/_tabs.html.haml index 95b7e0dcee..eb663613a5 100644 --- a/app/views/shopping_shared/_tabs.html.haml +++ b/app/views/shopping_shared/_tabs.html.haml @@ -3,12 +3,12 @@ - shop_tabs.each do |tab| = render "shopping_shared/tabs/#{tab[:name]}" - .pageset-ctrl#shop-tabs{ selected: shop_tabs.first[:name], prefix: 'shop', ng: { cloak: true } } + #shop-tabs{ ng: { controller: "PagesetCtrl", init: "selectDefault('#{shop_tabs.first[:name]}')", cloak: true } } .tab-buttons .row .columns.small-12.large-8 - shop_tabs.each do |tab| - .page{ id: "tab_#{tab[:name]}", name: tab[:name] } - %a{ href: 'javascript:void(0)' }=tab[:title] + .page{ "ng-class" => "{ selected: selected() == '#{tab[:name]}' }" } + %a{ href: "##{tab[:name]}" }=tab[:title] - .page-view + .page-view{ "ng-include" => '"shop/" + selected() + ".html"' } diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index 989eea5e4e..b8a6727727 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -27,7 +27,9 @@ feature "As a consumer I want to shop with a distributor", js: true do # Then we should see the distributor and its logo visit shop_path expect(page).to have_text distributor.name - find("#tab_about a").click + within ".tab-buttons" do + click_link "About" + end expect(first("distributor img")['src']).to include distributor.logo.url(:thumb) end @@ -36,7 +38,9 @@ feature "As a consumer I want to shop with a distributor", js: true do add_variant_to_order_cycle(exchange, variant) visit shop_path - find("#tab_producers a").click + within ".tab-buttons" do + click_link "Producers" + end expect(page).to have_content supplier.name end From a21ef195296d685d2c02b787825a4e138ed1d505 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 11 Mar 2020 10:53:20 +1100 Subject: [PATCH 012/130] Render only shop tabs within shop The include directive was listening to all $location paths including `#login` which is unrelated to the shop tabs. Angular tried to load the template `shop/login.html` which doesn't exist. We now whitelist the templates that can be included by having an include tag for each shop tab/page. --- app/views/shopping_shared/_tabs.html.haml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/shopping_shared/_tabs.html.haml b/app/views/shopping_shared/_tabs.html.haml index eb663613a5..4f6a2192ea 100644 --- a/app/views/shopping_shared/_tabs.html.haml +++ b/app/views/shopping_shared/_tabs.html.haml @@ -11,4 +11,5 @@ .page{ "ng-class" => "{ selected: selected() == '#{tab[:name]}' }" } %a{ href: "##{tab[:name]}" }=tab[:title] - .page-view{ "ng-include" => '"shop/" + selected() + ".html"' } + - shop_tabs.each do |tab| + .page-view{ ng: {include: "'shop/#{tab[:name]}.html'", if: "selected() == '#{tab[:name]}'" } } From 4b8d9d18d7fdcfdd4e2110b90fe36c1e1705ffdd Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 11 Mar 2020 10:56:21 +1100 Subject: [PATCH 013/130] Simplify PagesetCtrl --- .../darkswarm/controllers/pageset_ctrl.js.coffee | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/pageset_ctrl.js.coffee b/app/assets/javascripts/darkswarm/controllers/pageset_ctrl.js.coffee index 2417469135..9a7cd3af6b 100644 --- a/app/assets/javascripts/darkswarm/controllers/pageset_ctrl.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/pageset_ctrl.js.coffee @@ -1,10 +1,6 @@ Darkswarm.controller "PagesetCtrl", ($scope, $location) -> $scope.selected = -> - path = $location.path()?.match(/^\/\w+$/)?[0] - if path - path[1..] - else - $scope.defaultPage + $location.path()[1..] || $scope.defaultPage $scope.selectDefault = (selection) -> $scope.defaultPage = selection From 17751c448f1e24a61790d08a5817739af33e9f0c Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 12 Mar 2020 09:36:46 +1100 Subject: [PATCH 014/130] Rename PageSelectionCtrl for clarity --- .../{pageset_ctrl.js.coffee => page_selection_ctrl.js.coffee} | 2 +- app/views/shopping_shared/_tabs.html.haml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename app/assets/javascripts/darkswarm/controllers/{pageset_ctrl.js.coffee => page_selection_ctrl.js.coffee} (69%) diff --git a/app/assets/javascripts/darkswarm/controllers/pageset_ctrl.js.coffee b/app/assets/javascripts/darkswarm/controllers/page_selection_ctrl.js.coffee similarity index 69% rename from app/assets/javascripts/darkswarm/controllers/pageset_ctrl.js.coffee rename to app/assets/javascripts/darkswarm/controllers/page_selection_ctrl.js.coffee index 9a7cd3af6b..9d2d097fee 100644 --- a/app/assets/javascripts/darkswarm/controllers/pageset_ctrl.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/page_selection_ctrl.js.coffee @@ -1,4 +1,4 @@ -Darkswarm.controller "PagesetCtrl", ($scope, $location) -> +Darkswarm.controller "PageSelectionCtrl", ($scope, $location) -> $scope.selected = -> $location.path()[1..] || $scope.defaultPage diff --git a/app/views/shopping_shared/_tabs.html.haml b/app/views/shopping_shared/_tabs.html.haml index 4f6a2192ea..6f2119b4c7 100644 --- a/app/views/shopping_shared/_tabs.html.haml +++ b/app/views/shopping_shared/_tabs.html.haml @@ -3,7 +3,7 @@ - shop_tabs.each do |tab| = render "shopping_shared/tabs/#{tab[:name]}" - #shop-tabs{ ng: { controller: "PagesetCtrl", init: "selectDefault('#{shop_tabs.first[:name]}')", cloak: true } } + #shop-tabs{ ng: { controller: "PageSelectionCtrl", init: "selectDefault('#{shop_tabs.first[:name]}')", cloak: true } } .tab-buttons .row .columns.small-12.large-8 From 1d42ce885b2f6901495933f61faa1af2b187bd09 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 12 Mar 2020 11:15:56 +1100 Subject: [PATCH 015/130] Stay on shop page when opening login modal The login modal changes the URL to `#/login` which interfers with our shop pages. In order to show the right shop page, we need to know which pages are valid and where we have been before we clicked on Login. --- .../controllers/page_selection_ctrl.js.coffee | 19 +++++++++++++++---- app/helpers/shop_helper.rb | 4 ++++ app/views/shopping_shared/_tabs.html.haml | 7 +++---- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/page_selection_ctrl.js.coffee b/app/assets/javascripts/darkswarm/controllers/page_selection_ctrl.js.coffee index 9d2d097fee..cb2515ec7a 100644 --- a/app/assets/javascripts/darkswarm/controllers/page_selection_ctrl.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/page_selection_ctrl.js.coffee @@ -1,6 +1,17 @@ Darkswarm.controller "PageSelectionCtrl", ($scope, $location) -> - $scope.selected = -> - $location.path()[1..] || $scope.defaultPage + $scope.selectedPage = -> + # The path looks like `/contact` for the URL `https://ofn.org/shop#/contact`. + # We remove the slash at the beginning. + page = $location.path()[1..] + if page in $scope.whitelist + $scope.lastPage = page + page + else if page + # The path points to an unrelated path like `/login`. Stay where we were. + $scope.lastPage + else + $scope.whitelist[0] - $scope.selectDefault = (selection) -> - $scope.defaultPage = selection + $scope.whitelistPages = (pages) -> + $scope.whitelist = pages + $scope.lastPage = pages[0] diff --git a/app/helpers/shop_helper.rb b/app/helpers/shop_helper.rb index 29cb11b9bf..5aae96e101 100644 --- a/app/helpers/shop_helper.rb +++ b/app/helpers/shop_helper.rb @@ -35,6 +35,10 @@ module ShopHelper ].select{ |tab| tab[:show] } end + def shop_tab_names + shop_tabs.map { |tab| tab[:name] } + end + def show_home_tab? require_customer? || current_distributor.preferred_shopfront_message.present? end diff --git a/app/views/shopping_shared/_tabs.html.haml b/app/views/shopping_shared/_tabs.html.haml index 6f2119b4c7..a703851127 100644 --- a/app/views/shopping_shared/_tabs.html.haml +++ b/app/views/shopping_shared/_tabs.html.haml @@ -3,13 +3,12 @@ - shop_tabs.each do |tab| = render "shopping_shared/tabs/#{tab[:name]}" - #shop-tabs{ ng: { controller: "PageSelectionCtrl", init: "selectDefault('#{shop_tabs.first[:name]}')", cloak: true } } + #shop-tabs{ ng: { controller: "PageSelectionCtrl", init: "whitelistPages(#{shop_tab_names.to_json})", cloak: true } } .tab-buttons .row .columns.small-12.large-8 - shop_tabs.each do |tab| - .page{ "ng-class" => "{ selected: selected() == '#{tab[:name]}' }" } + .page{ "ng-class" => "{ selected: selectedPage() == '#{tab[:name]}' }" } %a{ href: "##{tab[:name]}" }=tab[:title] - - shop_tabs.each do |tab| - .page-view{ ng: {include: "'shop/#{tab[:name]}.html'", if: "selected() == '#{tab[:name]}'" } } + .page-view{ ng: {include: "'shop/' + selectedPage() + '.html'" } } From 5a9b5660f196ab86227f3d1dccdfeac8de58dd7a Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 18 Mar 2020 09:31:28 +1100 Subject: [PATCH 016/130] Simplify logic in PageSelectionCtrl --- .../controllers/page_selection_ctrl.js.coffee | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/darkswarm/controllers/page_selection_ctrl.js.coffee b/app/assets/javascripts/darkswarm/controllers/page_selection_ctrl.js.coffee index cb2515ec7a..9f4cbe2c1b 100644 --- a/app/assets/javascripts/darkswarm/controllers/page_selection_ctrl.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/page_selection_ctrl.js.coffee @@ -3,14 +3,14 @@ Darkswarm.controller "PageSelectionCtrl", ($scope, $location) -> # The path looks like `/contact` for the URL `https://ofn.org/shop#/contact`. # We remove the slash at the beginning. page = $location.path()[1..] - if page in $scope.whitelist - $scope.lastPage = page - page - else if page - # The path points to an unrelated path like `/login`. Stay where we were. - $scope.lastPage - else - $scope.whitelist[0] + + return $scope.whitelist[0] unless page + + # If the path points to an unrelated path like `/login`, stay where we were. + return $scope.lastPage unless page in $scope.whitelist + + $scope.lastPage = page + page $scope.whitelistPages = (pages) -> $scope.whitelist = pages From 7e00f78a7743e3a800b4cb98494b25174e7a3216 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 19 Mar 2020 15:49:29 +1100 Subject: [PATCH 017/130] Highlight menu item only when active or hovered The `:focus` selector meant that every link that was clicked on was still highlighted after going back or forward in the browser history. We don't need that selector because tabs you click on are then active and are highlighted anyway. --- app/assets/stylesheets/darkswarm/shop_tabs.css.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/darkswarm/shop_tabs.css.scss b/app/assets/stylesheets/darkswarm/shop_tabs.css.scss index 4da15cbe1b..a4c9c1f692 100644 --- a/app/assets/stylesheets/darkswarm/shop_tabs.css.scss +++ b/app/assets/stylesheets/darkswarm/shop_tabs.css.scss @@ -46,7 +46,7 @@ padding: 1em 2em; border: none; - &:hover, &:focus, &:active { + &:hover, &:active { color: $teal-500; } From 3de887e1d8d3bcd28fac798a9b6221d9f750fde0 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Thu, 19 Mar 2020 23:36:16 +0100 Subject: [PATCH 018/130] Remove some N+1s relating to address (found with bullet gem) --- app/controllers/producers_controller.rb | 2 +- app/controllers/shops_controller.rb | 2 +- app/helpers/injection_helper.rb | 8 +++++--- app/helpers/spree/base_helper_decorator.rb | 16 ++++++++++++++++ 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/app/controllers/producers_controller.rb b/app/controllers/producers_controller.rb index 0f45546fde..a5f4752c49 100644 --- a/app/controllers/producers_controller.rb +++ b/app/controllers/producers_controller.rb @@ -8,7 +8,7 @@ class ProducersController < BaseController .activated .visible .is_primary_producer - .includes(address: :state) + .includes(address: [:state, :country]) .includes(:properties) .includes(supplied_products: :properties) .all diff --git a/app/controllers/shops_controller.rb b/app/controllers/shops_controller.rb index 611baa11b8..d4f350b47f 100644 --- a/app/controllers/shops_controller.rb +++ b/app/controllers/shops_controller.rb @@ -8,7 +8,7 @@ class ShopsController < BaseController .activated .visible .is_distributor - .includes(address: :state) + .includes(address: [:state, :country]) .includes(:properties) .includes(supplied_products: :properties) .all diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index 6ebf77a8fb..a10a4b1fee 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -3,7 +3,7 @@ require 'open_food_network/enterprise_injection_data' module InjectionHelper include SerializerHelper - def inject_enterprises(enterprises = Enterprise.activated.includes(address: :state).all) + def inject_enterprises(enterprises = Enterprise.activated.includes(address: [:state, :country]).all) inject_json_ams( "enterprises", enterprises, @@ -35,13 +35,15 @@ module InjectionHelper inject_json_ams( "enterprises", - Enterprise.activated.visible.select(select_only).includes(address: :state).all, + Enterprise.activated.visible.select(select_only).includes(address: [:state, :country]).all, Api::EnterpriseShopfrontListSerializer ) end def inject_enterprise_and_relatives - inject_json_ams "enterprises", current_distributor.relatives_including_self.activated.includes(address: :state).all, Api::EnterpriseSerializer, enterprise_injection_data + inject_json_ams "enterprises", + current_distributor.relatives_including_self.activated.includes(address: [:state, :country]).all, + Api::EnterpriseSerializer, enterprise_injection_data end def inject_group_enterprises diff --git a/app/helpers/spree/base_helper_decorator.rb b/app/helpers/spree/base_helper_decorator.rb index 67ea0a711d..8d80c51d9a 100644 --- a/app/helpers/spree/base_helper_decorator.rb +++ b/app/helpers/spree/base_helper_decorator.rb @@ -5,5 +5,21 @@ module Spree def variant_options(v, _options = {}) v.options_text end + + # Overriden to eager-load :states + def available_countries + checkout_zone = Zone.find_by_name(Spree::Config[:checkout_zone]) + + if checkout_zone && checkout_zone.kind == 'country' + countries = checkout_zone.country_list + else + countries = Country.includes(:states).all + end + + countries.collect do |country| + country.name = Spree.t(country.iso, scope: 'country_names', default: country.name) + country + end.sort { |a, b| a.name <=> b.name } + end end end From 7baa875a9189f140b22ea845a4527b8403a5c545 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Thu, 19 Mar 2020 23:40:57 +0100 Subject: [PATCH 019/130] Fix big N+1 issues in enterprises#edit for superadmin The page is usable now as superadmin. Roughly 10x faster... --- app/controllers/admin/enterprises_controller.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index 8369c5283f..4fad65ce37 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -32,6 +32,11 @@ module Admin end end + def edit + @object = Enterprise.where(permalink: params[:id]).includes(users: [:ship_address, :bill_address]).first + super + end + def welcome render layout: "spree/layouts/bare_admin" end @@ -172,12 +177,14 @@ module Admin end def load_methods_and_fees + enterprise_payment_methods = @enterprise.payment_methods.to_a + enterprise_shipping_methods = @enterprise.shipping_methods.to_a # rubocop:disable Style/TernaryParentheses @payment_methods = Spree::PaymentMethod.managed_by(spree_current_user).sort_by! do |pm| - [(@enterprise.payment_methods.include? pm) ? 0 : 1, pm.name] + [(enterprise_payment_methods.include? pm) ? 0 : 1, pm.name] end @shipping_methods = Spree::ShippingMethod.managed_by(spree_current_user).sort_by! do |sm| - [(@enterprise.shipping_methods.include? sm) ? 0 : 1, sm.name] + [(enterprise_shipping_methods.include? sm) ? 0 : 1, sm.name] end # rubocop:enable Style/TernaryParentheses From 22384cb4da0b139a19bc40095b14eb24994200d1 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 20 Mar 2020 11:43:55 +1100 Subject: [PATCH 020/130] Display shipping and billing address on invoices --- .../spree/admin/orders/invoice.html.haml | 9 ++- config/locales/en.yml | 1 + .../admin/orders/invoice.html.haml_spec.rb | 67 +++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 spec/views/spree/admin/orders/invoice.html.haml_spec.rb diff --git a/app/views/spree/admin/orders/invoice.html.haml b/app/views/spree/admin/orders/invoice.html.haml index 982ef43f59..74e626d77d 100644 --- a/app/views/spree/admin/orders/invoice.html.haml +++ b/app/views/spree/admin/orders/invoice.html.haml @@ -29,16 +29,21 @@ %td{width: "10%" }   %td{ :align => "right" } - %strong= "#{t('.to')}: #{@order.ship_address.full_name}" + %strong= "#{t('.to')}: #{@order.bill_address.full_name}" - if @order.andand.customer.andand.code.present? %br = "#{t('.code')}: #{@order.customer.code}" %br - = @order.ship_address.full_address + = @order.bill_address.full_address %br - if @order.andand.customer.andand.email.present? = "#{@order.customer.email}," = "#{@order.bill_address.phone}" + %br + %strong= "#{t('.shipping')}: #{@order.shipping_method&.name}" + - if @order.shipping_method&.require_ship_address && @order.ship_address != @order.bill_address + %br + = @order.ship_address.full_address = render 'spree/admin/orders/invoice_table' diff --git a/config/locales/en.yml b/config/locales/en.yml index 804ccbbcef..edbc0de299 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -3190,6 +3190,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using code: "Code" from: "From" to: "To" + shipping: "Shipping" form: distribution_fields: title: "Distribution" diff --git a/spec/views/spree/admin/orders/invoice.html.haml_spec.rb b/spec/views/spree/admin/orders/invoice.html.haml_spec.rb new file mode 100644 index 0000000000..b6bea69c67 --- /dev/null +++ b/spec/views/spree/admin/orders/invoice.html.haml_spec.rb @@ -0,0 +1,67 @@ +require "spec_helper" + +describe "spree/admin/orders/invoice.html.haml" do + let(:shop) { create(:distributor_enterprise) } + let(:order) { create(:completed_order_with_totals, distributor: shop) } + let(:adas_address) do + Spree::Address.new( + firstname: "Ada", + lastname: "Lovelace", + phone: "0404 123 456", + address1: "2 Mahome St", + city: "Thornbury", + zipcode: "3071", + state_id: 1, + state_name: "Victoria", + ) + end + let(:adas_address_display) { "2 Mahome St, Thornbury, 3071, Victoria" } + + before do + assign(:order, order) + end + + it "displays the customer code" do + order.customer = Customer.create!( + user: order.user, + email: order.user.email, + enterprise: order.distributor, + code: "Money Penny", + ) + render + expect(rendered).to have_content "Code: Money Penny" + end + + it "displays the billing and shipping address" do + order.bill_address = adas_address + render + expect(rendered).to have_content "To: Ada Lovelace" + expect(rendered).to have_content adas_address.phone + expect(rendered).to have_content adas_address_display + end + + it "displays shipping info" do + order.shipping_method.update_attributes!( + name: "Home delivery", + require_ship_address: true, + ) + order.ship_address = adas_address + + render + expect(rendered).to have_content "Shipping: Home delivery" + expect(rendered).to have_content adas_address_display + end + + it "prints address once if billing and shipping address are the same" do + order.bill_address = adas_address + order.ship_address = Spree::Address.new(order.bill_address.attributes) + order.shipping_method.update_attributes!( + name: "Home delivery", + require_ship_address: true, + ) + + render + expect(rendered).to have_content "Shipping: Home delivery" + expect(rendered.scan(/2 Mahome St, Thornbury, 3071/).count).to eq 1 + end +end From 25b11f1f4b03886c6ed26cf6ddd1def7671737f0 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 20 Mar 2020 15:40:04 +1100 Subject: [PATCH 021/130] Display delivery details in the header --- .../spree/admin/orders/invoice.html.haml | 37 +++++++++++-------- config/locales/en.yml | 2 +- .../admin/orders/invoice.html.haml_spec.rb | 25 ++++++++----- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/app/views/spree/admin/orders/invoice.html.haml b/app/views/spree/admin/orders/invoice.html.haml index 74e626d77d..bc2b4dc556 100644 --- a/app/views/spree/admin/orders/invoice.html.haml +++ b/app/views/spree/admin/orders/invoice.html.haml @@ -17,7 +17,7 @@ %td{ :align => "right" } %h4= @order.order_cycle.andand.name %tr{ valign: "top" } - %td{ :align => "left" } + %td{ align: "left", colspan: 3 } %strong= "#{t('.from')}: #{@order.distributor.name}" - if @order.distributor.abn.present? %br @@ -26,10 +26,14 @@ = @order.distributor.address.full_address %br = @order.distributor.contact.email - %td{width: "10%" } + %tr{ valign: "top" } + %td{ colspan: 3 }   - %td{ :align => "right" } - %strong= "#{t('.to')}: #{@order.bill_address.full_name}" + %tr{ valign: "top" } + %td{ align: "left" } + %strong= "#{t('.to')}:" + %br + = @order.bill_address.full_name - if @order.andand.customer.andand.code.present? %br = "#{t('.code')}: #{@order.customer.code}" @@ -39,23 +43,26 @@ - if @order.andand.customer.andand.email.present? = "#{@order.customer.email}," = "#{@order.bill_address.phone}" - %br + %td +   + %td{ align: "left", style: "border-left: .1em solid black; padding-left: 1em" } %strong= "#{t('.shipping')}: #{@order.shipping_method&.name}" - - if @order.shipping_method&.require_ship_address && @order.ship_address != @order.bill_address + - if @order.shipping_method&.require_ship_address + %br + = @order.ship_address.full_name %br = @order.ship_address.full_address + %br + = @order.ship_address.phone + - if @order.special_instructions.present? + %br + %br + %strong= t :customer_instructions + = @order.special_instructions + = render 'spree/admin/orders/invoice_table' -- if @order.special_instructions.present? - %p.callout - %strong - = t :customer_instructions - %p - %em= @order.special_instructions - %p -   - - if @order.distributor.invoice_text.present? %p = @order.distributor.invoice_text diff --git a/config/locales/en.yml b/config/locales/en.yml index edbc0de299..86c8f1611e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -3189,7 +3189,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using tax_invoice: "TAX INVOICE" code: "Code" from: "From" - to: "To" + to: "Bill to" shipping: "Shipping" form: distribution_fields: diff --git a/spec/views/spree/admin/orders/invoice.html.haml_spec.rb b/spec/views/spree/admin/orders/invoice.html.haml_spec.rb index b6bea69c67..8d7a82c129 100644 --- a/spec/views/spree/admin/orders/invoice.html.haml_spec.rb +++ b/spec/views/spree/admin/orders/invoice.html.haml_spec.rb @@ -32,10 +32,10 @@ describe "spree/admin/orders/invoice.html.haml" do expect(rendered).to have_content "Code: Money Penny" end - it "displays the billing and shipping address" do + it "displays the billing address" do order.bill_address = adas_address render - expect(rendered).to have_content "To: Ada Lovelace" + expect(rendered).to have_content "Ada Lovelace" expect(rendered).to have_content adas_address.phone expect(rendered).to have_content adas_address_display end @@ -49,19 +49,26 @@ describe "spree/admin/orders/invoice.html.haml" do render expect(rendered).to have_content "Shipping: Home delivery" + expect(rendered).to have_content adas_address.phone expect(rendered).to have_content adas_address_display end - it "prints address once if billing and shipping address are the same" do - order.bill_address = adas_address - order.ship_address = Spree::Address.new(order.bill_address.attributes) + it "displays special instructions" do + order.special_instructions = "The combination is 12345." + + render + expect(rendered).to have_content "The combination is 12345." + end + + it "hides billing address for pickups" do + order.ship_address = adas_address order.shipping_method.update_attributes!( - name: "Home delivery", - require_ship_address: true, + name: "Pickup", + require_ship_address: false, ) render - expect(rendered).to have_content "Shipping: Home delivery" - expect(rendered.scan(/2 Mahome St, Thornbury, 3071/).count).to eq 1 + expect(rendered).to have_content "Shipping: Pickup" + expect(rendered).to_not have_content adas_address_display end end From c788f1ae5745629144f8677962863ef3a43e4988 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Fri, 20 Mar 2020 19:32:35 +1100 Subject: [PATCH 022/130] Updating translations for config/locales/it.yml --- config/locales/it.yml | 89 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 5 deletions(-) diff --git a/config/locales/it.yml b/config/locales/it.yml index d4452aba36..cb576b0cb1 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -791,13 +791,13 @@ it: producer_shop_description_text2: Un Negozio produttore è solo per i tuoi prodotti. Se vuoi vendere prodotti altrui, seleziona "Hub produttore". producer_hub: Hub produttore producer_hub_text: Vendi prodotti tuoi e di altri - producer_hub_description_text: La tua azienda è un pilastro del nostro sistema di cibo locale. Puoi vendere i tuoi prodotti, ma anche prodotti di tuoi produttori fidati attraverso la tua vetrina di Open Food Network. + producer_hub_description_text: La tua azienda è un pilastro della nostra filiera del cibo. Puoi vendere i tuoi prodotti, ma anche prodotti di tuoi produttori fidati attraverso la tua vetrina di Open Food Network. profile: Solo Profilo get_listing: Ottieni un listino profile_description_text: Le persone ti possono trovare e contattare su OFN. La tua azienda sarà visibile sulla mappa e potrà essere trovata nelle ricerche degli utenti. hub_shop: Isola logistica hub_shop_text: Vendi prodotti di altri - hub_shop_description_text: La tua azienda è la spina dorsale del vostro sistema alimentare locale. Puoi aggregare i prodotti di altre aziende e venderli attraverso il tuo negozio su Open Food Network. + hub_shop_description_text: La tua azienda è la spina dorsale della vostra filiera locale del cibo. Puoi aggregare i prodotti di altre aziende e venderli attraverso il tuo negozio su Open Food Network. choose_option: Per favore seleziona una delle opzioni. change_now: Modifica ora enterprise_user_index: @@ -1114,6 +1114,7 @@ it: already_ordered: cart: "carrello" message_html: "Hai già un ordine per questo ciclo d'ordine. Controlla%{cart} per vedere gli articoli ordinati in precedenza. Puoi anche cancellare gli articoli finché il ciclo dell'ordine è aperto." + failed: "Checkout fallito. Contattaci per poter processare il tuo ordine." shops: hubs: show_closed_shops: "Mostra i negozi chiusi" @@ -1341,7 +1342,7 @@ it: brandstory_part3: "Così costruiamo un mercato online che riequilibra le parti. E' trasparente, così crea raporti reali. E' open source, così è di tutti. Si adatta dalla scala regionale a quella nazionale, così le persone lo adottano in tutto il mondo." brandstory_part4: "Funziona ovunque. Cambia ogni cosa." brandstory_part5_strong: "Noi lo chiamiamo Open Food Network." - brandstory_part6: "Noi tutti amiamo il cibo. Adesso possiamo amare anche il nostro sistema cibo." + brandstory_part6: "Noi tutti amiamo il cibo. Adesso possiamo amare anche la nostra filiera del cibo." learn_body: "Esplora modelli, storie e risorse per aiutarti e sviluppare la tua azienda o la tua organizzazione in modo equo. Cerca corsi, eventi e altre opportunità per imparare da chi è come te." learn_cta: "Prendi ispirazione" connect_body: "Cerca la nostra intera lista di produttori, hub e gruppi per trovare chi è vicino a te. Aggiungi alla lista di OFN la tua azienda o organizzazionie così i clienti potranno trovarti. Unisciti alla comunità per avere consigli e risolvere problemi insieme." @@ -1355,7 +1356,7 @@ it: system_step3_text: "Attendi per la tua consegna, o visita il tuo produttore o distributore per una connessione più personale con il tuo cibo. Comprare cibo così variegato come lo intende natura." cta_headline: "La spesa che rende il mondo un posto migliore." cta_label: "Sono Pronto" - stats_headline: "Stiamo creando un nuovo sistema del cibo." + stats_headline: "Stiamo creando una nuova filiera del cibo." stats_producers: "produttori" stats_shops: "negozi" stats_shoppers: "clienti" @@ -1486,6 +1487,7 @@ it: shopping_oc_closed_description: "Per favore aspetta l'apertura del prossimo ciclo (o contattaci direttamente per sapere se possiamo accettare ordini fuori tempo)" shopping_oc_last_closed: "L'ultimo ciclo è chiuso %{distance_of_time} fa" shopping_oc_next_open: "Il prossimo ciclo aprirà tra %{distance_of_time}" + shopping_oc_select: "Seleziona..." shopping_tabs_home: "Home" shopping_tabs_shop: "Negozio" shopping_tabs_about: "Descrizione" @@ -1859,6 +1861,7 @@ it: headline: "Finito!" thanks: "Grazie per aver riempito i dettagli per %{enterprise}." login: "Puoi cambiare o aggiornare la tua azienda ad ogni passo accedendo a Open Food Network e andando su Amministrazione." + action: "Vai alla dashboard aziendale" back: "Indietro" continue: "Continua" action_or: "O" @@ -1944,6 +1947,7 @@ it: tax_category: "Categoria di imposta" calculator: "Calcolatore" calculator_values: "Valori del calcolatore" + calculator_settings_warning: "Se stai modificando il tipo di calcolatore, devi prima salvare prima di editare i setting del calcolatore" flat_percent_per_item: "Percentuale (per prodotto)" flat_rate_per_item: "Tariffa fissa (per articolo)" flat_rate_per_order: "Tariffa fissa (per gentile richiesta)" @@ -2274,6 +2278,7 @@ it: enterprise_register_success_notice: "Congratulazioni! L registrazione di %{enterprise} è completa!" enterprise_bulk_update_success_notice: "Aziende aggiornate con successo" enterprise_bulk_update_error: 'Aggiornamento fallito' + enterprise_shop_show_error: "Il negozio che stai cercando non esiste o risulta inattivo su OFN. Prego visita altri shop." order_cycles_create_notice: 'Il tuo ciclo di richieste è stato creato.' order_cycles_update_notice: 'Il tuo ciclo di richieste è stato aggiornato' order_cycles_bulk_update_notice: 'I cicli di richieste sono stati aggiornati.' @@ -2347,7 +2352,7 @@ it: alimentare locale attraverso Open Food Network sarà sempre gratuito.. hub_shop: Isola logistica hub_shop_text1: > - La tua azienda è la spina dorsale del vostro sistema alimentare locale. + La tua azienda è la spina dorsale della vostra filiera alimentare locale. Puoi aggregare i prodotti di altre aziende e venderli attraverso il tuo negozio su Open Food Network. hub_shop_text2: > @@ -2431,6 +2436,8 @@ it: exchange_products: load_more_variants: "Carica più Varianti" load_all_variants: "Carica tutte le Varianti" + select_all_variants: "Seleziona tutto %{total_number_of_variants} varianti" + variants_loaded: "%{num_of_variants_loaded} di %{total_number_of_variants} varianti caricate" loading_variants: "Caricamento Varianti" tag_rules: shipping_method_tagged_top: "Metodi di consegna taggati" @@ -2591,9 +2598,78 @@ it: have_an_account: "Hai già un account?" action_login: "Accedi ora." inflections: + each: + one: "ogni" + other: "ogni" bunch: one: "mazzo" other: "grappoli" + pack: + one: "imballare" + other: "confezioni" + box: + one: "scatola" + other: "scatole" + bottle: + one: "bottiglia" + other: "bottiglie" + jar: + one: "vaso" + other: "barattoli" + head: + one: "testa" + other: "teste" + bag: + one: "Borsa" + other: "borse" + loaf: + one: "pagnotta" + other: "pagnotte" + single: + one: "singolo" + other: "singoli" + tub: + one: "vasca" + other: "vasche" + punnet: + one: "cestino" + other: "cestini" + packet: + one: "pacchetto" + other: "pacchetti" + item: + one: "articolo" + other: "elementi" + dozen: + one: "dozzina" + other: "dozzine" + unit: + one: "unità" + other: "unità" + serve: + one: "servire" + other: "porzioni" + tray: + one: "vassoio" + other: "vassoi" + piece: + one: "pezzo" + other: "pezzi" + pot: + one: "pentola" + other: "vasi" + bundle: + one: "fascio" + other: "gruppi" + flask: + one: "borraccia" + other: "fiaschi" + basket: + one: "cestino" + other: "cesti" + sack: + one: "sacco" + other: "sacchi" producers: signup: start_free_profile: "Inizia con un profilo gratuito e migliora quando sei pronto!" @@ -2836,6 +2912,8 @@ it: zipcode: CAP weight: Peso (kg) error_user_destroy_with_orders: "Gli utenti con ordini completi non dovrebbero essere cancellati" + cannot_create_payment_without_payment_methods: "Non è possibile creare un pagamento per un ordine senza alcun metodo di pagamento definito." + please_define_payment_methods: "Definisci prima alcuni metodi di pagamento." options: "Opzioni" actions: update: "Aggiorna" @@ -2922,6 +3000,7 @@ it: capture: "Cattura" ship: "Spedizione" edit: "Modifica" + order_not_updated: "L'ordine non può essere aggiornato" note: "Nota" first: "Primo" last: "Ultimo" From 540b26105f9b10b6f034e14e02a3c752f20730d7 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Thu, 20 Feb 2020 17:16:24 +0000 Subject: [PATCH 023/130] Delete dead code in oc helper --- app/helpers/order_cycles_helper.rb | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/app/helpers/order_cycles_helper.rb b/app/helpers/order_cycles_helper.rb index 8eb32e7d66..d5184a0436 100644 --- a/app/helpers/order_cycles_helper.rb +++ b/app/helpers/order_cycles_helper.rb @@ -47,17 +47,6 @@ module OrderCyclesHelper end end - def order_cycle_options - @order_cycles. - with_distributor(current_distributor). - map { |oc| [order_cycle_close_to_s(oc.orders_close_at), oc.id] } - end - - def order_cycle_close_to_s(orders_close_at) - "%s (%s)" % [orders_close_at.strftime("#{orders_close_at.day.ordinalize} %b"), - distance_of_time_in_words_to_now(orders_close_at)] - end - def active_order_cycle_for_distributor?(_distributor) OrderCycle.active.with_distributor(@distributor).present? end From d2eee1dafdeb83e5f4864a22420a1f35b7cd066d Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Thu, 20 Feb 2020 17:42:26 +0000 Subject: [PATCH 024/130] Extract and fix reset_order_cycle logic from set_order_cycles: ActiveRecord.count will reload the relation and ignore the changes done by the TagRuleApplicator --- app/controllers/base_controller.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/controllers/base_controller.rb b/app/controllers/base_controller.rb index b01b5b002e..45b6a6f155 100644 --- a/app/controllers/base_controller.rb +++ b/app/controllers/base_controller.rb @@ -37,9 +37,17 @@ class BaseController < ApplicationController current_customer.andand.tag_list) applicator.filter!(@order_cycles) - # And default to the only order cycle if there's only the one - if @order_cycles.count == 1 - current_order(true).set_order_cycle! @order_cycles.first - end + reset_order_cycle + end + + # Default to the only order cycle if there's only one + # + # Here we need to use @order_cycles.size not @order_cycles.count + # because TagRuleApplicator changes ActiveRecord::Relation @order_cycles + # and these changes are not seen if the relation is reloaded with count + def reset_order_cycle + return if @order_cycles.size != 1 + + current_order(true).set_order_cycle! @order_cycles.first end end From d5cf355a11b57553ad300e5cf44391d884fcabc5 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Thu, 20 Feb 2020 19:19:04 +0000 Subject: [PATCH 025/130] Call fetch_order_cycles from reset_order_cycles so we dont repeat the calculation BaseController#set_order_cycle cant be used in reset_order_cycle because it will empty the order if the OC is not defined previously --- app/controllers/base_controller.rb | 18 ++++++++++++------ app/controllers/enterprises_controller.rb | 4 ++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app/controllers/base_controller.rb b/app/controllers/base_controller.rb index 45b6a6f155..321a4c7e35 100644 --- a/app/controllers/base_controller.rb +++ b/app/controllers/base_controller.rb @@ -29,15 +29,21 @@ class BaseController < ApplicationController return end - @order_cycles = OrderCycle.with_distributor(@distributor).active - .order(@distributor.preferred_shopfront_order_cycle_order) + fetch_order_cycles(@distributor) - applicator = OpenFoodNetwork::TagRuleApplicator.new(@distributor, + set_order_cycle + end + + def fetch_order_cycles(distributor) + return if @order_cycles.present? + + @order_cycles = OrderCycle.with_distributor(distributor).active + .order(distributor.preferred_shopfront_order_cycle_order) + + applicator = OpenFoodNetwork::TagRuleApplicator.new(distributor, "FilterOrderCycles", current_customer.andand.tag_list) applicator.filter!(@order_cycles) - - reset_order_cycle end # Default to the only order cycle if there's only one @@ -45,7 +51,7 @@ class BaseController < ApplicationController # Here we need to use @order_cycles.size not @order_cycles.count # because TagRuleApplicator changes ActiveRecord::Relation @order_cycles # and these changes are not seen if the relation is reloaded with count - def reset_order_cycle + def set_order_cycle return if @order_cycles.size != 1 current_order(true).set_order_cycle! @order_cycles.first diff --git a/app/controllers/enterprises_controller.rb b/app/controllers/enterprises_controller.rb index c1b5b5104f..6e3e0f0c0a 100644 --- a/app/controllers/enterprises_controller.rb +++ b/app/controllers/enterprises_controller.rb @@ -96,8 +96,8 @@ class EnterprisesController < BaseController end def reset_order_cycle(order, distributor) - order_cycle_options = OrderCycle.active.with_distributor(distributor) - order.order_cycle = order_cycle_options.first if order_cycle_options.count == 1 + fetch_order_cycles(distributor) + order.order_cycle = @order_cycles.first if @order_cycles.size == 1 end def shop_order_cycles From 1c1f066884d23e2ff52ec6093d4b6bf32940329b Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Fri, 21 Feb 2020 10:15:59 +0000 Subject: [PATCH 026/130] Extract Shop::OrderCyclesList from base_controller --- app/controllers/base_controller.rb | 14 +------------- app/controllers/enterprises_controller.rb | 4 ++-- app/services/shop/order_cycles_list.rb | 22 ++++++++++++++++++++++ 3 files changed, 25 insertions(+), 15 deletions(-) create mode 100644 app/services/shop/order_cycles_list.rb diff --git a/app/controllers/base_controller.rb b/app/controllers/base_controller.rb index 321a4c7e35..afaa3b42d3 100644 --- a/app/controllers/base_controller.rb +++ b/app/controllers/base_controller.rb @@ -29,23 +29,11 @@ class BaseController < ApplicationController return end - fetch_order_cycles(@distributor) + @order_cycles = Shop::OrderCyclesList.new(@distributor, current_customer).call set_order_cycle end - def fetch_order_cycles(distributor) - return if @order_cycles.present? - - @order_cycles = OrderCycle.with_distributor(distributor).active - .order(distributor.preferred_shopfront_order_cycle_order) - - applicator = OpenFoodNetwork::TagRuleApplicator.new(distributor, - "FilterOrderCycles", - current_customer.andand.tag_list) - applicator.filter!(@order_cycles) - end - # Default to the only order cycle if there's only one # # Here we need to use @order_cycles.size not @order_cycles.count diff --git a/app/controllers/enterprises_controller.rb b/app/controllers/enterprises_controller.rb index 6e3e0f0c0a..717de0f29b 100644 --- a/app/controllers/enterprises_controller.rb +++ b/app/controllers/enterprises_controller.rb @@ -96,8 +96,8 @@ class EnterprisesController < BaseController end def reset_order_cycle(order, distributor) - fetch_order_cycles(distributor) - order.order_cycle = @order_cycles.first if @order_cycles.size == 1 + order_cycles = Shop::OrderCyclesList.new(distributor, current_customer).call + order.order_cycle = order_cycles.first if order_cycles.size == 1 end def shop_order_cycles diff --git a/app/services/shop/order_cycles_list.rb b/app/services/shop/order_cycles_list.rb new file mode 100644 index 0000000000..63327036a4 --- /dev/null +++ b/app/services/shop/order_cycles_list.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +# Lists available order cycles for a given customer in a given distributor + +class OrderCyclesList + def initialize(distributor, customer) + @distributor = distributor + @customer = customer + end + + def call + order_cycles = OrderCycle.with_distributor(@distributor).active + .order(@distributor.preferred_shopfront_order_cycle_order) + + applicator = OpenFoodNetwork::TagRuleApplicator.new(@distributor, + "FilterOrderCycles", + @customer.andand.tag_list) + applicator.filter!(order_cycles) + + order_cycles + end +end From e2940eb9ff26dd9c24b142db7452c866f3e3250f Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Fri, 21 Feb 2020 11:05:47 +0000 Subject: [PATCH 027/130] Add namespace to OrderCyclesList Shop will probably be the name of the future engine/domain for the shopfront pages --- app/services/shop/order_cycles_list.rb | 29 +++++++++++++------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/app/services/shop/order_cycles_list.rb b/app/services/shop/order_cycles_list.rb index 63327036a4..730343ca6e 100644 --- a/app/services/shop/order_cycles_list.rb +++ b/app/services/shop/order_cycles_list.rb @@ -1,22 +1,23 @@ # frozen_string_literal: true # Lists available order cycles for a given customer in a given distributor +module Shop + class OrderCyclesList + def initialize(distributor, customer) + @distributor = distributor + @customer = customer + end -class OrderCyclesList - def initialize(distributor, customer) - @distributor = distributor - @customer = customer - end + def call + order_cycles = OrderCycle.with_distributor(@distributor).active + .order(@distributor.preferred_shopfront_order_cycle_order) - def call - order_cycles = OrderCycle.with_distributor(@distributor).active - .order(@distributor.preferred_shopfront_order_cycle_order) + applicator = OpenFoodNetwork::TagRuleApplicator.new(@distributor, + "FilterOrderCycles", + @customer.andand.tag_list) + applicator.filter!(order_cycles) - applicator = OpenFoodNetwork::TagRuleApplicator.new(@distributor, - "FilterOrderCycles", - @customer.andand.tag_list) - applicator.filter!(order_cycles) - - order_cycles + order_cycles + end end end From 0e62dc04bdee27cb137488689923cbec513340c8 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Fri, 21 Feb 2020 12:10:01 +0000 Subject: [PATCH 028/130] Improve comments to explain glitch on set_order_cycle and OrderCyclesList --- app/controllers/base_controller.rb | 4 ++-- app/services/shop/order_cycles_list.rb | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/app/controllers/base_controller.rb b/app/controllers/base_controller.rb index afaa3b42d3..dab3eb6752 100644 --- a/app/controllers/base_controller.rb +++ b/app/controllers/base_controller.rb @@ -37,8 +37,8 @@ class BaseController < ApplicationController # Default to the only order cycle if there's only one # # Here we need to use @order_cycles.size not @order_cycles.count - # because TagRuleApplicator changes ActiveRecord::Relation @order_cycles - # and these changes are not seen if the relation is reloaded with count + # because OrderCyclesList returns a modified ActiveRecord::Relation + # and these modifications are not seen if it is reloaded with count def set_order_cycle return if @order_cycles.size != 1 diff --git a/app/services/shop/order_cycles_list.rb b/app/services/shop/order_cycles_list.rb index 730343ca6e..d4ee5e07c3 100644 --- a/app/services/shop/order_cycles_list.rb +++ b/app/services/shop/order_cycles_list.rb @@ -12,6 +12,14 @@ module Shop order_cycles = OrderCycle.with_distributor(@distributor).active .order(@distributor.preferred_shopfront_order_cycle_order) + apply_tag_rules!(order_cycles) + end + + private + + # order_cycles is a ActiveRecord::Relation that is modified with reject in the TagRuleApplicator + # If this relation is reloaded (for example by calling count on it), the modifications are lost + def apply_tag_rules!(order_cycles) applicator = OpenFoodNetwork::TagRuleApplicator.new(@distributor, "FilterOrderCycles", @customer.andand.tag_list) From e8692fec4c5a74c4ab343e8dda3eff49738043de Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 20 Mar 2020 09:30:33 +0100 Subject: [PATCH 029/130] Temporarily hide OC selector in checkout page --- app/views/checkout/edit.html.haml | 2 +- app/views/shopping_shared/_header.html.haml | 5 +++-- spec/features/consumer/shopping/embedded_shopfronts_spec.rb | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/views/checkout/edit.html.haml b/app/views/checkout/edit.html.haml index 312b7b5a79..27271ae6d3 100644 --- a/app/views/checkout/edit.html.haml +++ b/app/views/checkout/edit.html.haml @@ -15,7 +15,7 @@ %strong = pickup_time current_order_cycle - = render partial: "shopping_shared/header" + = render partial: "shopping_shared/header", locals: { hide_oc_selector: true } %accordion{"close-others" => "false"} %checkout.row{"ng-controller" => "CheckoutCtrl"} diff --git a/app/views/shopping_shared/_header.html.haml b/app/views/shopping_shared/_header.html.haml index 03661a22ea..37806539e2 100644 --- a/app/views/shopping_shared/_header.html.haml +++ b/app/views/shopping_shared/_header.html.haml @@ -10,5 +10,6 @@ = distributor.name %location= distributor.address.city - .show-for-large-up.large-4.columns - = render partial: "shopping_shared/order_cycles" + - unless defined? hide_oc_selector + .show-for-large-up.large-4.columns + = render partial: "shopping_shared/order_cycles" diff --git a/spec/features/consumer/shopping/embedded_shopfronts_spec.rb b/spec/features/consumer/shopping/embedded_shopfronts_spec.rb index 4a70dbf672..290c6af516 100644 --- a/spec/features/consumer/shopping/embedded_shopfronts_spec.rb +++ b/spec/features/consumer/shopping/embedded_shopfronts_spec.rb @@ -53,7 +53,7 @@ feature "Using embedded shopfront functionality", js: true do expect(page).to have_text 'Your shopping cart' find('a#checkout-link').click - expect(page).to have_text 'Checkout now' + expect(page).to have_text 'Ok, ready to checkout?' click_button 'Login' login_with_modal From 7582df27713869594937b95ac20e44dfc4f282a8 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Sat, 21 Mar 2020 00:37:37 +1100 Subject: [PATCH 030/130] Updating translations for config/locales/en_CA.yml --- config/locales/en_CA.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/locales/en_CA.yml b/config/locales/en_CA.yml index a2b274d610..aea11218e9 100644 --- a/config/locales/en_CA.yml +++ b/config/locales/en_CA.yml @@ -1486,6 +1486,7 @@ en_CA: shopping_oc_closed_description: "Please wait until the next cycle opens (or contact us directly to see if we can accept any late orders)" shopping_oc_last_closed: "The last cycle closed %{distance_of_time} ago" shopping_oc_next_open: "The next cycle opens in %{distance_of_time}" + shopping_oc_select: "Select" shopping_tabs_home: "Home" shopping_tabs_shop: "Shop" shopping_tabs_about: "About" From 70643a84b22126c6b37c171a934e83253cc97f68 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 20 Mar 2020 18:44:23 +0100 Subject: [PATCH 031/130] Update all locales with the latest Transifex translations --- config/locales/en_CA.yml | 19 +++++---- config/locales/fr.yml | 2 +- config/locales/fr_CA.yml | 1 + config/locales/it.yml | 89 +++++++++++++++++++++++++++++++++++++--- 4 files changed, 96 insertions(+), 15 deletions(-) diff --git a/config/locales/en_CA.yml b/config/locales/en_CA.yml index a2b274d610..893ada91df 100644 --- a/config/locales/en_CA.yml +++ b/config/locales/en_CA.yml @@ -871,10 +871,10 @@ en_CA: incoming: "2. Incoming Products" outgoing: "3. Outgoing Products" exchange_form: - pickup_time_tip: Date orders will be ready for pick-up/delivery. Or enter the customer type (e.g. 'wholesale', 'members only'...) if you are setting up multiple shopfronts. + pickup_time_tip: 'This phrase will help buyers select their shop from multiple shops. It will be preceded by the phrase "Orders for". So enter a pick-up date, or buyer group... ' pickup_instructions_placeholder: "Pick-up instructions" pickup_instructions_tip: These instructions are shown to customers after they complete an order - pickup_time_placeholder: "Ready for (ie. Date/Time)" + pickup_time_placeholder: "Order for (ie. Date, or buyer group...)" receival_instructions_placeholder: "Receival instructions" add_fee: 'Add fee' remove: 'Remove' @@ -921,7 +921,7 @@ en_CA: distributors: distributors variants: variants simple_form: - ready_for: Ready for + ready_for: Order for ready_for_placeholder: Date / time customer_instructions: Customer instructions customer_instructions_placeholder: 'Notes:' @@ -1362,7 +1362,7 @@ en_CA: stats_orders: "orders" checkout_title: Checkout checkout_now: Checkout now - checkout_order_ready: Order ready for + checkout_order_ready: Order for checkout_hide: Hide checkout_expand: Expand checkout_headline: "Ok, ready to checkout?" @@ -1374,7 +1374,7 @@ en_CA: checkout_default_ship_address: "Save as default shipping address" checkout_method_free: Free checkout_address_same: Shipping address same as billing address? - checkout_ready_for: "Ready for:" + checkout_ready_for: "Order for: " checkout_instructions: "Any comments or special instructions?" checkout_payment: Payment checkout_send: Place order now @@ -1393,7 +1393,7 @@ en_CA: order_delivery_address: Delivery address order_delivery_time: Delivery time order_special_instructions: "Your notes:" - order_pickup_time: Ready for collection + order_pickup_time: 'Order for:' order_pickup_instructions: Collection Instructions order_produce: Subtotal order_total_price: Total @@ -1462,7 +1462,7 @@ en_CA: email_shipping_delivery_time: "Delivery on:" email_shipping_delivery_address: "Delivery address:" email_shipping_collection_details: Collection details - email_shipping_collection_time: "Ready for collection:" + email_shipping_collection_time: "Order for:" email_shipping_collection_instructions: "Collection instructions:" email_special_instructions: "Your notes:" email_signup_greeting: Hello! @@ -1486,6 +1486,7 @@ en_CA: shopping_oc_closed_description: "Please wait until the next cycle opens (or contact us directly to see if we can accept any late orders)" shopping_oc_last_closed: "The last cycle closed %{distance_of_time} ago" shopping_oc_next_open: "The next cycle opens in %{distance_of_time}" + shopping_oc_select: "Select" shopping_tabs_home: "Home" shopping_tabs_shop: "Shop" shopping_tabs_about: "About" @@ -1496,7 +1497,7 @@ en_CA: shopping_groups_part_of: "is part of:" shopping_producers_of_hub: "%{hub}'s producers:" enterprises_next_closing: "Next order closing" - enterprises_ready_for: "Ready for" + enterprises_ready_for: "Order for" enterprises_choose: "Choose from the dropdown:" maps_open: "Open" maps_closed: "Closed" @@ -1647,7 +1648,7 @@ en_CA: orders_fees: Fees... orders_edit_title: Shopping Cart orders_edit_headline: Your shopping cart - orders_edit_time: Order ready for + orders_edit_time: Order for orders_edit_continue: Continue shopping orders_edit_checkout: Checkout orders_form_empty_cart: "Empty cart" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index a7e45ee93e..695d8126b1 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -1498,7 +1498,7 @@ fr: shopping_contact_social: "Suivre" shopping_groups_part_of: "fait partie de:" shopping_producers_of_hub: "Les producteurs de %{hub}:" - enterprises_next_closing: "Clôture des commandes pour ce cycle" + enterprises_next_closing: "Clôture des commandes" enterprises_ready_for: "Prêt pour" enterprises_choose: "Choisissez votre option:" maps_open: "Ouvert" diff --git a/config/locales/fr_CA.yml b/config/locales/fr_CA.yml index 17c75fdde7..eed14f17d6 100644 --- a/config/locales/fr_CA.yml +++ b/config/locales/fr_CA.yml @@ -247,6 +247,7 @@ fr_CA: notes: Commentaires error: Erreur processing_payment: "Paiement en cours..." + no_pending_payments: "Aucun paiement en attente." filter_results: Filtrer les résultats quantity: Quantité pick_up: Retrait diff --git a/config/locales/it.yml b/config/locales/it.yml index d4452aba36..cb576b0cb1 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -791,13 +791,13 @@ it: producer_shop_description_text2: Un Negozio produttore è solo per i tuoi prodotti. Se vuoi vendere prodotti altrui, seleziona "Hub produttore". producer_hub: Hub produttore producer_hub_text: Vendi prodotti tuoi e di altri - producer_hub_description_text: La tua azienda è un pilastro del nostro sistema di cibo locale. Puoi vendere i tuoi prodotti, ma anche prodotti di tuoi produttori fidati attraverso la tua vetrina di Open Food Network. + producer_hub_description_text: La tua azienda è un pilastro della nostra filiera del cibo. Puoi vendere i tuoi prodotti, ma anche prodotti di tuoi produttori fidati attraverso la tua vetrina di Open Food Network. profile: Solo Profilo get_listing: Ottieni un listino profile_description_text: Le persone ti possono trovare e contattare su OFN. La tua azienda sarà visibile sulla mappa e potrà essere trovata nelle ricerche degli utenti. hub_shop: Isola logistica hub_shop_text: Vendi prodotti di altri - hub_shop_description_text: La tua azienda è la spina dorsale del vostro sistema alimentare locale. Puoi aggregare i prodotti di altre aziende e venderli attraverso il tuo negozio su Open Food Network. + hub_shop_description_text: La tua azienda è la spina dorsale della vostra filiera locale del cibo. Puoi aggregare i prodotti di altre aziende e venderli attraverso il tuo negozio su Open Food Network. choose_option: Per favore seleziona una delle opzioni. change_now: Modifica ora enterprise_user_index: @@ -1114,6 +1114,7 @@ it: already_ordered: cart: "carrello" message_html: "Hai già un ordine per questo ciclo d'ordine. Controlla%{cart} per vedere gli articoli ordinati in precedenza. Puoi anche cancellare gli articoli finché il ciclo dell'ordine è aperto." + failed: "Checkout fallito. Contattaci per poter processare il tuo ordine." shops: hubs: show_closed_shops: "Mostra i negozi chiusi" @@ -1341,7 +1342,7 @@ it: brandstory_part3: "Così costruiamo un mercato online che riequilibra le parti. E' trasparente, così crea raporti reali. E' open source, così è di tutti. Si adatta dalla scala regionale a quella nazionale, così le persone lo adottano in tutto il mondo." brandstory_part4: "Funziona ovunque. Cambia ogni cosa." brandstory_part5_strong: "Noi lo chiamiamo Open Food Network." - brandstory_part6: "Noi tutti amiamo il cibo. Adesso possiamo amare anche il nostro sistema cibo." + brandstory_part6: "Noi tutti amiamo il cibo. Adesso possiamo amare anche la nostra filiera del cibo." learn_body: "Esplora modelli, storie e risorse per aiutarti e sviluppare la tua azienda o la tua organizzazione in modo equo. Cerca corsi, eventi e altre opportunità per imparare da chi è come te." learn_cta: "Prendi ispirazione" connect_body: "Cerca la nostra intera lista di produttori, hub e gruppi per trovare chi è vicino a te. Aggiungi alla lista di OFN la tua azienda o organizzazionie così i clienti potranno trovarti. Unisciti alla comunità per avere consigli e risolvere problemi insieme." @@ -1355,7 +1356,7 @@ it: system_step3_text: "Attendi per la tua consegna, o visita il tuo produttore o distributore per una connessione più personale con il tuo cibo. Comprare cibo così variegato come lo intende natura." cta_headline: "La spesa che rende il mondo un posto migliore." cta_label: "Sono Pronto" - stats_headline: "Stiamo creando un nuovo sistema del cibo." + stats_headline: "Stiamo creando una nuova filiera del cibo." stats_producers: "produttori" stats_shops: "negozi" stats_shoppers: "clienti" @@ -1486,6 +1487,7 @@ it: shopping_oc_closed_description: "Per favore aspetta l'apertura del prossimo ciclo (o contattaci direttamente per sapere se possiamo accettare ordini fuori tempo)" shopping_oc_last_closed: "L'ultimo ciclo è chiuso %{distance_of_time} fa" shopping_oc_next_open: "Il prossimo ciclo aprirà tra %{distance_of_time}" + shopping_oc_select: "Seleziona..." shopping_tabs_home: "Home" shopping_tabs_shop: "Negozio" shopping_tabs_about: "Descrizione" @@ -1859,6 +1861,7 @@ it: headline: "Finito!" thanks: "Grazie per aver riempito i dettagli per %{enterprise}." login: "Puoi cambiare o aggiornare la tua azienda ad ogni passo accedendo a Open Food Network e andando su Amministrazione." + action: "Vai alla dashboard aziendale" back: "Indietro" continue: "Continua" action_or: "O" @@ -1944,6 +1947,7 @@ it: tax_category: "Categoria di imposta" calculator: "Calcolatore" calculator_values: "Valori del calcolatore" + calculator_settings_warning: "Se stai modificando il tipo di calcolatore, devi prima salvare prima di editare i setting del calcolatore" flat_percent_per_item: "Percentuale (per prodotto)" flat_rate_per_item: "Tariffa fissa (per articolo)" flat_rate_per_order: "Tariffa fissa (per gentile richiesta)" @@ -2274,6 +2278,7 @@ it: enterprise_register_success_notice: "Congratulazioni! L registrazione di %{enterprise} è completa!" enterprise_bulk_update_success_notice: "Aziende aggiornate con successo" enterprise_bulk_update_error: 'Aggiornamento fallito' + enterprise_shop_show_error: "Il negozio che stai cercando non esiste o risulta inattivo su OFN. Prego visita altri shop." order_cycles_create_notice: 'Il tuo ciclo di richieste è stato creato.' order_cycles_update_notice: 'Il tuo ciclo di richieste è stato aggiornato' order_cycles_bulk_update_notice: 'I cicli di richieste sono stati aggiornati.' @@ -2347,7 +2352,7 @@ it: alimentare locale attraverso Open Food Network sarà sempre gratuito.. hub_shop: Isola logistica hub_shop_text1: > - La tua azienda è la spina dorsale del vostro sistema alimentare locale. + La tua azienda è la spina dorsale della vostra filiera alimentare locale. Puoi aggregare i prodotti di altre aziende e venderli attraverso il tuo negozio su Open Food Network. hub_shop_text2: > @@ -2431,6 +2436,8 @@ it: exchange_products: load_more_variants: "Carica più Varianti" load_all_variants: "Carica tutte le Varianti" + select_all_variants: "Seleziona tutto %{total_number_of_variants} varianti" + variants_loaded: "%{num_of_variants_loaded} di %{total_number_of_variants} varianti caricate" loading_variants: "Caricamento Varianti" tag_rules: shipping_method_tagged_top: "Metodi di consegna taggati" @@ -2591,9 +2598,78 @@ it: have_an_account: "Hai già un account?" action_login: "Accedi ora." inflections: + each: + one: "ogni" + other: "ogni" bunch: one: "mazzo" other: "grappoli" + pack: + one: "imballare" + other: "confezioni" + box: + one: "scatola" + other: "scatole" + bottle: + one: "bottiglia" + other: "bottiglie" + jar: + one: "vaso" + other: "barattoli" + head: + one: "testa" + other: "teste" + bag: + one: "Borsa" + other: "borse" + loaf: + one: "pagnotta" + other: "pagnotte" + single: + one: "singolo" + other: "singoli" + tub: + one: "vasca" + other: "vasche" + punnet: + one: "cestino" + other: "cestini" + packet: + one: "pacchetto" + other: "pacchetti" + item: + one: "articolo" + other: "elementi" + dozen: + one: "dozzina" + other: "dozzine" + unit: + one: "unità" + other: "unità" + serve: + one: "servire" + other: "porzioni" + tray: + one: "vassoio" + other: "vassoi" + piece: + one: "pezzo" + other: "pezzi" + pot: + one: "pentola" + other: "vasi" + bundle: + one: "fascio" + other: "gruppi" + flask: + one: "borraccia" + other: "fiaschi" + basket: + one: "cestino" + other: "cesti" + sack: + one: "sacco" + other: "sacchi" producers: signup: start_free_profile: "Inizia con un profilo gratuito e migliora quando sei pronto!" @@ -2836,6 +2912,8 @@ it: zipcode: CAP weight: Peso (kg) error_user_destroy_with_orders: "Gli utenti con ordini completi non dovrebbero essere cancellati" + cannot_create_payment_without_payment_methods: "Non è possibile creare un pagamento per un ordine senza alcun metodo di pagamento definito." + please_define_payment_methods: "Definisci prima alcuni metodi di pagamento." options: "Opzioni" actions: update: "Aggiorna" @@ -2922,6 +3000,7 @@ it: capture: "Cattura" ship: "Spedizione" edit: "Modifica" + order_not_updated: "L'ordine non può essere aggiornato" note: "Nota" first: "Primo" last: "Ultimo" From fa98a8ea177555e2729d8586cb78e6c022fa99e0 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Sat, 21 Mar 2020 15:19:06 +1100 Subject: [PATCH 032/130] Updating translations for config/locales/en_NZ.yml --- config/locales/en_NZ.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config/locales/en_NZ.yml b/config/locales/en_NZ.yml index 1dd1a01334..16afa19f19 100644 --- a/config/locales/en_NZ.yml +++ b/config/locales/en_NZ.yml @@ -246,6 +246,8 @@ en_NZ: notes: Notes error: Error processing_payment: "Processing payment..." + no_pending_payments: "No pending payments" + invalid_payment_state: "Invalid payment state" filter_results: Filter Results quantity: Quantity pick_up: Pick up @@ -1484,6 +1486,7 @@ en_NZ: shopping_oc_closed_description: "Please wait until the next cycle opens (or contact us directly to see if we can accept any late orders)" shopping_oc_last_closed: "The last cycle closed %{distance_of_time} ago" shopping_oc_next_open: "The next cycle opens in %{distance_of_time}" + shopping_oc_select: "Select..." shopping_tabs_home: "Home" shopping_tabs_shop: "Shop" shopping_tabs_about: "About" @@ -1857,6 +1860,7 @@ en_NZ: headline: "Finished!" thanks: "Thanks for filling out the details for %{enterprise}." login: "You can change or update your enterprise at any stage by logging into Open Food Network and going to Admin." + action: "Go to Enterprise Dashboard" back: "Back" continue: "Continue" action_or: "OR" @@ -2273,6 +2277,7 @@ en_NZ: enterprise_register_success_notice: "Congratulations! Registration for %{enterprise} is complete!" enterprise_bulk_update_success_notice: "Enterprises updated successfully" enterprise_bulk_update_error: 'Update failed' + enterprise_shop_show_error: "The shop you are looking for doesn't exist or is inactive on OFN. Please check other shops." order_cycles_create_notice: 'Your order cycle has been created.' order_cycles_update_notice: 'Your order cycle has been updated.' order_cycles_bulk_update_notice: 'Order cycles have been updated.' From 45c204017f700957ce055a0a0301e6d305dddc6c Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 20 Mar 2020 13:48:03 +0100 Subject: [PATCH 033/130] Improve reports performance Add the scoping from the search filters for reports into the logic of building sets like `editable_line_items` before combining them into the query to reduce the number of `line_item` ids in the IN clause. --- app/services/permissions/order.rb | 22 ++++++++++++++----- lib/open_food_network/bulk_coop_report.rb | 2 +- .../order_and_distributor_report.rb | 2 +- .../orders_and_fulfillments_report.rb | 2 +- lib/open_food_network/packing_report.rb | 2 +- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/app/services/permissions/order.rb b/app/services/permissions/order.rb index 5e95ba92c8..cb18cc3175 100644 --- a/app/services/permissions/order.rb +++ b/app/services/permissions/order.rb @@ -2,24 +2,28 @@ require 'open_food_network/permissions' module Permissions class Order - def initialize(user) + def initialize(user, search_params = nil) @user = user @permissions = OpenFoodNetwork::Permissions.new(@user) + @search_params = search_params end # Find orders that the user can see def visible_orders - Spree::Order. + orders = Spree::Order. with_line_items_variants_and_products_outer. where(visible_orders_where_values) + orders = orders.complete.not_state(:canceled).search(search_params).result if search_orders? + orders end # Any orders that the user can edit def editable_orders - Spree::Order.where( - managed_orders_where_values. - or(coordinated_orders_where_values) - ) + orders = Spree::Order. + where(managed_orders_where_values. + or(coordinated_orders_where_values)) + orders = orders.complete.not_state(:canceled).search(search_params).result if search_orders? + orders end def visible_line_items @@ -35,6 +39,12 @@ module Permissions private + attr_reader :search_params + + def search_orders? + search_params.present? + end + def visible_orders_where_values # Grouping keeps the 2 where clauses from produced_orders_where_values inside parentheses # This way it makes the OR work between the 3 types of orders: diff --git a/lib/open_food_network/bulk_coop_report.rb b/lib/open_food_network/bulk_coop_report.rb index e4a89dbbe5..019dcddbb7 100644 --- a/lib/open_food_network/bulk_coop_report.rb +++ b/lib/open_food_network/bulk_coop_report.rb @@ -128,7 +128,7 @@ module OpenFoodNetwork def order_permissions return @order_permissions unless @order_permissions.nil? - @order_permissions = ::Permissions::Order.new(@user) + @order_permissions = ::Permissions::Order.new(@user, @params[:q]) end def report_line_items diff --git a/lib/open_food_network/order_and_distributor_report.rb b/lib/open_food_network/order_and_distributor_report.rb index 64f46f41d1..b705c0cb16 100644 --- a/lib/open_food_network/order_and_distributor_report.rb +++ b/lib/open_food_network/order_and_distributor_report.rb @@ -5,7 +5,7 @@ module OpenFoodNetwork @user = user @render_table = render_table - @permissions = ::Permissions::Order.new(user) + @permissions = ::Permissions::Order.new(user, @params[:q]) end def header diff --git a/lib/open_food_network/orders_and_fulfillments_report.rb b/lib/open_food_network/orders_and_fulfillments_report.rb index 01b2ae7617..7c3780bb55 100644 --- a/lib/open_food_network/orders_and_fulfillments_report.rb +++ b/lib/open_food_network/orders_and_fulfillments_report.rb @@ -83,7 +83,7 @@ module OpenFoodNetwork def order_permissions return @order_permissions unless @order_permissions.nil? - @order_permissions = ::Permissions::Order.new(@user) + @order_permissions = ::Permissions::Order.new(@user, options[:q]) end def report_line_items diff --git a/lib/open_food_network/packing_report.rb b/lib/open_food_network/packing_report.rb index b9f9bc7f52..cecec8cff8 100644 --- a/lib/open_food_network/packing_report.rb +++ b/lib/open_food_network/packing_report.rb @@ -128,7 +128,7 @@ module OpenFoodNetwork def order_permissions return @order_permissions unless @order_permissions.nil? - @order_permissions = ::Permissions::Order.new(@user) + @order_permissions = ::Permissions::Order.new(@user, @params[:q]) end def is_temperature_controlled?(line_item) From 0b878dd0a2c5d69e62c86d7afe5aad736dd05654 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sat, 21 Mar 2020 08:44:31 +0100 Subject: [PATCH 034/130] Use named scope for finding line_items in a set of orders --- app/models/spree/line_item_decorator.rb | 4 ++++ lib/open_food_network/reports/line_items.rb | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/models/spree/line_item_decorator.rb b/app/models/spree/line_item_decorator.rb index 5581993dff..6f085f30f5 100644 --- a/app/models/spree/line_item_decorator.rb +++ b/app/models/spree/line_item_decorator.rb @@ -36,6 +36,10 @@ Spree::LineItem.class_eval do end } + scope :in_orders, lambda { |orders| + where(order_id: orders) + } + # Find line items that are from order sorted by variant name and unit value scope :sorted_by_name_and_unit_value, -> { joins(variant: :product). diff --git a/lib/open_food_network/reports/line_items.rb b/lib/open_food_network/reports/line_items.rb index 4e272a7c13..ce3bc9e97e 100644 --- a/lib/open_food_network/reports/line_items.rb +++ b/lib/open_food_network/reports/line_items.rb @@ -12,9 +12,7 @@ module OpenFoodNetwork end def list(line_item_includes = nil) - line_items = @order_permissions. - visible_line_items. - merge(Spree::LineItem.where(order_id: orders.result)) + line_items = @order_permissions.visible_line_items.in_orders(orders.result) if @params[:supplier_id_in].present? line_items = line_items.supplied_by_any(@params[:supplier_id_in]) From a29f2630411d4a38095bf1d435d0677a16d53525 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Sat, 21 Mar 2020 22:33:49 +0000 Subject: [PATCH 035/130] Make explicit the namespace of the BaseController for api controllers --- app/controllers/api/customers_controller.rb | 2 +- app/controllers/api/enterprise_attachment_controller.rb | 2 +- app/controllers/api/enterprise_fees_controller.rb | 2 +- app/controllers/api/logos_controller.rb | 2 +- app/controllers/api/order_cycles_controller.rb | 2 +- app/controllers/api/orders_controller.rb | 2 +- app/controllers/api/product_images_controller.rb | 2 +- app/controllers/api/promo_images_controller.rb | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/controllers/api/customers_controller.rb b/app/controllers/api/customers_controller.rb index 2d72513982..99c5834578 100644 --- a/app/controllers/api/customers_controller.rb +++ b/app/controllers/api/customers_controller.rb @@ -1,5 +1,5 @@ module Api - class CustomersController < BaseController + class CustomersController < Api::BaseController skip_authorization_check only: :index def index diff --git a/app/controllers/api/enterprise_attachment_controller.rb b/app/controllers/api/enterprise_attachment_controller.rb index 406b1988fe..6178b40104 100644 --- a/app/controllers/api/enterprise_attachment_controller.rb +++ b/app/controllers/api/enterprise_attachment_controller.rb @@ -1,5 +1,5 @@ module Api - class EnterpriseAttachmentController < BaseController + class EnterpriseAttachmentController < Api::BaseController class MissingImplementationError < StandardError; end class UnknownEnterpriseAuthorizationActionError < StandardError; end diff --git a/app/controllers/api/enterprise_fees_controller.rb b/app/controllers/api/enterprise_fees_controller.rb index c6216a1270..208643d7dc 100644 --- a/app/controllers/api/enterprise_fees_controller.rb +++ b/app/controllers/api/enterprise_fees_controller.rb @@ -1,5 +1,5 @@ module Api - class EnterpriseFeesController < BaseController + class EnterpriseFeesController < Api::BaseController respond_to :json def destroy diff --git a/app/controllers/api/logos_controller.rb b/app/controllers/api/logos_controller.rb index f3e7934724..0c4b4c2f45 100644 --- a/app/controllers/api/logos_controller.rb +++ b/app/controllers/api/logos_controller.rb @@ -1,5 +1,5 @@ module Api - class LogosController < EnterpriseAttachmentController + class LogosController < Api::EnterpriseAttachmentController private def attachment_name diff --git a/app/controllers/api/order_cycles_controller.rb b/app/controllers/api/order_cycles_controller.rb index e0088d43f4..0ac5223ea1 100644 --- a/app/controllers/api/order_cycles_controller.rb +++ b/app/controllers/api/order_cycles_controller.rb @@ -1,5 +1,5 @@ module Api - class OrderCyclesController < BaseController + class OrderCyclesController < Api::BaseController include EnterprisesHelper respond_to :json diff --git a/app/controllers/api/orders_controller.rb b/app/controllers/api/orders_controller.rb index 3be0910f10..c011db44ec 100644 --- a/app/controllers/api/orders_controller.rb +++ b/app/controllers/api/orders_controller.rb @@ -1,5 +1,5 @@ module Api - class OrdersController < BaseController + class OrdersController < Api::BaseController def show authorize! :read, order render json: order, serializer: Api::OrderDetailedSerializer, current_order: order diff --git a/app/controllers/api/product_images_controller.rb b/app/controllers/api/product_images_controller.rb index a8706f6458..e7551aeea7 100644 --- a/app/controllers/api/product_images_controller.rb +++ b/app/controllers/api/product_images_controller.rb @@ -1,5 +1,5 @@ module Api - class ProductImagesController < BaseController + class ProductImagesController < Api::BaseController respond_to :json def update_product_image diff --git a/app/controllers/api/promo_images_controller.rb b/app/controllers/api/promo_images_controller.rb index 0e79ebdb93..9cdb799931 100644 --- a/app/controllers/api/promo_images_controller.rb +++ b/app/controllers/api/promo_images_controller.rb @@ -1,5 +1,5 @@ module Api - class PromoImagesController < EnterpriseAttachmentController + class PromoImagesController < Api::EnterpriseAttachmentController private def attachment_name From 0f2c5d379a329279a8aff6135b853fa4621be77b Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Sun, 22 Mar 2020 10:30:18 +0000 Subject: [PATCH 036/130] Add test locales to I18n.available_locales before the test and reset them afterwards --- engines/web/spec/helpers/cookies_policy_helper_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/engines/web/spec/helpers/cookies_policy_helper_spec.rb b/engines/web/spec/helpers/cookies_policy_helper_spec.rb index b4d7f7fdf3..9c333b363f 100644 --- a/engines/web/spec/helpers/cookies_policy_helper_spec.rb +++ b/engines/web/spec/helpers/cookies_policy_helper_spec.rb @@ -33,6 +33,14 @@ module Web end describe "language from locale" do + # keeps global state unchanged + around do |example| + original_available_locales = I18n.available_locales + I18n.available_locales = ['en', 'en_GB', ''] + example.run + I18n.available_locales = original_available_locales + end + scenario "when locale is the language" do I18n.locale = "en" expect(helper.locale_language).to eq "en" From dd0e135a4d14ee812ff5e2ddd8ca6c2c82e9e9b4 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sun, 22 Mar 2020 11:45:40 +0100 Subject: [PATCH 037/130] Add test coverage for Permissions::Order with search_params --- spec/services/permissions/order_spec.rb | 49 ++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/spec/services/permissions/order_spec.rb b/spec/services/permissions/order_spec.rb index 1ccb6648ec..d60d6968e2 100644 --- a/spec/services/permissions/order_spec.rb +++ b/spec/services/permissions/order_spec.rb @@ -5,14 +5,21 @@ module Permissions let(:user) { double(:user) } let(:permissions) { Permissions::Order.new(user) } let!(:basic_permissions) { OpenFoodNetwork::Permissions.new(user) } + let(:distributor) { create(:distributor_enterprise) } + let(:coordinator) { create(:distributor_enterprise) } + let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator, distributors: [distributor]) } + let(:order_completed) { create(:completed_order_with_totals, order_cycle: order_cycle, distributor: distributor ) } + let(:order_cancelled) { create(:order, order_cycle: order_cycle, distributor: distributor, state: 'canceled' ) } + let(:order_cart) { create(:order, order_cycle: order_cycle, distributor: distributor, state: 'cart' ) } + let(:order_from_last_year) { + create(:completed_order_with_totals, order_cycle: order_cycle, distributor: distributor, + completed_at: Time.zone.now - 1.year) + } before { allow(OpenFoodNetwork::Permissions).to receive(:new) { basic_permissions } } describe "finding orders that are visible in reports" do - let(:distributor) { create(:distributor_enterprise) } - let(:coordinator) { create(:distributor_enterprise) } let(:random_enterprise) { create(:distributor_enterprise) } - let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator, distributors: [distributor]) } let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor ) } let!(:line_item) { create(:line_item, order: order) } let!(:producer) { create(:supplier_enterprise) } @@ -64,6 +71,18 @@ module Permissions expect(permissions.visible_orders).to_not include order end end + + context "with search params" do + let(:search_params) { { completed_at_gt: Time.zone.now.yesterday.strftime('%Y-%m-%d') } } + let(:permissions) { Permissions::Order.new(user, search_params) } + + it "only returns completed, non-cancelled orders within search filter range" do + expect(permissions.visible_orders).to include order_completed + expect(permissions.visible_orders).to_not include order_cancelled + expect(permissions.visible_orders).to_not include order_cart + expect(permissions.visible_orders).to_not include order_from_last_year + end + end end context "as an enterprise that is a distributor in the order cycle, but not the distributor of the order" do @@ -78,10 +97,7 @@ module Permissions end describe "finding line items that are visible in reports" do - let(:distributor) { create(:distributor_enterprise) } - let(:coordinator) { create(:distributor_enterprise) } let(:random_enterprise) { create(:distributor_enterprise) } - let(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator, distributors: [distributor]) } let(:order) { create(:order, order_cycle: order_cycle, distributor: distributor ) } let!(:line_item1) { create(:line_item, order: order) } let!(:line_item2) { create(:line_item, order: order) } @@ -137,6 +153,27 @@ module Permissions expect(permissions.visible_line_items).to_not include line_item1, line_item2 end end + + context "with search params" do + let!(:line_item3) { create(:line_item, order: order_completed) } + let!(:line_item4) { create(:line_item, order: order_cancelled) } + let!(:line_item5) { create(:line_item, order: order_cart) } + let!(:line_item6) { create(:line_item, order: order_from_last_year) } + + let(:search_params) { { completed_at_gt: Time.zone.now.yesterday.strftime('%Y-%m-%d') } } + let(:permissions) { Permissions::Order.new(user, search_params) } + + before do + allow(user).to receive(:has_spree_role?) { "admin" } + end + + it "only returns line items from completed, non-cancelled orders within search filter range" do + expect(permissions.visible_line_items).to include order_completed.line_items.first + expect(permissions.visible_line_items).to_not include order_cancelled.line_items.first + expect(permissions.visible_line_items).to_not include order_cart.line_items.first + expect(permissions.visible_line_items).to_not include order_from_last_year.line_items.first + end + end end end end From b81843921bf61d168b846e5bb9de4f53aa476e6e Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Mon, 23 Mar 2020 08:57:54 +1100 Subject: [PATCH 038/130] Updating translations for config/locales/en_CA.yml --- config/locales/en_CA.yml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/config/locales/en_CA.yml b/config/locales/en_CA.yml index aea11218e9..cb02b83181 100644 --- a/config/locales/en_CA.yml +++ b/config/locales/en_CA.yml @@ -871,10 +871,10 @@ en_CA: incoming: "2. Incoming Products" outgoing: "3. Outgoing Products" exchange_form: - pickup_time_tip: Date orders will be ready for pick-up/delivery. Or enter the customer type (e.g. 'wholesale', 'members only'...) if you are setting up multiple shopfronts. + pickup_time_tip: 'This phrase will help buyers select their shop from multiple shops. It will be preceded by the phrase "Orders for". So enter a pick-up date, or buyer group... ' pickup_instructions_placeholder: "Pick-up instructions" pickup_instructions_tip: These instructions are shown to customers after they complete an order - pickup_time_placeholder: "Ready for (ie. Date/Time)" + pickup_time_placeholder: "Order for (ie. Date, or buyer group...)" receival_instructions_placeholder: "Receival instructions" add_fee: 'Add fee' remove: 'Remove' @@ -921,7 +921,7 @@ en_CA: distributors: distributors variants: variants simple_form: - ready_for: Ready for + ready_for: Order for ready_for_placeholder: Date / time customer_instructions: Customer instructions customer_instructions_placeholder: 'Notes:' @@ -1362,7 +1362,7 @@ en_CA: stats_orders: "orders" checkout_title: Checkout checkout_now: Checkout now - checkout_order_ready: Order ready for + checkout_order_ready: Order for checkout_hide: Hide checkout_expand: Expand checkout_headline: "Ok, ready to checkout?" @@ -1374,7 +1374,7 @@ en_CA: checkout_default_ship_address: "Save as default shipping address" checkout_method_free: Free checkout_address_same: Shipping address same as billing address? - checkout_ready_for: "Ready for:" + checkout_ready_for: "Order for: " checkout_instructions: "Any comments or special instructions?" checkout_payment: Payment checkout_send: Place order now @@ -1393,7 +1393,7 @@ en_CA: order_delivery_address: Delivery address order_delivery_time: Delivery time order_special_instructions: "Your notes:" - order_pickup_time: Ready for collection + order_pickup_time: 'Order for:' order_pickup_instructions: Collection Instructions order_produce: Subtotal order_total_price: Total @@ -1462,7 +1462,7 @@ en_CA: email_shipping_delivery_time: "Delivery on:" email_shipping_delivery_address: "Delivery address:" email_shipping_collection_details: Collection details - email_shipping_collection_time: "Ready for collection:" + email_shipping_collection_time: "Order for:" email_shipping_collection_instructions: "Collection instructions:" email_special_instructions: "Your notes:" email_signup_greeting: Hello! @@ -1497,7 +1497,7 @@ en_CA: shopping_groups_part_of: "is part of:" shopping_producers_of_hub: "%{hub}'s producers:" enterprises_next_closing: "Next order closing" - enterprises_ready_for: "Ready for" + enterprises_ready_for: "Order for" enterprises_choose: "Choose from the dropdown:" maps_open: "Open" maps_closed: "Closed" @@ -1648,7 +1648,7 @@ en_CA: orders_fees: Fees... orders_edit_title: Shopping Cart orders_edit_headline: Your shopping cart - orders_edit_time: Order ready for + orders_edit_time: Order for orders_edit_continue: Continue shopping orders_edit_checkout: Checkout orders_form_empty_cart: "Empty cart" @@ -3018,7 +3018,8 @@ en_CA: tax_invoice: "Order Number" code: "Code" from: "From" - to: "To" + to: "Bill to" + shipping: "Shipping" form: distribution_fields: title: "Distribution" From 310906c7da8731a520b326c4dcf373c174273d35 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Mon, 23 Mar 2020 15:49:19 +1100 Subject: [PATCH 039/130] Updating translations for config/locales/en_NZ.yml --- config/locales/en_NZ.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/locales/en_NZ.yml b/config/locales/en_NZ.yml index 16afa19f19..8299a63b7d 100644 --- a/config/locales/en_NZ.yml +++ b/config/locales/en_NZ.yml @@ -3019,7 +3019,8 @@ en_NZ: tax_invoice: "TAX INVOICE" code: "Code" from: "From" - to: "To" + to: "Bill to" + shipping: "Shipping" form: distribution_fields: title: "Distribution" From 63d748b2a426c5efbf15141a2cda1f4a700d03ce Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Tue, 24 Mar 2020 03:32:23 +1100 Subject: [PATCH 040/130] Updating translations for config/locales/pt_BR.yml --- config/locales/pt_BR.yml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/config/locales/pt_BR.yml b/config/locales/pt_BR.yml index a77ee7cecd..28415c23bf 100644 --- a/config/locales/pt_BR.yml +++ b/config/locales/pt_BR.yml @@ -583,7 +583,7 @@ pt_BR: actions_delete: "Deletar selecionado" loading: "Carregando pedidos" no_results: "Nenhum pedido encontrado. " - group_buy_unit_size: "Tamanho de unidade para compras de Grupo" + group_buy_unit_size: "Tamanho da Unidade para Grupo de Compras" total_qtt_ordered: "Quantidade total do pedido" max_qtt_ordered: "Quantidade máxima do pedido" current_fulfilled_units: "Unidades completadas no momento" @@ -598,7 +598,7 @@ pt_BR: title: Iniciativas new_enterprise: Nova iniciativa producer?: "Produtor?" - package: Embalagem + package: Tipo de Perfil status: Status manage: Administrar form: @@ -806,7 +806,7 @@ pt_BR: manage: Administrar manage_link: Configurações producer?: "Produtor?" - package: "Embalagem" + package: "Tipo de Perfil" status: "Status" new_form: owner: Proprietário @@ -1901,7 +1901,7 @@ pt_BR: successfully_updated: '%{resource} foi atualizado com sucesso!' running_balance: "Balanço corrente" outstanding_balance: "Saldo devedor" - admin_enterprise_relationships: "Permissões corporativas" + admin_enterprise_relationships: "Permissões da Iniciativa" admin_enterprise_relationships_everything: "Tudo" admin_enterprise_relationships_permits: "permite" admin_enterprise_relationships_seach_placeholder: "Buscar" @@ -2133,7 +2133,7 @@ pt_BR: report_header_producer: Produtor report_header_producer_suburb: Subúrbio do produtor report_header_unit: Unidade - report_header_group_buy_unit_quantity: Quantidade de unidade de compra em grupo + report_header_group_buy_unit_quantity: Quantidade da Unidade para Grupo de Compras report_header_cost: Custo report_header_shipping_cost: ' Custo de envio' report_header_curr_cost_per_unit: Custo unitário atual @@ -2229,7 +2229,7 @@ pt_BR: adrdress: "Endereço" contact: "Contato" social: "Social" - business_details: "Detalhes do negócio" + business_details: "Detalhes da iniciativa" properties: "Propriedades" shipping: "Envio" shipping_methods: "Métodos de envio" @@ -2271,7 +2271,7 @@ pt_BR: mas não possuem métodos de envio e pagamento válidos. Até que você configure eles, os consumidores não poderão fazer compras nessas centrais. enterprise_fees_update_notice: As tarifas da sua iniciativa foram atualizadas. - enterprise_register_package_error: "Por favor selecione um pacote" + enterprise_register_package_error: "Por favor selecione um tipo de perfil" enterprise_register_error: "Não foi possível completar o registro para %{enterprise}" enterprise_register_success_notice: "Parabéns! O registro para %{enterprise} está completo!" enterprise_bulk_update_success_notice: "Iniciativas atualizadas com sucesso" @@ -2360,7 +2360,7 @@ pt_BR: hub_shop_text3: > Se você também quer vender seus próprios produtos, você precisará mudar essa iniciativa para ser um produtor. - choose_package: Por favor escolha um pacote + choose_package: Por favor escolha um tipo de perfil choose_package_text1: > Sua iniciativa não será totalmente ativada até que um tipo de perfil seja selecionado a partir das opções à esquerda. @@ -3023,7 +3023,8 @@ pt_BR: tax_invoice: "NOTA FISCAL" code: "Código" from: "De" - to: "Para" + to: "Informações de cobrança" + shipping: "Entrega" form: distribution_fields: title: "Distribuição" @@ -3140,7 +3141,7 @@ pt_BR: primary_taxon_form: product_category: Categoria de Produto group_buy_form: - group_buy: "Compra de grupo?" + group_buy: "Grupo de Compras?" bulk_unit_size: Tamanho da unidade em massa display_as: display_as: Mostrar como From 7110f9e6ee69949f38e5c351e31e77d01c79137c Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Mon, 23 Mar 2020 14:30:53 +0100 Subject: [PATCH 041/130] Eager-load data used in EnterpriseGroupsController Cuts page load time by ~75% --- app/helpers/injection_helper.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index 6ebf77a8fb..e44de4af0e 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -17,7 +17,10 @@ module InjectionHelper inject_json_ams( "groups", - EnterpriseGroup.on_front_page.by_position.select(select_only).includes(address: :state).all, + EnterpriseGroup.on_front_page.by_position.select(select_only). + includes(enterprises: [:shipping_methods, { address: [:state, :country] }], + address: :state). + all, Api::GroupListSerializer ) end From 5b481c19cc47b258664e54ccc83fb29dee375e45 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Mon, 23 Mar 2020 14:53:13 +0100 Subject: [PATCH 042/130] Eager-load country in Producers controller --- app/controllers/producers_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/producers_controller.rb b/app/controllers/producers_controller.rb index 0f45546fde..a5f4752c49 100644 --- a/app/controllers/producers_controller.rb +++ b/app/controllers/producers_controller.rb @@ -8,7 +8,7 @@ class ProducersController < BaseController .activated .visible .is_primary_producer - .includes(address: :state) + .includes(address: [:state, :country]) .includes(:properties) .includes(supplied_products: :properties) .all From de180d32bf131ed7d5a4548474d5b820ddafd902 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2020 20:37:57 +0000 Subject: [PATCH 043/130] Bump rubocop-rails from 2.4.2 to 2.5.0 Bumps [rubocop-rails](https://github.com/rubocop-hq/rubocop-rails) from 2.4.2 to 2.5.0. - [Release notes](https://github.com/rubocop-hq/rubocop-rails/releases) - [Changelog](https://github.com/rubocop-hq/rubocop-rails/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop-hq/rubocop-rails/compare/v2.4.2...v2.5.0) Signed-off-by: dependabot-preview[bot] --- Gemfile.lock | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 88ead73305..84c427fb72 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -499,7 +499,7 @@ GEM parallel (1.19.1) paranoia (1.3.4) activerecord (~> 3.1) - parser (2.7.0.4) + parser (2.7.0.5) ast (~> 2.4.0) paypal-sdk-core (0.2.10) multi_json (~> 1.0) @@ -612,7 +612,8 @@ GEM rexml ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 1.7) - rubocop-rails (2.4.2) + rubocop-rails (2.5.0) + activesupport rack (>= 1.1) rubocop (>= 0.72.0) ruby-progressbar (1.10.1) From c82c54873c34aafc348bc63aeff9331043b7fe3e Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Mon, 23 Mar 2020 16:23:14 +0100 Subject: [PATCH 044/130] Pluck :id when querying active distributors for serializer data --- app/serializers/api/cached_enterprise_serializer.rb | 2 +- app/serializers/api/uncached_enterprise_serializer.rb | 2 +- lib/open_food_network/enterprise_injection_data.rb | 5 +++-- spec/controllers/shops_controller_spec.rb | 2 +- spec/serializers/api/cached_enterprise_serializer_spec.rb | 4 ++-- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/serializers/api/cached_enterprise_serializer.rb b/app/serializers/api/cached_enterprise_serializer.rb index 26a6f351d8..05677720db 100644 --- a/app/serializers/api/cached_enterprise_serializer.rb +++ b/app/serializers/api/cached_enterprise_serializer.rb @@ -107,7 +107,7 @@ module Api end def active - data.active_distributors.andand.include? enterprise + data.active_distributor_ids.andand.include? enterprise.id end # Map svg icons. diff --git a/app/serializers/api/uncached_enterprise_serializer.rb b/app/serializers/api/uncached_enterprise_serializer.rb index bad8808533..1bbae872ba 100644 --- a/app/serializers/api/uncached_enterprise_serializer.rb +++ b/app/serializers/api/uncached_enterprise_serializer.rb @@ -9,7 +9,7 @@ module Api end def active - options[:data].active_distributors.andand.include? object + options[:data].active_distributor_ids.andand.include? object.id end end end diff --git a/lib/open_food_network/enterprise_injection_data.rb b/lib/open_food_network/enterprise_injection_data.rb index 3f23ff7a0a..8bc386b2a6 100644 --- a/lib/open_food_network/enterprise_injection_data.rb +++ b/lib/open_food_network/enterprise_injection_data.rb @@ -1,7 +1,8 @@ module OpenFoodNetwork class EnterpriseInjectionData - def active_distributors - @active_distributors ||= Enterprise.distributors_with_active_order_cycles.ready_for_checkout + def active_distributor_ids + @active_distributor_ids ||= + Enterprise.distributors_with_active_order_cycles.ready_for_checkout.pluck(:id) end def earliest_closing_times diff --git a/spec/controllers/shops_controller_spec.rb b/spec/controllers/shops_controller_spec.rb index 2cecfc2c96..023e7b0245 100644 --- a/spec/controllers/shops_controller_spec.rb +++ b/spec/controllers/shops_controller_spec.rb @@ -7,7 +7,7 @@ describe ShopsController, type: :controller do let!(:distributor) { create(:distributor_enterprise) } before do - allow(Enterprise).to receive_message_chain(:distributors_with_active_order_cycles, :ready_for_checkout) { [distributor] } + allow(OpenFoodNetwork::EnterpriseInjectionData).to receive(:active_distributor_ids) { [distributor.id] } end it 'renders distributed product properties' do diff --git a/spec/serializers/api/cached_enterprise_serializer_spec.rb b/spec/serializers/api/cached_enterprise_serializer_spec.rb index e46b0006b9..c4e5624fa8 100644 --- a/spec/serializers/api/cached_enterprise_serializer_spec.rb +++ b/spec/serializers/api/cached_enterprise_serializer_spec.rb @@ -50,7 +50,7 @@ describe Api::CachedEnterpriseSerializer do context 'when the enterprise is not an active distributor' do let(:enterprise_injection_data) do - instance_double(OpenFoodNetwork::EnterpriseInjectionData, active_distributors: []) + instance_double(OpenFoodNetwork::EnterpriseInjectionData, active_distributor_ids: []) end it 'does not duplicate properties' do @@ -69,7 +69,7 @@ describe Api::CachedEnterpriseSerializer do context 'when the enterprise is an active distributor' do let(:enterprise_injection_data) do - instance_double(OpenFoodNetwork::EnterpriseInjectionData, active_distributors: [shop]) + instance_double(OpenFoodNetwork::EnterpriseInjectionData, active_distributor_ids: [shop.id]) end it 'does not duplicate properties' do From 434f98fb4693c796fb808fa292fe4e20ffaaff05 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Mon, 23 Mar 2020 17:16:33 +0100 Subject: [PATCH 045/130] Fix insane N+1 in Package The #ships_with? method was being called ~800 times when loading the admin order edit page (with Aus production data), and triggering a new query each time it was called. --- app/models/stock/package.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/models/stock/package.rb b/app/models/stock/package.rb index 75f6b30261..ffa76ffa72 100644 --- a/app/models/stock/package.rb +++ b/app/models/stock/package.rb @@ -24,8 +24,10 @@ module Stock # # @return [Array] def shipping_methods - super.delete_if do |shipping_method| - !ships_with?(order.distributor, shipping_method) + available_shipping_methods = super.to_a + + available_shipping_methods.delete_if do |shipping_method| + !ships_with?(order.distributor.shipping_methods.to_a, shipping_method) end end @@ -33,11 +35,11 @@ module Stock # Checks whether the given distributor provides the specified shipping method # - # @param distributor [Spree::Enterprise] + # @param shipping_methods [Array] # @param shipping_method [Spree::ShippingMethod] # @return [Boolean] - def ships_with?(distributor, shipping_method) - distributor.shipping_methods.include?(shipping_method) + def ships_with?(shipping_methods, shipping_method) + shipping_methods.include?(shipping_method) end end end From 8b93c5ab5624d4adfd392fd6bc980d57ee3531d5 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Mon, 23 Mar 2020 17:17:20 +0100 Subject: [PATCH 046/130] Invert conditionals for better readability --- app/models/stock/package.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/stock/package.rb b/app/models/stock/package.rb index ffa76ffa72..eada3741c1 100644 --- a/app/models/stock/package.rb +++ b/app/models/stock/package.rb @@ -26,8 +26,8 @@ module Stock def shipping_methods available_shipping_methods = super.to_a - available_shipping_methods.delete_if do |shipping_method| - !ships_with?(order.distributor.shipping_methods.to_a, shipping_method) + available_shipping_methods.keep_if do |shipping_method| + ships_with?(order.distributor.shipping_methods.to_a, shipping_method) end end From ac38b2735cba35f004f921a3dc3b1df95855889e Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Mon, 23 Mar 2020 17:22:20 +0100 Subject: [PATCH 047/130] Eager-load data in OrdersController --- app/controllers/spree/admin/orders_controller.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/controllers/spree/admin/orders_controller.rb b/app/controllers/spree/admin/orders_controller.rb index 2ddc9dccc7..5f8154a355 100644 --- a/app/controllers/spree/admin/orders_controller.rb +++ b/app/controllers/spree/admin/orders_controller.rb @@ -109,7 +109,10 @@ module Spree private def load_order - @order = Order.find_by_number!(params[:id], include: :adjustments) if params[:id] + if params[:id] + @order = Order.includes(:adjustments, :shipments, line_items: :adjustments). + find_by_number!(params[:id]) + end authorize! action, @order end @@ -128,7 +131,7 @@ module Spree def load_distribution_choices @shops = Enterprise.is_distributor.managed_by(spree_current_user).by_name - ocs = OrderCycle.managed_by(spree_current_user) + ocs = OrderCycle.includes(:suppliers, :distributors).managed_by(spree_current_user) @order_cycles = ocs.soonest_closing + ocs.soonest_opening + ocs.closed + From 36aa52736a2f055a995a42b60d1b3ad1636a0a35 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 24 Mar 2020 09:36:59 +0100 Subject: [PATCH 048/130] Refactor order filtering logic --- app/services/permissions/order.rb | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/app/services/permissions/order.rb b/app/services/permissions/order.rb index cb18cc3175..ea5c4c2bfa 100644 --- a/app/services/permissions/order.rb +++ b/app/services/permissions/order.rb @@ -13,8 +13,8 @@ module Permissions orders = Spree::Order. with_line_items_variants_and_products_outer. where(visible_orders_where_values) - orders = orders.complete.not_state(:canceled).search(search_params).result if search_orders? - orders + + filtered_orders(orders) end # Any orders that the user can edit @@ -22,8 +22,8 @@ module Permissions orders = Spree::Order. where(managed_orders_where_values. or(coordinated_orders_where_values)) - orders = orders.complete.not_state(:canceled).search(search_params).result if search_orders? - orders + + filtered_orders(orders) end def visible_line_items @@ -41,7 +41,13 @@ module Permissions attr_reader :search_params - def search_orders? + def filtered_orders(orders) + return orders unless filter_orders? + + orders.complete.not_state(:canceled).search(search_params).result + end + + def filter_orders? search_params.present? end From 54c3c73ed21d5a5e6b6dede72f47049d7b9e0dc2 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 24 Mar 2020 12:46:21 +0100 Subject: [PATCH 049/130] Fix duplicate key in hash --- spec/models/calculator/weight_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/calculator/weight_spec.rb b/spec/models/calculator/weight_spec.rb index 94f7d1b498..010abd1d0b 100644 --- a/spec/models/calculator/weight_spec.rb +++ b/spec/models/calculator/weight_spec.rb @@ -141,7 +141,7 @@ describe Calculator::Weight do end context "when the product uses item unit" do - let!(:product_attributes) { { variant_unit: "items", variant_unit_scale: nil, variant_unit: "pc", display_as: "pc" } } + let!(:product_attributes) { { variant_unit: "items", variant_unit_scale: nil, variant_unit_name: "pc", display_as: "pc" } } let!(:variant_attributes) { { unit_value: 3.0, weight: 2.5, display_as: "pc" } } it "is correct" do From f5567e556bf229a2174e1ccfe16938241be37a86 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Wed, 25 Mar 2020 02:22:31 +1100 Subject: [PATCH 050/130] Updating translations for config/locales/pt_BR.yml --- config/locales/pt_BR.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/config/locales/pt_BR.yml b/config/locales/pt_BR.yml index 28415c23bf..7bb6d77399 100644 --- a/config/locales/pt_BR.yml +++ b/config/locales/pt_BR.yml @@ -175,7 +175,7 @@ pt_BR: home: "OFB" title: Open Food Brasil welcome_to: 'Bem-vindo a' - site_meta_description: "A gente começa pela base. Com agricultores e produtores preparados para contar suas histórias com orgulho e verdade. Com distribuidores prontos para conectar pessoas com produtos de forma justa e honesta. Com os compradores que acreditam que melhores decisões de compras semanais podem..." + site_meta_description: "A nossa proposta é conectar agricultoras/es, produtoras/es e consumidoras/es que estão próximos entre si. Buscamos, assim, potencializar a circulação e a comercialização de produtos agroecológicos, orgânicos e artesanais em circuitos curtos, bem como criar novas formas de relação, orientadas pela troca, diálogo e confiança." search_by_name: Procurar por nome ou localidade producers_join: Produtores nacionais estão convidados a se unirem à Open Food Brasil. charges_sales_tax: Cobra imposto sobre bens e serviços? @@ -1333,14 +1333,14 @@ pt_BR: cookies_policy_link: "política de cookies" cookies_accept_button: "Aceitar Cookies" home_shop: Compre Agora - brandstory_headline: "Alimentos, com liberdade" - brandstory_intro: "Às vezes, a melhor maneira de consertar o sistema é construir um novo..." - brandstory_part1: "Começamos do início. Com agricultores e produtores preparados para contar com orgulho suas verdadeiras histórias. Com distribuidores prontos para conectar pessoas com produtos de forma justa e honesta. Com consumidores que acreditam que melhores decisões de compras podem mudar seriamente o mundo." - brandstory_part2: "Então precisamos de uma forma para tornar isto real. Uma maneira para empoderar todos os que produzem, vendem e compram alimentos. Uma maneira de contar história e lidar com a logística. Uma maneira de transformar suas compras em mudanças positivas para a sociedade." - brandstory_part3: "Assim construímos um mercado de alimentos mais justo e transparente, por isso criamos relacionamentos reais. Nosso código é aberto, por isso é de todos. Nos multiplicamos por diversas regiões e países, com pessoas criando seus próprios mercados pelo mundo." - brandstory_part4: "É acessível a todos, em qualquer lugar do mundo." - brandstory_part5_strong: "Somos a Open Food Brasil." - brandstory_part6: "Todos amamos comida. Agora a gente também pode amar nosso maneira de consumir. " + brandstory_headline: "Comida de verdade, com autonomia " + brandstory_intro: "Comer é um ato político! Por trás de todo alimento há pessoas, processos e histórias. " + brandstory_part1: "A nossa proposta é conectar agricultoras/es, produtoras/es e consumidoras/es que estão próximos entre si. Buscamos, assim, potencializar a circulação e a comercialização de produtos agroecológicos, orgânicos e artesanais em circuitos curtos, bem como criar novas formas de relação, orientadas pela troca, diálogo e confiança." + brandstory_part2: "Para tornar essa ideia real, trouxemos a plataforma Open Food Network para o Brasil. Nosso código (a receita do site) é aberto e seu desenvolvimento é orientado globalmente pelas/os interessadas/os e baseado em uma cultura de autonomia e transparência." + brandstory_part3: "Uma maneira de incentivar a colaboração entre quem produz, vende e compra. Uma maneira de contar histórias e lidar com a logística. Uma maneira de transformar seu consumo de alimentos em mudanças positivas para a sociedade." + brandstory_part4: "Colabore com essa (des)construção!" + brandstory_part5_strong: "Open Food Brasil" + brandstory_part6: "–" learn_body: "Explore modelos, histórias e recursos para te dar suporte para desenvolver seu próprio negócio ou organização justa de alimentos. Encontre treinamento, eventos e outras oportunidades para aprender com quem faz parte do seu setor. " learn_cta: "Inspire-se" connect_body: "Procure em nossa lista de produtores, distribuidores e cooperativas para encontrar um comércio justo perto de você. Registre seu negócio ou organização na Open Food Brasil para que os consumidores possam te encontrar. Junte-se à comunidade para trocar experiências e resolver problemas." From 87ee4bbebc65dc7f7173deb999c8ae0b7e8778db Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 24 Mar 2020 13:24:24 +0100 Subject: [PATCH 051/130] Add spec for current problematic behaviour --- spec/models/calculator/weight_spec.rb | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/spec/models/calculator/weight_spec.rb b/spec/models/calculator/weight_spec.rb index 010abd1d0b..cebaba31cd 100644 --- a/spec/models/calculator/weight_spec.rb +++ b/spec/models/calculator/weight_spec.rb @@ -151,4 +151,32 @@ describe Calculator::Weight do end end end + + context "when variant_unit is 'items' and unit_value is zero" do + let(:product) { + create(:product, variant_unit: 'items', variant_unit_scale: nil, variant_unit_name: "bunch") + } + let(:variant1) { create(:variant, product: product, unit_value: 0, weight: nil) } + let(:variant2) { create(:variant, product: product, unit_value: 0, weight: 10.0) } + let(:line_item1) { create(:line_item, variant: variant1, quantity: 1) } + let(:line_item2) { create(:line_item, variant: variant2, quantity: 1) } + + before { subject.set_preference(:per_kg, 5) } + + it "returns NaN if variant.weight is not present" do + expect(subject.compute(line_item1).nan?).to be_truthy + end + + xit "uses zero weight if variant.weight is not present" do + expect(subject.compute(line_item1)).to eq 0 + end + + it "returns NaN if variant.weight is present" do + expect(subject.compute(line_item2).nan?).to be_truthy + end + + xit "uses the variant weight if variant.weight is present" do + expect(subject.compute(line_item2)).to eq 50.0 + end + end end From ef0fb18fdac6e85c4af79dd9755f4e61643d1fab Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 24 Mar 2020 13:51:47 +0100 Subject: [PATCH 052/130] Fix calculations for weight when variant.unit_value is zero --- app/models/calculator/weight.rb | 2 ++ spec/models/calculator/weight_spec.rb | 12 ++---------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/app/models/calculator/weight.rb b/app/models/calculator/weight.rb index cff4dba4fa..d5bf102c14 100644 --- a/app/models/calculator/weight.rb +++ b/app/models/calculator/weight.rb @@ -54,6 +54,8 @@ module Calculator # Customer ends up getting 350mL (line_item.final_weight_volume) of wine # that represent 2.8 (quantity_implied_in_final_weight_volume) glasses of wine def quantity_implied_in_final_weight_volume(line_item) + return line_item.quantity if line_item.variant.unit_value.zero? + (1.0 * line_item.final_weight_volume / line_item.variant.unit_value).round(3) end diff --git a/spec/models/calculator/weight_spec.rb b/spec/models/calculator/weight_spec.rb index cebaba31cd..8155456bf0 100644 --- a/spec/models/calculator/weight_spec.rb +++ b/spec/models/calculator/weight_spec.rb @@ -163,19 +163,11 @@ describe Calculator::Weight do before { subject.set_preference(:per_kg, 5) } - it "returns NaN if variant.weight is not present" do - expect(subject.compute(line_item1).nan?).to be_truthy - end - - xit "uses zero weight if variant.weight is not present" do + it "uses zero weight if variant.weight is not present" do expect(subject.compute(line_item1)).to eq 0 end - it "returns NaN if variant.weight is present" do - expect(subject.compute(line_item2).nan?).to be_truthy - end - - xit "uses the variant weight if variant.weight is present" do + it "uses the variant weight if variant.weight is present" do expect(subject.compute(line_item2)).to eq 50.0 end end From bddfa95eb53e3c71ecb8acb90dec77718210dea4 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Tue, 24 Mar 2020 16:12:35 +0000 Subject: [PATCH 053/130] Fix broken new property button in properties page --- app/views/spree/admin/properties/new.js.erb | 2 +- spec/features/admin/properties_spec.rb | 27 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 spec/features/admin/properties_spec.rb diff --git a/app/views/spree/admin/properties/new.js.erb b/app/views/spree/admin/properties/new.js.erb index d13137c1aa..c508aba3d0 100644 --- a/app/views/spree/admin/properties/new.js.erb +++ b/app/views/spree/admin/properties/new.js.erb @@ -1,2 +1,2 @@ -$("#new_property").html('<%= escape_javascript(render :template => "spree/admin/properties/new", :formats => [:html], :handlers => [:erb]) %>'); +$("#new_property").html('<%= escape_javascript(render :template => "spree/admin/properties/new", :formats => [:html], :handlers => [:haml]) %>'); $("#new_property_link").parent().hide(); diff --git a/spec/features/admin/properties_spec.rb b/spec/features/admin/properties_spec.rb new file mode 100644 index 0000000000..905810039e --- /dev/null +++ b/spec/features/admin/properties_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require "spec_helper" + +feature ' + As an admin + I want to manage product properties +' do + include AuthenticationWorkflow + + scenario "creating and editing a property" do + login_to_admin_section + visit spree.admin_properties_path + + click_link 'New Property' + fill_in 'property_name', with: 'New property!' + fill_in 'property_presentation', with: 'New property presentation!' + click_button 'Create' + expect(page).to have_content 'New property!' + + page.find('td.actions a.icon-edit').click + expect(page).to have_field 'property_name', with: 'New property!' + fill_in 'property_name', with: 'New changed property!' + click_button 'Update' + expect(page).to have_content 'New changed property!' + end +end From e27f7a43016544460159a9a268ee9046644b45cf Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 25 Mar 2020 08:42:21 +0100 Subject: [PATCH 054/130] Ensure perform_deliveries is correctly set when testing user confirmation emails --- spec/models/spree/user_spec.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/spec/models/spree/user_spec.rb b/spec/models/spree/user_spec.rb index d73042c67a..7ed818fee2 100644 --- a/spec/models/spree/user_spec.rb +++ b/spec/models/spree/user_spec.rb @@ -76,9 +76,11 @@ describe Spree.user_class do it "should send a confirmation email" do setup_email - expect do - create(:user, email: 'new_user@example.com', confirmation_sent_at: nil, confirmed_at: nil) - end.to send_confirmation_instructions + performing_deliveries do + expect do + create(:user, email: 'new_user@example.com', confirmation_sent_at: nil, confirmed_at: nil) + end.to send_confirmation_instructions + end sent_mail = ActionMailer::Base.deliveries.last expect(sent_mail.to).to eq ['new_user@example.com'] From d847560d7c9909bc2c6cb1d28c8f2f20b96d9027 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sat, 21 Mar 2020 10:54:02 +0100 Subject: [PATCH 055/130] Fix rubocop issues --- app/controllers/admin/enterprises_controller.rb | 3 ++- app/helpers/injection_helper.rb | 16 +++++++++++++--- app/helpers/spree/base_helper_decorator.rb | 10 +++++----- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/app/controllers/admin/enterprises_controller.rb b/app/controllers/admin/enterprises_controller.rb index 4fad65ce37..6f74043078 100644 --- a/app/controllers/admin/enterprises_controller.rb +++ b/app/controllers/admin/enterprises_controller.rb @@ -33,7 +33,8 @@ module Admin end def edit - @object = Enterprise.where(permalink: params[:id]).includes(users: [:ship_address, :bill_address]).first + @object = Enterprise.where(permalink: params[:id]). + includes(users: [:ship_address, :bill_address]).first super end diff --git a/app/helpers/injection_helper.rb b/app/helpers/injection_helper.rb index a10a4b1fee..bbc05c938f 100644 --- a/app/helpers/injection_helper.rb +++ b/app/helpers/injection_helper.rb @@ -3,10 +3,10 @@ require 'open_food_network/enterprise_injection_data' module InjectionHelper include SerializerHelper - def inject_enterprises(enterprises = Enterprise.activated.includes(address: [:state, :country]).all) + def inject_enterprises(enterprises = nil) inject_json_ams( "enterprises", - enterprises, + enterprises || default_enterprise_query, Api::EnterpriseSerializer, enterprise_injection_data ) @@ -41,8 +41,14 @@ module InjectionHelper end def inject_enterprise_and_relatives + enterprises_and_relatives = current_distributor. + relatives_including_self. + activated. + includes(address: [:state, :country]). + all + inject_json_ams "enterprises", - current_distributor.relatives_including_self.activated.includes(address: [:state, :country]).all, + enterprises_and_relatives, Api::EnterpriseSerializer, enterprise_injection_data end @@ -140,6 +146,10 @@ module InjectionHelper private + def default_enterprise_query + Enterprise.activated.includes(address: [:state, :country]).all + end + def enterprise_injection_data @enterprise_injection_data ||= OpenFoodNetwork::EnterpriseInjectionData.new { data: @enterprise_injection_data } diff --git a/app/helpers/spree/base_helper_decorator.rb b/app/helpers/spree/base_helper_decorator.rb index 8d80c51d9a..0125347d58 100644 --- a/app/helpers/spree/base_helper_decorator.rb +++ b/app/helpers/spree/base_helper_decorator.rb @@ -10,11 +10,11 @@ module Spree def available_countries checkout_zone = Zone.find_by_name(Spree::Config[:checkout_zone]) - if checkout_zone && checkout_zone.kind == 'country' - countries = checkout_zone.country_list - else - countries = Country.includes(:states).all - end + countries = if checkout_zone && checkout_zone.kind == 'country' + checkout_zone.country_list + else + Country.includes(:states).all + end countries.collect do |country| country.name = Spree.t(country.iso, scope: 'country_names', default: country.name) From 9af4bb9757bea166015ad4f4345a1fc144310470 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Wed, 25 Mar 2020 11:22:40 +0000 Subject: [PATCH 056/130] Use create instead of build so that we test with callbacks --- spec/models/calculator/weight_spec.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/models/calculator/weight_spec.rb b/spec/models/calculator/weight_spec.rb index 8155456bf0..298293db92 100644 --- a/spec/models/calculator/weight_spec.rb +++ b/spec/models/calculator/weight_spec.rb @@ -19,8 +19,8 @@ describe Calculator::Weight do end describe "line item with variant_unit weight and variant unit_value" do - let(:variant) { build(:variant, unit_value: 10_000) } - let(:line_item) { build(:line_item, variant: variant, quantity: 2) } + let(:variant) { create(:variant, unit_value: 10_000) } + let(:line_item) { create(:line_item, variant: variant, quantity: 2) } before { subject.set_preference(:per_kg, 5) } @@ -46,11 +46,11 @@ describe Calculator::Weight do end it "computes shipping cost for an object with an order" do - variant1 = build(:variant, unit_value: 10_000) - variant2 = build(:variant, unit_value: 20_000) + variant1 = create(:variant, unit_value: 10_000) + variant2 = create(:variant, unit_value: 20_000) - line_item1 = build(:line_item, variant: variant1, quantity: 1) - line_item2 = build(:line_item, variant: variant2, quantity: 2) + line_item1 = create(:line_item, variant: variant1, quantity: 1) + line_item2 = create(:line_item, variant: variant2, quantity: 2) order = double(:order, line_items: [line_item1, line_item2]) object_with_order = double(:object_with_order, order: order) @@ -65,7 +65,7 @@ describe Calculator::Weight do let(:calculator) { described_class.new(preferred_per_kg: 6) } let(:line_item) do - build(:line_item, variant: variant, quantity: 2).tap do |object| + create(:line_item, variant: variant, quantity: 2).tap do |object| object.send(:calculate_final_weight_volume) end end From 60d9edb185f06e8f1e1962224e84c7c5e490c06e Mon Sep 17 00:00:00 2001 From: Yuhao Gao Date: Wed, 25 Mar 2020 12:14:09 +0900 Subject: [PATCH 057/130] fix translation missing in order cycles outgoing page (closed #4937) --- config/locales/en.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/locales/en.yml b/config/locales/en.yml index 86c8f1611e..145fa2941f 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -914,6 +914,12 @@ en: cancel: "Cancel" back_to_list: "Back To List" outgoing: + outgoing: "Outgoing" + distributor: "Distributor" + products: "Products" + tags: "Tags" + delivery_details: "Delivery Details" + fees: "Fees" previous: "Previous" save: "Save" save_and_back_to_list: "Save and Back to List" From 24c8f38111bdf2a0d9b131d26dcc23a74a6d8cfd Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Wed, 25 Mar 2020 11:23:25 +0000 Subject: [PATCH 058/130] Restructure spec to avoid variable names with numbers --- spec/models/calculator/weight_spec.rb | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/spec/models/calculator/weight_spec.rb b/spec/models/calculator/weight_spec.rb index 298293db92..e469733b4e 100644 --- a/spec/models/calculator/weight_spec.rb +++ b/spec/models/calculator/weight_spec.rb @@ -156,19 +156,24 @@ describe Calculator::Weight do let(:product) { create(:product, variant_unit: 'items', variant_unit_scale: nil, variant_unit_name: "bunch") } - let(:variant1) { create(:variant, product: product, unit_value: 0, weight: nil) } - let(:variant2) { create(:variant, product: product, unit_value: 0, weight: 10.0) } - let(:line_item1) { create(:line_item, variant: variant1, quantity: 1) } - let(:line_item2) { create(:line_item, variant: variant2, quantity: 1) } + let(:line_item) { create(:line_item, variant: variant, quantity: 1) } before { subject.set_preference(:per_kg, 5) } - it "uses zero weight if variant.weight is not present" do - expect(subject.compute(line_item1)).to eq 0 + context "when variant.weight is present" do + let(:variant) { create(:variant, product: product, unit_value: 0, weight: 10.0) } + + it "uses the variant weight" do + expect(subject.compute(line_item)).to eq 50.0 + end end - it "uses the variant weight if variant.weight is present" do - expect(subject.compute(line_item2)).to eq 50.0 + context "when variant.weight is nil" do + let(:variant) { create(:variant, product: product, unit_value: 0, weight: nil) } + + it "uses zero weight" do + expect(subject.compute(line_item)).to eq 0 + end end end end From 523faa670d4460f3ef18409fc2c08a4d979b5bb3 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Wed, 25 Mar 2020 17:31:56 +0000 Subject: [PATCH 059/130] Remove FoundationRailsHelper, this is dead code now --- Gemfile | 1 - Gemfile.lock | 11 ----------- app/helpers/application_helper.rb | 2 -- 3 files changed, 14 deletions(-) diff --git a/Gemfile b/Gemfile index 7fbb46c5ee..72dc87335c 100644 --- a/Gemfile +++ b/Gemfile @@ -112,7 +112,6 @@ gem 'momentjs-rails' gem 'turbo-sprockets-rails3' gem "foundation-rails" -gem 'foundation_rails_helper', github: 'willrjmarshall/foundation_rails_helper', branch: "rails3" gem 'jquery-migrate-rails' gem 'jquery-rails', '3.1.5' diff --git a/Gemfile.lock b/Gemfile.lock index 84c427fb72..58fc7269d2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -65,16 +65,6 @@ GIT rails-i18n spree_core (>= 1.1) -GIT - remote: https://github.com/willrjmarshall/foundation_rails_helper.git - revision: 4d5d53fdc4b1fb71e66524d298c5c635de82cfbb - branch: rails3 - specs: - foundation_rails_helper (0.4) - actionpack (>= 3.0) - activemodel (>= 3.0) - railties (>= 3.0) - PATH remote: engines/catalog specs: @@ -740,7 +730,6 @@ DEPENDENCIES foreigner foundation-icons-sass-rails foundation-rails - foundation_rails_helper! fuubar (~> 2.5.0) geocoder gmaps4rails diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 0142825667..2e03e68bc0 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,6 +1,4 @@ module ApplicationHelper - include FoundationRailsHelper::FlashHelper - def feature?(feature) OpenFoodNetwork::FeatureToggle.enabled? feature end From 296d2e5edb54102d701ec0fbc39357b5404e8d16 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2020 19:17:31 +0000 Subject: [PATCH 060/130] Bump unicorn from 5.5.3 to 5.5.4 Bumps [unicorn](https://yhbt.net/unicorn/) from 5.5.3 to 5.5.4. Signed-off-by: dependabot-preview[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 84c427fb72..e05a795406 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -672,7 +672,7 @@ GEM uglifier (4.2.0) execjs (>= 0.3.0, < 3) unicode-display_width (1.6.1) - unicorn (5.5.3) + unicorn (5.5.4) kgio (~> 2.6) raindrops (~> 0.7) unicorn-rails (2.2.1) From b41b5d0395169bd1a50c0355bddc8a3222bcc430 Mon Sep 17 00:00:00 2001 From: Sigmund Petersen Date: Wed, 25 Mar 2020 20:38:45 +0100 Subject: [PATCH 061/130] Update Slack invitation link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9459c689d5..16a97f2709 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ We use [BrowserStack](https://www.browserstack.com/) as a manual testing tool. B Copyright (c) 2012 - 2020 Open Food Foundation, released under the AGPL licence. [survey]: https://docs.google.com/a/eaterprises.com.au/forms/d/1zxR5vSiU9CigJ9cEaC8-eJLgYid8CR8er7PPH9Mc-30/edit# -[slack-invite]: https://join.slack.com/t/openfoodnetwork/shared_invite/enQtNzY3NDEwNzM2MDM0LWFmNGRhNDUwYzNmNWNkYmFkMzgxNDg1OTg1ODNjNWY4Y2FhNDIwNmE4ZWI0OThiMGNmZjFkODczNGZiYTJmNWI +[slack-invite]: https://join.slack.com/t/openfoodnetwork/shared_invite/zt-9sjkjdlu-r02kUMP1zbrTgUhZhYPF~A [contributor-guide]: https://ofn-user-guide.gitbook.io/ofn-contributor-guide/who-are-we [ofn-install]: https://github.com/openfoodfoundation/ofn-install [super-admin-guide]: https://ofn-user-guide.gitbook.io/ofn-super-admin-guide From f5e254a105254a6097741e2fc8ddd1efd656c916 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 25 Mar 2020 10:05:00 +0100 Subject: [PATCH 062/130] Remove quick fix for hiding oc selector --- app/views/checkout/edit.html.haml | 2 +- app/views/shopping_shared/_header.html.haml | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/views/checkout/edit.html.haml b/app/views/checkout/edit.html.haml index 27271ae6d3..312b7b5a79 100644 --- a/app/views/checkout/edit.html.haml +++ b/app/views/checkout/edit.html.haml @@ -15,7 +15,7 @@ %strong = pickup_time current_order_cycle - = render partial: "shopping_shared/header", locals: { hide_oc_selector: true } + = render partial: "shopping_shared/header" %accordion{"close-others" => "false"} %checkout.row{"ng-controller" => "CheckoutCtrl"} diff --git a/app/views/shopping_shared/_header.html.haml b/app/views/shopping_shared/_header.html.haml index 37806539e2..03661a22ea 100644 --- a/app/views/shopping_shared/_header.html.haml +++ b/app/views/shopping_shared/_header.html.haml @@ -10,6 +10,5 @@ = distributor.name %location= distributor.address.city - - unless defined? hide_oc_selector - .show-for-large-up.large-4.columns - = render partial: "shopping_shared/order_cycles" + .show-for-large-up.large-4.columns + = render partial: "shopping_shared/order_cycles" From 91e88bd02842df82d45eb5f72f978f96ce7bb237 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 25 Mar 2020 10:08:48 +0100 Subject: [PATCH 063/130] Allow conditional use of order cycle sidebar We need to re-use the "shopping_shared/header" partial in multiple places, but we don't always want the (nested) order cycle sidebar inside it. --- app/views/checkout/edit.html.haml | 5 ++++- app/views/enterprises/shop.html.haml | 4 ++++ app/views/shopping_shared/_header.html.haml | 3 +-- app/views/spree/orders/edit.html.haml | 4 ++++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/app/views/checkout/edit.html.haml b/app/views/checkout/edit.html.haml index 312b7b5a79..2f5d2afabe 100644 --- a/app/views/checkout/edit.html.haml +++ b/app/views/checkout/edit.html.haml @@ -7,7 +7,6 @@ .darkswarm.footer-pad - content_for :order_cycle_form do - %closing = t :checkout_now %p @@ -15,6 +14,10 @@ %strong = pickup_time current_order_cycle + - content_for :ordercycle_sidebar do + .show-for-large-up.large-4.columns + = render partial: "shopping_shared/order_cycles" + = render partial: "shopping_shared/header" %accordion{"close-others" => "false"} diff --git a/app/views/enterprises/shop.html.haml b/app/views/enterprises/shop.html.haml index e08625d0df..7f98960b33 100644 --- a/app/views/enterprises/shop.html.haml +++ b/app/views/enterprises/shop.html.haml @@ -38,6 +38,10 @@ - if oc_select_options.count > 1 %option{value: "", disabled: "", selected: ""}= t :shopping_oc_select + - content_for :ordercycle_sidebar do + .show-for-large-up.large-4.columns + = render partial: "shopping_shared/order_cycles" + = render partial: "shopping_shared/header" = render partial: "shopping_shared/tabs" diff --git a/app/views/shopping_shared/_header.html.haml b/app/views/shopping_shared/_header.html.haml index 03661a22ea..e718e9247c 100644 --- a/app/views/shopping_shared/_header.html.haml +++ b/app/views/shopping_shared/_header.html.haml @@ -10,5 +10,4 @@ = distributor.name %location= distributor.address.city - .show-for-large-up.large-4.columns - = render partial: "shopping_shared/order_cycles" + = yield :ordercycle_sidebar diff --git a/app/views/spree/orders/edit.html.haml b/app/views/spree/orders/edit.html.haml index 56f4e03ef8..e1ded2b7a8 100644 --- a/app/views/spree/orders/edit.html.haml +++ b/app/views/spree/orders/edit.html.haml @@ -16,6 +16,10 @@ - else = @order.distributor.next_collection_at + - content_for :ordercycle_sidebar do + .show-for-large-up.large-4.columns + = render partial: "shopping_shared/order_cycles" + = render partial: "shopping_shared/header" %fieldset.footer-pad From 81537d92cf870cd90cef5fbf7fed851ee4301dac Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 25 Mar 2020 11:22:47 +0100 Subject: [PATCH 064/130] Extract styles for distributor header These styles are used in shop, cart, checkout, order confirmation. Not just shop. --- .../darkswarm/_shop-navigation.css.scss | 42 ------------------ .../darkswarm/distributor_header.css.scss | 43 +++++++++++++++++++ 2 files changed, 43 insertions(+), 42 deletions(-) create mode 100644 app/assets/stylesheets/darkswarm/distributor_header.css.scss diff --git a/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss b/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss index fd08599222..ccc2e1b54d 100644 --- a/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss +++ b/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss @@ -1,47 +1,5 @@ @import "typography"; -.darkswarm navigation { - display: block; - background: $white; - - distributor.details { - box-sizing: border-box; - display: block; - min-height: 150px; - padding: 30px 0 20px 0; - position: relative; - - select { - width: 200px; - } - - img { - display: block; - height: 100px; - width: 100px; - margin-right: 12px; - } - - location { - @include headingFont; - } - @media all and (max-width: 768px) { - location, location + small { - display: block; - } - } - - #distributor_title h3 { - margin-top: 0; - padding-top: 0.45em; - - @media all and (max-width: 768px) { - margin-bottom: 8px; - } - } - } -} - ordercycle { float: right; background: $teal-400; diff --git a/app/assets/stylesheets/darkswarm/distributor_header.css.scss b/app/assets/stylesheets/darkswarm/distributor_header.css.scss new file mode 100644 index 0000000000..fb1dc01a55 --- /dev/null +++ b/app/assets/stylesheets/darkswarm/distributor_header.css.scss @@ -0,0 +1,43 @@ +@import "typography"; + +.darkswarm navigation { + display: block; + background: $white; + + distributor.details { + box-sizing: border-box; + display: block; + min-height: 150px; + padding: 30px 0 20px 0; + position: relative; + + select { + width: 200px; + } + + img { + display: block; + height: 100px; + width: 100px; + margin-right: 12px; + } + + location { + @include headingFont; + } + @media all and (max-width: 768px) { + location, location + small { + display: block; + } + } + + #distributor_title h3 { + margin-top: 0; + padding-top: 0.45em; + + @media all and (max-width: 768px) { + margin-bottom: 8px; + } + } + } +} From cff8f6dd96713b61a0c3a24d00f8aeeb4153e82b Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 25 Mar 2020 11:23:44 +0100 Subject: [PATCH 065/130] Add box-shadow to distributor header for cart, checkout, and order confirmation pages --- .../stylesheets/darkswarm/distributor_header.css.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/assets/stylesheets/darkswarm/distributor_header.css.scss b/app/assets/stylesheets/darkswarm/distributor_header.css.scss index fb1dc01a55..d06ef83c4c 100644 --- a/app/assets/stylesheets/darkswarm/distributor_header.css.scss +++ b/app/assets/stylesheets/darkswarm/distributor_header.css.scss @@ -1,5 +1,11 @@ @import "typography"; +section { + :not(shop) navigation { + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.05), 0 8px 6px -6px rgba(0, 0, 0, 0.2) + } +} + .darkswarm navigation { display: block; background: $white; From a3a26f704fa0975d998cfba2864b217eba5cb3d3 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 25 Mar 2020 11:32:28 +0100 Subject: [PATCH 066/130] Extract re-used box-shadow style --- app/assets/stylesheets/darkswarm/branding.css.scss | 2 ++ app/assets/stylesheets/darkswarm/distributor_header.css.scss | 2 +- app/assets/stylesheets/darkswarm/shop_tabs.css.scss | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/branding.css.scss b/app/assets/stylesheets/darkswarm/branding.css.scss index 55401e0f24..c1b6ffdd6e 100644 --- a/app/assets/stylesheets/darkswarm/branding.css.scss +++ b/app/assets/stylesheets/darkswarm/branding.css.scss @@ -1,5 +1,7 @@ $ofn-brand: #f27052; +$distributor-header-shadow: 0 1px 0 rgba(0, 0, 0, 0.05), 0 8px 6px -6px rgba(0, 0, 0, 0.2); + // e.g. australia, uk, norway specific color $ofn-grey: #808184; diff --git a/app/assets/stylesheets/darkswarm/distributor_header.css.scss b/app/assets/stylesheets/darkswarm/distributor_header.css.scss index d06ef83c4c..6fb73d7186 100644 --- a/app/assets/stylesheets/darkswarm/distributor_header.css.scss +++ b/app/assets/stylesheets/darkswarm/distributor_header.css.scss @@ -2,7 +2,7 @@ section { :not(shop) navigation { - box-shadow: 0 1px 0 rgba(0, 0, 0, 0.05), 0 8px 6px -6px rgba(0, 0, 0, 0.2) + box-shadow: $distributor-header-shadow; } } diff --git a/app/assets/stylesheets/darkswarm/shop_tabs.css.scss b/app/assets/stylesheets/darkswarm/shop_tabs.css.scss index a4c9c1f692..7eaf76afeb 100644 --- a/app/assets/stylesheets/darkswarm/shop_tabs.css.scss +++ b/app/assets/stylesheets/darkswarm/shop_tabs.css.scss @@ -7,7 +7,7 @@ .tab-buttons { color: $dark-grey; - box-shadow: 0 1px 0 rgba(0,0,0,0.05), 0 8px 6px -6px rgba(0, 0, 0, 0.2); + box-shadow: $distributor-header-shadow; .columns { display: flex; From 52ebd1b4028d183f6da749489deed697c5c0b97d Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 25 Mar 2020 11:43:08 +0100 Subject: [PATCH 067/130] Remove order cycle popovers; these styles are no longer used --- .../darkswarm/_shop-popovers.css.scss | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/_shop-popovers.css.scss b/app/assets/stylesheets/darkswarm/_shop-popovers.css.scss index c1a4c8ffe3..d233284767 100644 --- a/app/assets/stylesheets/darkswarm/_shop-popovers.css.scss +++ b/app/assets/stylesheets/darkswarm/_shop-popovers.css.scss @@ -1,31 +1,6 @@ @import "mixins"; @import "branding"; -// .darkswarm -// product - -ordercycle { - .joyride-tip-guide { - background-color: $clr-brick; - - .joyride-nub.right { - border-color: $clr-brick !important; - border-top-color: transparent !important; - border-right-color: transparent !important; - border-bottom-color: transparent !important; - } - - p { - margin: 0; - font-weight: 700; - } - - @media all and (max-width: 768px) { - z-index: 10; - } - } -} - // Pop over // Foundation overrides .joyride-tip-guide.price_breakdown { From 7a3549209faff748651c1b2384d48a45ab2921b8 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 25 Mar 2020 12:04:30 +0100 Subject: [PATCH 068/130] Use 4 columns on checkout sidebar --- app/views/checkout/edit.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/checkout/edit.html.haml b/app/views/checkout/edit.html.haml index 2f5d2afabe..35b9269a0f 100644 --- a/app/views/checkout/edit.html.haml +++ b/app/views/checkout/edit.html.haml @@ -22,12 +22,12 @@ %accordion{"close-others" => "false"} %checkout.row{"ng-controller" => "CheckoutCtrl"} - .small-12.medium-8.large-9.columns + .small-12.medium-8.columns - unless spree_current_user = render partial: "checkout/authentication" %div{"ng-show" => "enabled", "ng-controller" => "AccordionCtrl"} = render partial: "checkout/form" - .small-12.medium-4.large-3.columns + .small-12.medium-4.columns = render partial: "checkout/summary" From eb4d970bc7d2a069b0ba63b511d2ab681b443d88 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 25 Mar 2020 12:43:31 +0100 Subject: [PATCH 069/130] Adjust positioning for order cycle sidebar --- .../darkswarm/_shop-navigation.css.scss | 15 ++++++++++----- .../darkswarm/distributor_header.css.scss | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss b/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss index ccc2e1b54d..36a8d6b6d9 100644 --- a/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss +++ b/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss @@ -6,11 +6,8 @@ ordercycle { color: $white; width: 100%; border-radius: 0.5em 0.5em 0 0; - position: absolute; - right: 1em; - padding: 1em; - margin-top: 3em; - height: 7.6em; + margin-top: 1em; + padding: 1em 1em 0; &.requires-selection { background-color: $red-700; @@ -147,3 +144,11 @@ ordercycle { } } } + +shop navigation ordercycle { + margin-top: 3em; + padding: 1em; + height: 7.6em; + position: absolute; + right: 1em; +} diff --git a/app/assets/stylesheets/darkswarm/distributor_header.css.scss b/app/assets/stylesheets/darkswarm/distributor_header.css.scss index 6fb73d7186..5e402580aa 100644 --- a/app/assets/stylesheets/darkswarm/distributor_header.css.scss +++ b/app/assets/stylesheets/darkswarm/distributor_header.css.scss @@ -14,7 +14,7 @@ section { box-sizing: border-box; display: block; min-height: 150px; - padding: 30px 0 20px 0; + padding: 30px 0 0 0; position: relative; select { From a08020490d05b4593ae0068190253486965378bb Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 25 Mar 2020 12:52:14 +0100 Subject: [PATCH 070/130] Define new colour and use variable where already in use --- app/assets/stylesheets/darkswarm/active_table_search.css.scss | 2 +- app/assets/stylesheets/darkswarm/branding.css.scss | 2 ++ app/assets/stylesheets/darkswarm/checkout.css.scss | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/active_table_search.css.scss b/app/assets/stylesheets/darkswarm/active_table_search.css.scss index 4159126538..b934116cdc 100644 --- a/app/assets/stylesheets/darkswarm/active_table_search.css.scss +++ b/app/assets/stylesheets/darkswarm/active_table_search.css.scss @@ -27,7 +27,7 @@ } products .filter-box { - background: #f7f7f7; + background: $grey-050; } .filter-box { diff --git a/app/assets/stylesheets/darkswarm/branding.css.scss b/app/assets/stylesheets/darkswarm/branding.css.scss index c1b6ffdd6e..b8ab72349c 100644 --- a/app/assets/stylesheets/darkswarm/branding.css.scss +++ b/app/assets/stylesheets/darkswarm/branding.css.scss @@ -41,6 +41,8 @@ $light-grey-transparency: rgba(0, 0, 0, .1); $black: #000; $white: #fff; +$grey-050: #f7f7f7; + $grey-400: #bbb; $grey-500: #999; $grey-600: #777; diff --git a/app/assets/stylesheets/darkswarm/checkout.css.scss b/app/assets/stylesheets/darkswarm/checkout.css.scss index 458fd24d5e..1ad32a975b 100644 --- a/app/assets/stylesheets/darkswarm/checkout.css.scss +++ b/app/assets/stylesheets/darkswarm/checkout.css.scss @@ -44,7 +44,7 @@ checkout { h5 { margin: 0; padding: 0.65em; - background: #f7f7f7; + background: $grey-050; .label { font-size: 1em; From 7daba62f430f57cb15d8a81c4184e9daa00b84da Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 25 Mar 2020 13:01:24 +0100 Subject: [PATCH 071/130] Update colours for different order cycle sidebar cases --- .../darkswarm/_shop-navigation.css.scss | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss b/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss index 36a8d6b6d9..2aff5bcac2 100644 --- a/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss +++ b/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss @@ -2,32 +2,13 @@ ordercycle { float: right; - background: $teal-400; - color: $white; + background: $grey-050; + color: $grey-800; width: 100%; border-radius: 0.5em 0.5em 0 0; margin-top: 1em; padding: 1em 1em 0; - &.requires-selection { - background-color: $red-700; - - .order-cycle-select { - border: 1px solid $red-500; - - .select-label { - background-color: rgba($red-500, 0.5); - } - - select { - background-color: $white; - background-image: url('/assets/black-caret.svg'); - color: $grey-500; - font-style: italic; - } - } - } - p { max-width: 400px; } @@ -119,7 +100,7 @@ ordercycle { closing { @include headingFont; - color: $white; + color: $grey-800; font-size: 1.25rem; display: block; padding: 0 0 12px; @@ -151,4 +132,29 @@ shop navigation ordercycle { height: 7.6em; position: absolute; right: 1em; + background: $teal-400; + color: $white; + + &.requires-selection { + background-color: $red-700; + + .order-cycle-select { + border: 1px solid $red-500; + + .select-label { + background-color: rgba($red-500, 0.5); + } + + select { + background-color: $white; + background-image: url('/assets/black-caret.svg'); + color: $grey-500; + font-style: italic; + } + } + } + + closing { + color: $white; + } } From b2ed69831beddd37720114ca730cb3d98f043b87 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 25 Mar 2020 13:36:32 +0100 Subject: [PATCH 072/130] Adjust style contexts --- .../darkswarm/_shop-navigation.css.scss | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss b/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss index 2aff5bcac2..c3d16aa4d1 100644 --- a/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss +++ b/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss @@ -33,14 +33,10 @@ ordercycle { } } - @media all and (max-width: 768px) { + @media all and (max-width: 480px) { padding: 0.5em 1em 0.75em; } - form.custom { - text-align: center; - } - .order-cycle-select { border: 1px solid $teal-300; display: inline-block; @@ -126,12 +122,7 @@ ordercycle { } } -shop navigation ordercycle { - margin-top: 3em; - padding: 1em; - height: 7.6em; - position: absolute; - right: 1em; +shop ordercycle { background: $teal-400; color: $white; @@ -157,4 +148,16 @@ shop navigation ordercycle { closing { color: $white; } + + form.custom { + text-align: center; + } +} + +shop navigation ordercycle { + margin-top: 3em; + padding: 1em; + height: 7.6em; + position: absolute; + right: 1em; } From c3283adcf5201146d41c701b735d6f2f260c13a7 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 25 Mar 2020 14:25:04 +0100 Subject: [PATCH 073/130] Show full-width ordercycle subheader on tablet and below --- .../darkswarm/_shop-navigation.css.scss | 39 ++++++++++++------- .../darkswarm/distributor_header.css.scss | 2 + app/views/checkout/edit.html.haml | 3 ++ app/views/spree/orders/edit.html.haml | 3 ++ 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss b/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss index c3d16aa4d1..70ddb8c652 100644 --- a/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss +++ b/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss @@ -7,7 +7,7 @@ ordercycle { width: 100%; border-radius: 0.5em 0.5em 0 0; margin-top: 1em; - padding: 1em 1em 0; + padding: 1em 1.25em 0; p { max-width: 400px; @@ -99,20 +99,7 @@ ordercycle { color: $grey-800; font-size: 1.25rem; display: block; - padding: 0 0 12px; - - @media all and (max-width: 1024px) { - float: none; - display: inline-block; - padding: 0.2em 0 0; - font-size: 1.2em; - margin-right: 1em; - } - - @media all and (max-width: 768px) { - float: none; - padding: 0 0 10px; - } + padding: 0.5em 0 0.5em; span { @media all and (max-width: 768px) { @@ -147,6 +134,20 @@ shop ordercycle { closing { color: $white; + padding: 0 0 12px; + + @media all and (max-width: 1024px) { + float: none; + display: inline-block; + padding: 0.2em 0 0; + font-size: 1.2em; + margin-right: 1em; + } + + @media all and (max-width: 768px) { + float: none; + padding: 0 0 10px; + } } form.custom { @@ -161,3 +162,11 @@ shop navigation ordercycle { position: absolute; right: 1em; } + +.sub-header { + form { + p { + margin-bottom: 0.75em; + } + } +} diff --git a/app/assets/stylesheets/darkswarm/distributor_header.css.scss b/app/assets/stylesheets/darkswarm/distributor_header.css.scss index 5e402580aa..1dbe38641d 100644 --- a/app/assets/stylesheets/darkswarm/distributor_header.css.scss +++ b/app/assets/stylesheets/darkswarm/distributor_header.css.scss @@ -9,6 +9,8 @@ section { .darkswarm navigation { display: block; background: $white; + position: relative; + z-index: 2; distributor.details { box-sizing: border-box; diff --git a/app/views/checkout/edit.html.haml b/app/views/checkout/edit.html.haml index 35b9269a0f..9d85b3e8df 100644 --- a/app/views/checkout/edit.html.haml +++ b/app/views/checkout/edit.html.haml @@ -20,6 +20,9 @@ = render partial: "shopping_shared/header" + .sub-header.show-for-medium-down + = render partial: "shopping_shared/order_cycles" + %accordion{"close-others" => "false"} %checkout.row{"ng-controller" => "CheckoutCtrl"} .small-12.medium-8.columns diff --git a/app/views/spree/orders/edit.html.haml b/app/views/spree/orders/edit.html.haml index e1ded2b7a8..f654cfebb1 100644 --- a/app/views/spree/orders/edit.html.haml +++ b/app/views/spree/orders/edit.html.haml @@ -22,6 +22,9 @@ = render partial: "shopping_shared/header" + .sub-header.show-for-medium-down + = render partial: "shopping_shared/order_cycles" + %fieldset.footer-pad - if @order.line_items.empty? %div.row{"data-hook" => "empty_cart"} From e63dbcfa898dd750d61cf87430c7541cfe12562b Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 25 Mar 2020 17:58:36 +0100 Subject: [PATCH 074/130] Fix some SCSS linting warnings --- app/assets/stylesheets/darkswarm/_shop-navigation.css.scss | 2 +- .../stylesheets/darkswarm/distributor_header.css.scss | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss b/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss index 70ddb8c652..80c2bfe09a 100644 --- a/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss +++ b/app/assets/stylesheets/darkswarm/_shop-navigation.css.scss @@ -99,7 +99,7 @@ ordercycle { color: $grey-800; font-size: 1.25rem; display: block; - padding: 0.5em 0 0.5em; + padding: 0.5em 0; span { @media all and (max-width: 768px) { diff --git a/app/assets/stylesheets/darkswarm/distributor_header.css.scss b/app/assets/stylesheets/darkswarm/distributor_header.css.scss index 1dbe38641d..8e3fdc2a9b 100644 --- a/app/assets/stylesheets/darkswarm/distributor_header.css.scss +++ b/app/assets/stylesheets/darkswarm/distributor_header.css.scss @@ -1,4 +1,4 @@ -@import "typography"; +@import 'typography'; section { :not(shop) navigation { @@ -12,11 +12,11 @@ section { position: relative; z-index: 2; - distributor.details { + .details { box-sizing: border-box; display: block; min-height: 150px; - padding: 30px 0 0 0; + padding: 30px 0 0; position: relative; select { @@ -33,6 +33,7 @@ section { location { @include headingFont; } + @media all and (max-width: 768px) { location, location + small { display: block; From 4bcd665379e00dda0c3f9be49c5409fbd43a21aa Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 26 Mar 2020 17:13:29 +1100 Subject: [PATCH 075/130] Handle all line items without unit_value in weight calculation --- app/models/calculator/weight.rb | 2 +- spec/models/calculator/weight_spec.rb | 30 ++++++++++++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/app/models/calculator/weight.rb b/app/models/calculator/weight.rb index d5bf102c14..009d6bbf83 100644 --- a/app/models/calculator/weight.rb +++ b/app/models/calculator/weight.rb @@ -54,7 +54,7 @@ module Calculator # Customer ends up getting 350mL (line_item.final_weight_volume) of wine # that represent 2.8 (quantity_implied_in_final_weight_volume) glasses of wine def quantity_implied_in_final_weight_volume(line_item) - return line_item.quantity if line_item.variant.unit_value.zero? + return line_item.quantity if line_item.variant.unit_value.to_f.zero? (1.0 * line_item.final_weight_volume / line_item.variant.unit_value).round(3) end diff --git a/spec/models/calculator/weight_spec.rb b/spec/models/calculator/weight_spec.rb index e469733b4e..1ca1759b40 100644 --- a/spec/models/calculator/weight_spec.rb +++ b/spec/models/calculator/weight_spec.rb @@ -152,7 +152,7 @@ describe Calculator::Weight do end end - context "when variant_unit is 'items' and unit_value is zero" do + context "when variant_unit is 'items'" do let(:product) { create(:product, variant_unit: 'items', variant_unit_scale: nil, variant_unit_name: "bunch") } @@ -160,7 +160,7 @@ describe Calculator::Weight do before { subject.set_preference(:per_kg, 5) } - context "when variant.weight is present" do + context "when unit_value is zero variant.weight is present" do let(:variant) { create(:variant, product: product, unit_value: 0, weight: 10.0) } it "uses the variant weight" do @@ -168,12 +168,36 @@ describe Calculator::Weight do end end - context "when variant.weight is nil" do + context "when unit_value is zero variant.weight is nil" do let(:variant) { create(:variant, product: product, unit_value: 0, weight: nil) } it "uses zero weight" do expect(subject.compute(line_item)).to eq 0 end end + + context "when unit_value is nil and variant.weight is present" do + let(:variant) { + create(:variant, product: product, unit_description: "bunches", unit_value: nil, weight: 10.0) + } + + it "uses the variant weight" do + line_item.final_weight_volume = 1 + + expect(subject.compute(line_item)).to eq 50.0 + end + end + + context "when unit_value is nil and variant.weight is nil" do + let(:variant) { + create(:variant, product: product, unit_description: "bunches", unit_value: nil, weight: nil) + } + + it "uses zero weight" do + line_item.final_weight_volume = 1 + + expect(subject.compute(line_item)).to eq 0 + end + end end end From 2966dd953612b28eeb47c3a0a5bc191e3fba8d29 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Thu, 26 Mar 2020 14:25:34 +0100 Subject: [PATCH 076/130] Update all locales with the latest Transifex translations --- config/locales/ar.yml | 36 ++++++++++++- config/locales/ca.yml | 3 +- config/locales/de_DE.yml | 1 - config/locales/en_AU.yml | 1 - config/locales/en_BE.yml | 1 - config/locales/en_DE.yml | 1 - config/locales/en_FR.yml | 1 - config/locales/en_GB.yml | 1 - config/locales/en_US.yml | 112 ++++++++++++++++++++++++++++++++++++++- config/locales/en_ZA.yml | 1 - config/locales/es.yml | 1 - config/locales/fr.yml | 1 - config/locales/fr_BE.yml | 1 - config/locales/fr_CA.yml | 1 - config/locales/it.yml | 5 +- config/locales/nb.yml | 1 - config/locales/nl_BE.yml | 1 - config/locales/pt.yml | 1 - config/locales/pt_BR.yml | 8 +-- config/locales/sv.yml | 1 - config/locales/tr.yml | 1 - 21 files changed, 153 insertions(+), 27 deletions(-) diff --git a/config/locales/ar.yml b/config/locales/ar.yml index d11e100468..56fac2a708 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -246,6 +246,8 @@ ar: notes: ملاحظات error: خطأ processing_payment: "معالجة الدفع..." + no_pending_payments: "لا توجد دفعات معلقة" + invalid_payment_state: "حالة الدفع غير صالحة" filter_results: تصفية النتائج quantity: الكمية pick_up: النقل @@ -1483,6 +1485,7 @@ ar: shopping_oc_closed_description: "يرجى الانتظار حتى تفتح الدورة التالية (أو اتصل بنا مباشرة لمعرفة ما إذا كان يمكننا قبول أي طلبات متأخرة)" shopping_oc_last_closed: "آخر دورة تم إغلاق %{distance_of_time} منذ" shopping_oc_next_open: "تفتح الدورة التالية في %{distance_of_time}" + shopping_oc_select: "اختيار..." shopping_tabs_home: "الصفحة الرئيسية" shopping_tabs_shop: "المتجر" shopping_tabs_about: "حول" @@ -1864,6 +1867,7 @@ ar: headline: "تم الانتهاء !" thanks: "شكرًا لملء تفاصيل %{enterprise}." login: "يمكنك تغيير أو تحديث شركتك في أي مرحلة من خلال تسجيل الدخول إلى شبكة الغذاء المفتوح والذهاب إلى المسؤول." + action: "انتقل إلى واجهة الشركة" back: "عودة" continue: "استمر" action_or: "أو" @@ -2278,6 +2282,7 @@ ar: enterprise_register_success_notice: "تهانينا! اكتمل التسجيل لـ %{enterprise}!" enterprise_bulk_update_success_notice: "تم تحديث المؤسسات بنجاح" enterprise_bulk_update_error: 'فشل التحديث' + enterprise_shop_show_error: "المتجر الذي تبحث عنه غير موجود أو غير نشط . يرجى التحقق من المحلات التجارية الأخرى." order_cycles_create_notice: 'تم إنشاء دورة الطلب الخاصة بك.' order_cycles_update_notice: 'تم تحديث دورة الطلب.' order_cycles_bulk_update_notice: 'تم تحديث دورات الطلب.' @@ -2603,6 +2608,34 @@ ar: few: "حزم" many: "حزم" other: "حزم" + bottle: + zero: "زجاجات" + one: "زجاجة" + two: "زجاجات" + few: "زجاجات" + many: "زجاجات" + other: "زجاجات" + item: + zero: "العناصر" + one: "بند" + two: "العناصر" + few: "العناصر" + many: "العناصر" + other: "المواد" + dozen: + zero: "العشرات" + one: "دزينة" + two: "العشرات" + few: "العشرات" + many: "العشرات" + other: "العشرات" + unit: + zero: "الوحدات" + one: "وحدة" + two: "الوحدات" + few: "الوحدات" + many: "الوحدات" + other: "الوحدات" producers: signup: start_free_profile: "ابدأ بملف تعريف مجاني ، وتوسع عندما تكون جاهزًا!" @@ -2956,7 +2989,8 @@ ar: tax_invoice: "فاتورة ضريبية" code: "الشفرة" from: "من " - to: "إلى" + to: "فاتورة الى" + shipping: "الشحن" form: distribution_fields: title: "توزيع" diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 7f7f1f9476..8033d2018f 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -1425,7 +1425,7 @@ ca: email_confirm_customer_greeting: "Hola %{name}," email_confirm_customer_intro_html: "Gràcies per comprar a %{distributor}." email_confirm_customer_number_html: "Confirmació de la comanda # %{number} " - email_confirm_customer_details_html: "Aquests són els detalls de la teva comanda de %{distributor} :" + email_confirm_customer_details_html: "Aquests són els detalls de la teva comanda de%{distributor}:" email_confirm_customer_signoff: "Salutacions cordials," email_confirm_shop_greeting: "Hola %{name}," email_confirm_shop_order_html: "Que bé! Tens una nova comanda per %{distributor} ." @@ -3015,7 +3015,6 @@ ca: tax_invoice: "FACTURA D'IMPOSTOS" code: "Codi" from: "De" - to: "Per a" form: distribution_fields: title: "Distribució" diff --git a/config/locales/de_DE.yml b/config/locales/de_DE.yml index 9025b190f1..589dd9bb33 100644 --- a/config/locales/de_DE.yml +++ b/config/locales/de_DE.yml @@ -2927,7 +2927,6 @@ de_DE: tax_invoice: "Steuerrechnung" code: "Code" from: "Von" - to: "Zu" form: distribution_fields: title: "Verteilung" diff --git a/config/locales/en_AU.yml b/config/locales/en_AU.yml index 2e6b6d1121..741661f508 100644 --- a/config/locales/en_AU.yml +++ b/config/locales/en_AU.yml @@ -2927,7 +2927,6 @@ en_AU: tax_invoice: "TAX INVOICE" code: "Code" from: "From" - to: "To" form: distribution_fields: title: "Distribution" diff --git a/config/locales/en_BE.yml b/config/locales/en_BE.yml index 1e1c139a46..51cf1d2479 100644 --- a/config/locales/en_BE.yml +++ b/config/locales/en_BE.yml @@ -2856,7 +2856,6 @@ en_BE: tax_invoice: "TAX INVOICE" code: "Code" from: "From" - to: "To" form: distribution_fields: title: "Distribution" diff --git a/config/locales/en_DE.yml b/config/locales/en_DE.yml index 5d71e42c64..462ab3502f 100644 --- a/config/locales/en_DE.yml +++ b/config/locales/en_DE.yml @@ -2872,7 +2872,6 @@ en_DE: tax_invoice: "TAX INVOICE" code: "Code" from: "From" - to: "To" form: distribution_fields: title: "Distribution" diff --git a/config/locales/en_FR.yml b/config/locales/en_FR.yml index 03dbc99fcf..f34151ad66 100644 --- a/config/locales/en_FR.yml +++ b/config/locales/en_FR.yml @@ -3019,7 +3019,6 @@ en_FR: tax_invoice: "TAX INVOICE" code: "Code" from: "From" - to: "To" form: distribution_fields: title: "Distribution" diff --git a/config/locales/en_GB.yml b/config/locales/en_GB.yml index ff495627d6..7f12eb59bf 100644 --- a/config/locales/en_GB.yml +++ b/config/locales/en_GB.yml @@ -3025,7 +3025,6 @@ en_GB: tax_invoice: "TAX INVOICE" code: "Code" from: "From" - to: "To" form: distribution_fields: title: "Distribution" diff --git a/config/locales/en_US.yml b/config/locales/en_US.yml index 03a8308f2a..1cb6cfdcb4 100644 --- a/config/locales/en_US.yml +++ b/config/locales/en_US.yml @@ -50,6 +50,8 @@ en_US: shipping_method_ids: "Shipping Methods" payment_method_ids: "Payment Methods" errors: + messages: + inclusion: "is not included in the list" models: subscription_validator: attributes: @@ -116,7 +118,9 @@ en_US: email_welcome: "Welcome" email_registered: "is now part of" email_userguide_html: "The User Guide with detailed support for setting up your Producer or Hub is here: %{link}" + userguide: "Open Food Network User Guide" email_admin_html: "You can manage your account by logging into the %{link} or by clicking on the cog in the top right hand side of the homepage, and selecting Administration." + admin_panel: "Admin Panel" email_community_html: "We also have an online forum for community discussion related to OFN software and the unique challenges of running a food enterprise. You are encouraged to join in. We are constantly evolving and your input into this forum will shape what happens next. %{link}" join_community: "Join the community" invite_manager: @@ -242,6 +246,8 @@ en_US: notes: Notes error: Error processing_payment: "Processing payment..." + no_pending_payments: "No pending payments" + invalid_payment_state: "Invalid payment state" filter_results: Filter Results quantity: Quantity pick_up: Pick up @@ -434,9 +440,12 @@ en_US: infinity: "Infinity" to_order_tip: "Items made to order do not have a set stock level, such as loaves of bread made fresh to order." back_to_products_list: "Back to products list" + editing_product: "Editing Product" tabs: + product_details: "Product Details" group_buy_options: "Group Buy Options" images: "Images" + variants: "Variants" product_properties: "Product Properties" product_import: title: Product Import @@ -539,6 +548,7 @@ en_US: title: Inventory description: Use this page to manage inventories for your enterprises. Any product details set here will override those set on the 'Products' page enable_reset?: Enable Stock Reset? + default_stock: "Default stock" inherit?: Inherit? add: Add hide: Hide @@ -693,6 +703,10 @@ en_US: enable_subscriptions_false: "Disabled" enable_subscriptions_true: "Enabled" shopfront_message: "Shopfront Message" + shopfront_message_placeholder: > + An optional message to welcome customers and explain how to shop with + you. If text is entered here it will be displayed in a home tab when + customers first arrive at your shopfront. shopfront_message_link_tooltip: "Insert / edit link" shopfront_message_link_prompt: "Please enter a URL to insert" shopfront_closed_message: "Shopfront Closed Message" @@ -830,20 +844,32 @@ en_US: new: create: "Create" cancel: "Cancel" + back_to_list: "Back To List" edit: advanced_settings: "Advanced Settings" save: "Save" + save_and_next: "Save and Next" next: "Next" cancel: "Cancel" + back_to_list: "Back To List" + save_and_back_to_list: "Save and Back to List" choose_products_from: "Choose Products From:" incoming: save: "Save" + save_and_next: "Save and Next" next: "Next" cancel: "Cancel" + back_to_list: "Back To List" outgoing: previous: "Previous" save: "Save" + save_and_back_to_list: "Save and Back to List" cancel: "Cancel" + back_to_list: "Back To List" + wizard_progress: + edit: "1. General Settings" + incoming: "2. Incoming Products" + outgoing: "3. Outgoing Products" exchange_form: pickup_time_tip: When orders from this OC will be ready for the customer pickup_instructions_placeholder: "Pick-up instructions" @@ -1081,10 +1107,13 @@ en_US: destroy_attachment_does_not_exist: "Logo does not exist" enterprise_promo_image: destroy_attachment_does_not_exist: "Promo image does not exist" + orders: + failed_to_update: "Failed to update order" checkout: already_ordered: cart: "cart" message_html: "You have an order for this order cycle already. Check the %{cart} to see the items you ordered before. You can also cancel items as long as the order cycle is open." + failed: "The checkout failed. Please let us know so that we can process your order." shops: hubs: show_closed_shops: "Show closed shops" @@ -1255,6 +1284,7 @@ en_US: saving_credit_card: Saving credit card... card_has_been_removed: "Your card has been removed (number: %{number})" card_could_not_be_removed: Sorry, the card could not be removed + invalid_credit_card: "Invalid credit card" ie_warning_headline: "Your browser is out of date :-(" ie_warning_text: "For the best Open Food Network experience, we strongly recommend upgrading your browser:" ie_warning_chrome: Download Chrome @@ -1456,6 +1486,7 @@ en_US: shopping_oc_closed_description: "Please wait until the next cycle opens (or contact us directly to see if we can accept any late orders)" shopping_oc_last_closed: "The last cycle closed %{distance_of_time} ago" shopping_oc_next_open: "The next cycle opens in %{distance_of_time}" + shopping_oc_select: "Select..." shopping_tabs_home: "Home" shopping_tabs_shop: "Shop" shopping_tabs_about: "About" @@ -1829,6 +1860,7 @@ en_US: headline: "Finished!" thanks: "Thanks for filling out the details for %{enterprise}." login: "You can change or update your enterprise at any stage by logging into Open Food Network and going to Admin." + action: "Go to Enterprise Dashboard" back: "Back" continue: "Continue" action_or: "OR" @@ -1914,6 +1946,7 @@ en_US: tax_category: "Tax Category" calculator: "Calculator" calculator_values: "Calculator values" + calculator_settings_warning: "If you are changing the calculator type, you must save first before you can edit the calculator settings" flat_percent_per_item: "Flat Percent (per item)" flat_rate_per_item: "Flat Rate (per item)" flat_rate_per_order: "Flat Rate (per order)" @@ -2244,6 +2277,7 @@ en_US: enterprise_register_success_notice: "Congratulations! Registration for %{enterprise} is complete!" enterprise_bulk_update_success_notice: "Enterprises updated successfully" enterprise_bulk_update_error: 'Update failed' + enterprise_shop_show_error: "The shop you are looking for doesn't exist or is inactive on OFN. Please check other shops." order_cycles_create_notice: 'Your order cycle has been created.' order_cycles_update_notice: 'Your order cycle has been updated.' order_cycles_bulk_update_notice: 'Order cycles have been updated.' @@ -2395,6 +2429,12 @@ en_US: severity: Severity description: Description resolve: Resolve + exchange_products: + load_more_variants: "Load More Variants" + load_all_variants: "Load All Variants" + select_all_variants: "Select All %{total_number_of_variants} Variants" + variants_loaded: "%{num_of_variants_loaded} of %{total_number_of_variants} Variants Loaded" + loading_variants: "Loading Variants" tag_rules: shipping_method_tagged_top: "Shipping methods tagged" shipping_method_tagged_bottom: "are:" @@ -2477,6 +2517,7 @@ en_US: customer_placeholder: "customer@example.org" valid_email_error: "Please enter a valid email address" subscriptions: + error_saving: "Error saving subscription" new: please_select_a_shop: "Please select a shop" insufficient_stock: "Insufficient stock available, only %{on_hand} remaining" @@ -2552,6 +2593,76 @@ en_US: signup_or_login: "Start By signing up (or logging in)" have_an_account: "Already have an account?" action_login: "Log in now." + inflections: + each: + one: "each" + other: "each" + bunch: + one: "bunch" + other: "bunches" + pack: + one: "pack" + other: "packs" + box: + one: "box" + other: "boxes" + bottle: + one: "bottle" + other: "bottles" + jar: + one: "jar" + other: "jars" + head: + one: "head" + other: "heads" + bag: + one: "bag" + other: "bags" + loaf: + one: "loaf" + other: "loaves" + single: + one: "single" + other: "singles" + tub: + one: "tub" + other: "tubs" + packet: + one: "packet" + other: "packets" + item: + one: "item" + other: "items" + dozen: + one: "dozen" + other: "dozens" + unit: + one: "unit" + other: "units" + serve: + one: "serve" + other: "serves" + tray: + one: "tray" + other: "trays" + piece: + one: "piece" + other: "pieces" + pot: + one: "pot" + other: "pots" + bundle: + one: "bundle" + other: "bundles" + flask: + one: "flask" + other: "flasks" + basket: + one: "basket" + other: "baskets" + sack: + one: "sack" + other: "sacks" producers: signup: start_free_profile: "Start with a free profile, and expand when you're ready!" @@ -2869,7 +2980,6 @@ en_US: tax_invoice: "TAX INVOICE" code: "Code" from: "From" - to: "To" form: distribution_fields: title: "Distribution" diff --git a/config/locales/en_ZA.yml b/config/locales/en_ZA.yml index 8db0c3d44f..95a6626d62 100644 --- a/config/locales/en_ZA.yml +++ b/config/locales/en_ZA.yml @@ -2870,7 +2870,6 @@ en_ZA: tax_invoice: "TAX INVOICE" code: "Code" from: "From" - to: "To" form: distribution_fields: title: "Distribution" diff --git a/config/locales/es.yml b/config/locales/es.yml index ca35023d67..d7cfd0eef1 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -2877,7 +2877,6 @@ es: tax_invoice: "FACTURA DE IMPUESTOS" code: "Código" from: "De" - to: "A" form: distribution_fields: title: "Distribución" diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 695d8126b1..105a648466 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -3049,7 +3049,6 @@ fr: tax_invoice: "FACTURE" code: "Code" from: "De" - to: "A" form: distribution_fields: title: "Distribution" diff --git a/config/locales/fr_BE.yml b/config/locales/fr_BE.yml index b780477f15..cebb76a300 100644 --- a/config/locales/fr_BE.yml +++ b/config/locales/fr_BE.yml @@ -2949,7 +2949,6 @@ fr_BE: tax_invoice: "FACTURE" code: "Code" from: "De" - to: "A" form: distribution_fields: title: "Distribution" diff --git a/config/locales/fr_CA.yml b/config/locales/fr_CA.yml index eed14f17d6..c9c76894c2 100644 --- a/config/locales/fr_CA.yml +++ b/config/locales/fr_CA.yml @@ -3029,7 +3029,6 @@ fr_CA: tax_invoice: "FACTURE" code: "Code" from: "De" - to: "A" form: distribution_fields: title: "Distribution" diff --git a/config/locales/it.yml b/config/locales/it.yml index cb576b0cb1..a0f7731518 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -1394,7 +1394,7 @@ it: order_delivery_address: Indirizzo di consegna order_delivery_time: Tempo di consegna order_special_instructions: "Tue note:" - order_pickup_time: Pronto per la raccolta + order_pickup_time: Pronto per il ritiro order_pickup_instructions: Istruzioni per la raccolta order_produce: Produrre order_total_price: Totale @@ -1463,7 +1463,7 @@ it: email_shipping_delivery_time: "Consegna il" email_shipping_delivery_address: "Indirizzo di consegna" email_shipping_collection_details: Dettagli della raccolta - email_shipping_collection_time: "Pronto per la raccolta:" + email_shipping_collection_time: "Pronto per il ritiro:" email_shipping_collection_instructions: "Istruzioni per la raccolta:" email_special_instructions: "Tue note:" email_signup_greeting: Ciao! @@ -3023,7 +3023,6 @@ it: tax_invoice: "FATTURA DELLE TASSE" code: "Codice" from: "Da" - to: "A" form: distribution_fields: title: "Distribuzione" diff --git a/config/locales/nb.yml b/config/locales/nb.yml index d5849fe3ab..e7d33d52a0 100644 --- a/config/locales/nb.yml +++ b/config/locales/nb.yml @@ -3018,7 +3018,6 @@ nb: tax_invoice: "AVGIFTSFAKTURA" code: "Kode" from: "Fra" - to: "Til" form: distribution_fields: title: "Distribusjon" diff --git a/config/locales/nl_BE.yml b/config/locales/nl_BE.yml index 7ac172e701..1ea003122e 100644 --- a/config/locales/nl_BE.yml +++ b/config/locales/nl_BE.yml @@ -2865,7 +2865,6 @@ nl_BE: tax_invoice: "FACTUUR" code: "Code" from: "Van" - to: "tot" form: distribution_fields: title: "Distributie" diff --git a/config/locales/pt.yml b/config/locales/pt.yml index 40e8f28f91..b9035d7f10 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -2811,7 +2811,6 @@ pt: tax_invoice: "FACTURA FISCAL" code: "Código" from: "De" - to: "Para" form: distribution_fields: title: "Distribuição" diff --git a/config/locales/pt_BR.yml b/config/locales/pt_BR.yml index 7bb6d77399..04c707defb 100644 --- a/config/locales/pt_BR.yml +++ b/config/locales/pt_BR.yml @@ -1345,16 +1345,16 @@ pt_BR: learn_cta: "Inspire-se" connect_body: "Procure em nossa lista de produtores, distribuidores e cooperativas para encontrar um comércio justo perto de você. Registre seu negócio ou organização na Open Food Brasil para que os consumidores possam te encontrar. Junte-se à comunidade para trocar experiências e resolver problemas." connect_cta: "Explore" - system_headline: "Compras - é assim que funciona:" + system_headline: "Como comprar:" system_step1: "1. Busca" system_step1_text: "Dentre os diversos mercados independentes, escolha o de sua preferência. Procure por região, tipo de alimentos, alimentos locais e sazonais e escolha o método de recebimento que pode ser receber em casa ou retirar no local. " system_step2: "2. Compra" - system_step2_text: "Conheça as histórias por trás da sua comida e as pessoas que as produzem! Transforme suas compras com alimentos locais acessíveis." + system_step2_text: "Conheça as pessoas e as histórias por trás da sua comida. Transforme sua relação com o alimento!" system_step3: "3. Retirada / Entrega" - system_step3_text: "Você escolhe a melhor forma de receber o seu alimentar, aguarde pela sua entrega, ou visite seus produtores ou centrais para uma conexão mais pessoal com seus alimentos." + system_step3_text: "Escolha a forma de recebimento do seu pedido: você pode receber em casa ou retirar no local, assim pode encontrar e conhecer as pessoas envolvidas e estreitar sua relação com as/os agricultoras/es ou as/os mediadoras/es da circulação de produtos. " cta_headline: "Compras que fazem do mundo um lugar melhor." cta_label: "Estou pronto" - stats_headline: "Estamos criando um novo sistema alimentar" + stats_headline: "Estamos trabalhando para relocalizar os sistemas agroalimentares" stats_producers: "produtores de alimentos" stats_shops: "lojas de alimentos" stats_shoppers: "consumidores de alimentos" diff --git a/config/locales/sv.yml b/config/locales/sv.yml index f129bac722..ae32c53f2e 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -1972,7 +1972,6 @@ sv: tax_invoice: "FAKTURA" code: "Kod" from: "Från" - to: "Till" form: distribution_fields: title: "Distribution" diff --git a/config/locales/tr.yml b/config/locales/tr.yml index bd09046967..8260ea4341 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -3010,7 +3010,6 @@ tr: tax_invoice: "VERGİ FATURASI" code: "Kod" from: "İtibaren" - to: "Şu vakte kadar" form: distribution_fields: title: "Dağıtım" From 2e98b0b5c1a1554cbc5c2b2f2450ad1fa6441ff5 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Fri, 27 Mar 2020 01:40:20 +1100 Subject: [PATCH 077/130] Updating translations for config/locales/en_GB.yml --- config/locales/en_GB.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/locales/en_GB.yml b/config/locales/en_GB.yml index 7f12eb59bf..b1f9ba7ec3 100644 --- a/config/locales/en_GB.yml +++ b/config/locales/en_GB.yml @@ -3025,6 +3025,8 @@ en_GB: tax_invoice: "TAX INVOICE" code: "Code" from: "From" + to: "Bill to" + shipping: "Shipping" form: distribution_fields: title: "Distribution" From 14fd9a121ea020f7f07edd8a194e6dea7b2dd996 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Fri, 27 Mar 2020 11:00:34 +0000 Subject: [PATCH 078/130] Make versions.custom_data text so it can take longer lists --- ...20200327105910_change_versions_custom_data_to_text.rb | 9 +++++++++ db/schema.rb | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20200327105910_change_versions_custom_data_to_text.rb diff --git a/db/migrate/20200327105910_change_versions_custom_data_to_text.rb b/db/migrate/20200327105910_change_versions_custom_data_to_text.rb new file mode 100644 index 0000000000..0a995a85f4 --- /dev/null +++ b/db/migrate/20200327105910_change_versions_custom_data_to_text.rb @@ -0,0 +1,9 @@ +class ChangeVersionsCustomDataToText < ActiveRecord::Migration + def up + change_column :versions, :custom_data, :text + end + + def down + change_column :versions, :custom_data, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index e064256c53..40badc11bf 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20200209163549) do +ActiveRecord::Schema.define(:version => 20200327105910) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -1202,7 +1202,7 @@ ActiveRecord::Schema.define(:version => 20200209163549) do t.string "whodunnit" t.text "object" t.datetime "created_at" - t.string "custom_data" + t.text "custom_data" end add_index "versions", ["item_type", "item_id"], :name => "index_versions_on_item_type_and_item_id" From 83b90f3167002e60bdd5202e67c46dc62c301952 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 27 Mar 2020 13:31:39 +0100 Subject: [PATCH 079/130] Add spec variant override test to VariantsStockLevels --- spec/services/variants_stock_levels_spec.rb | 26 +++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/spec/services/variants_stock_levels_spec.rb b/spec/services/variants_stock_levels_spec.rb index f4a77d7018..8aaa6503bf 100644 --- a/spec/services/variants_stock_levels_spec.rb +++ b/spec/services/variants_stock_levels_spec.rb @@ -48,4 +48,30 @@ describe VariantsStockLevels do ) end end + + describe "when the variant has an override" do + let!(:distributor) { create(:distributor_enterprise) } + let(:supplier) { variant_in_the_order.product.supplier } + let!(:order_cycle) { + create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], + variants: [variant_in_the_order]) + } + let!(:variant_override) { + create(:variant_override, hub: distributor, + variant: variant_in_the_order, + count_on_hand: 404) + } + + before do + order.order_cycle = order_cycle + order.distributor = distributor + order.save + end + + xit "returns the on_hand value of the override" do + expect(variant_stock_levels.call(order, [variant_in_the_order.id])).to eq( + variant_in_the_order.id => { quantity: 2, max_quantity: 3, on_hand: 404, on_demand: false } + ) + end + end end From fbfe663ebcc1991571507e7e43311e6bce2dc271 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 27 Mar 2020 13:38:57 +0100 Subject: [PATCH 080/130] Add variant scoping to VariantStockLevels --- app/services/variants_stock_levels.rb | 20 +++++++++++++++++--- spec/services/variants_stock_levels_spec.rb | 6 +++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/app/services/variants_stock_levels.rb b/app/services/variants_stock_levels.rb index 69dbc49dee..2d0628f5cb 100644 --- a/app/services/variants_stock_levels.rb +++ b/app/services/variants_stock_levels.rb @@ -1,6 +1,8 @@ # Report the stock levels of: # - all variants in the order # - all requested variant ids +require 'open_food_network/scope_variant_to_hub' + class VariantsStockLevels def call(order, requested_variant_ids) variant_stock_levels = variant_stock_levels(order.line_items) @@ -34,12 +36,24 @@ class VariantsStockLevels def variant_stock_levels(line_items) Hash[ line_items.map do |line_item| - [line_item.variant.id, + variant = scoped_variant(line_item) + + [variant.id, { quantity: line_item.quantity, max_quantity: line_item.max_quantity, - on_hand: line_item.variant.on_hand, - on_demand: line_item.variant.on_demand }] + on_hand: variant.on_hand, + on_demand: variant.on_demand }] end ] end + + def scoped_variant(line_item) + distributor = line_item.order.distributor + variant = line_item.variant + + return variant if distributor.blank? + + OpenFoodNetwork::ScopeVariantToHub.new(distributor).scope(variant) + variant + end end diff --git a/spec/services/variants_stock_levels_spec.rb b/spec/services/variants_stock_levels_spec.rb index 8aaa6503bf..ecd0656bc0 100644 --- a/spec/services/variants_stock_levels_spec.rb +++ b/spec/services/variants_stock_levels_spec.rb @@ -59,7 +59,7 @@ describe VariantsStockLevels do let!(:variant_override) { create(:variant_override, hub: distributor, variant: variant_in_the_order, - count_on_hand: 404) + count_on_hand: 200) } before do @@ -68,9 +68,9 @@ describe VariantsStockLevels do order.save end - xit "returns the on_hand value of the override" do + it "returns the on_hand value of the override" do expect(variant_stock_levels.call(order, [variant_in_the_order.id])).to eq( - variant_in_the_order.id => { quantity: 2, max_quantity: 3, on_hand: 404, on_demand: false } + variant_in_the_order.id => { quantity: 2, max_quantity: 3, on_hand: 200, on_demand: false } ) end end From 857cacb74bf30b532dfa315f530cfc2c2024f5e0 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 27 Mar 2020 14:03:17 +0100 Subject: [PATCH 081/130] Add test for additional case where variant is not in the order --- spec/services/variants_stock_levels_spec.rb | 29 ++++++++++++++++----- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/spec/services/variants_stock_levels_spec.rb b/spec/services/variants_stock_levels_spec.rb index ecd0656bc0..dc90cf3f9a 100644 --- a/spec/services/variants_stock_levels_spec.rb +++ b/spec/services/variants_stock_levels_spec.rb @@ -54,13 +54,18 @@ describe VariantsStockLevels do let(:supplier) { variant_in_the_order.product.supplier } let!(:order_cycle) { create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor], - variants: [variant_in_the_order]) + variants: [variant_in_the_order, variant_not_in_the_order]) } - let!(:variant_override) { + let!(:variant_override_in_order) { create(:variant_override, hub: distributor, variant: variant_in_the_order, count_on_hand: 200) } + let!(:variant_override_not_in_order) { + create(:variant_override, hub: distributor, + variant: variant_not_in_the_order, + count_on_hand: 404) + } before do order.order_cycle = order_cycle @@ -68,10 +73,22 @@ describe VariantsStockLevels do order.save end - it "returns the on_hand value of the override" do - expect(variant_stock_levels.call(order, [variant_in_the_order.id])).to eq( - variant_in_the_order.id => { quantity: 2, max_quantity: 3, on_hand: 200, on_demand: false } - ) + context "when the variant is in the order" do + it "returns the on_hand value of the override" do + expect(variant_stock_levels.call(order, [variant_in_the_order.id])).to eq( + variant_in_the_order.id => { quantity: 2, max_quantity: 3, on_hand: 200, on_demand: false } + ) + end + end + + context "with variants that are not in the order" do + xit "returns the on_hand value of the override" do + variant_ids = [variant_in_the_order.id, variant_not_in_the_order.id] + expect(variant_stock_levels.call(order, variant_ids)).to eq( + variant_in_the_order.id => { quantity: 2, max_quantity: 3, on_hand: 200, on_demand: false }, + variant_not_in_the_order.id => { quantity: 0, max_quantity: 0, on_hand: 404, on_demand: false } + ) + end end end end From 7d33a237d07b7167f9972fdc97b4b6a4064d81b2 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 27 Mar 2020 14:15:15 +0100 Subject: [PATCH 082/130] Add scoping to VariantsStockLevels when variant is not in the order --- app/services/variants_stock_levels.rb | 9 +++------ spec/services/variants_stock_levels_spec.rb | 16 +++++++++++----- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/app/services/variants_stock_levels.rb b/app/services/variants_stock_levels.rb index 2d0628f5cb..44e2611f04 100644 --- a/app/services/variants_stock_levels.rb +++ b/app/services/variants_stock_levels.rb @@ -12,7 +12,7 @@ class VariantsStockLevels order_variant_ids = variant_stock_levels.keys missing_variant_ids = requested_variant_ids - order_variant_ids missing_variant_ids.each do |variant_id| - variant = Spree::Variant.find(variant_id) + variant = scoped_variant(order.distributor, Spree::Variant.find(variant_id)) variant_stock_levels[variant_id] = { quantity: 0, max_quantity: 0, on_hand: variant.on_hand, on_demand: variant.on_demand } end @@ -36,7 +36,7 @@ class VariantsStockLevels def variant_stock_levels(line_items) Hash[ line_items.map do |line_item| - variant = scoped_variant(line_item) + variant = scoped_variant(line_item.order.distributor, line_item.variant) [variant.id, { quantity: line_item.quantity, @@ -47,10 +47,7 @@ class VariantsStockLevels ] end - def scoped_variant(line_item) - distributor = line_item.order.distributor - variant = line_item.variant - + def scoped_variant(distributor, variant) return variant if distributor.blank? OpenFoodNetwork::ScopeVariantToHub.new(distributor).scope(variant) diff --git a/spec/services/variants_stock_levels_spec.rb b/spec/services/variants_stock_levels_spec.rb index dc90cf3f9a..5977845665 100644 --- a/spec/services/variants_stock_levels_spec.rb +++ b/spec/services/variants_stock_levels_spec.rb @@ -64,7 +64,7 @@ describe VariantsStockLevels do let!(:variant_override_not_in_order) { create(:variant_override, hub: distributor, variant: variant_not_in_the_order, - count_on_hand: 404) + count_on_hand: 201) } before do @@ -76,17 +76,23 @@ describe VariantsStockLevels do context "when the variant is in the order" do it "returns the on_hand value of the override" do expect(variant_stock_levels.call(order, [variant_in_the_order.id])).to eq( - variant_in_the_order.id => { quantity: 2, max_quantity: 3, on_hand: 200, on_demand: false } + variant_in_the_order.id => { + quantity: 2, max_quantity: 3, on_hand: 200, on_demand: false + } ) end end context "with variants that are not in the order" do - xit "returns the on_hand value of the override" do + it "returns the on_hand value of the override" do variant_ids = [variant_in_the_order.id, variant_not_in_the_order.id] expect(variant_stock_levels.call(order, variant_ids)).to eq( - variant_in_the_order.id => { quantity: 2, max_quantity: 3, on_hand: 200, on_demand: false }, - variant_not_in_the_order.id => { quantity: 0, max_quantity: 0, on_hand: 404, on_demand: false } + variant_in_the_order.id => { + quantity: 2, max_quantity: 3, on_hand: 200, on_demand: false + }, + variant_not_in_the_order.id => { + quantity: 0, max_quantity: 0, on_hand: 201, on_demand: false + } ) end end From 71f00f9283f9801754397ee49a01ed51eae303f0 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Fri, 27 Mar 2020 14:23:48 +0100 Subject: [PATCH 083/130] Remove comment warning about this issue --- app/services/variants_stock_levels.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/services/variants_stock_levels.rb b/app/services/variants_stock_levels.rb index 44e2611f04..2865d44e39 100644 --- a/app/services/variants_stock_levels.rb +++ b/app/services/variants_stock_levels.rb @@ -7,8 +7,6 @@ class VariantsStockLevels def call(order, requested_variant_ids) variant_stock_levels = variant_stock_levels(order.line_items) - # Variants are not scoped here and so the stock levels reported are incorrect - # See cart_controller_spec for more details and #3222 order_variant_ids = variant_stock_levels.keys missing_variant_ids = requested_variant_ids - order_variant_ids missing_variant_ids.each do |variant_id| From ca78e9d0e28e8cb554fc15c4238555ea1014a802 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Sat, 28 Mar 2020 04:08:17 +1100 Subject: [PATCH 084/130] Updating translations for config/locales/fr.yml --- config/locales/fr.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 105a648466..4a8ddce9f6 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -3049,6 +3049,8 @@ fr: tax_invoice: "FACTURE" code: "Code" from: "De" + to: "Facturer à" + shipping: "Envoi" form: distribution_fields: title: "Distribution" From 6a57aa3b299fc2cac45bda6281e7c8ab3347378c Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Sat, 28 Mar 2020 04:08:31 +1100 Subject: [PATCH 085/130] Updating translations for config/locales/en_FR.yml --- config/locales/en_FR.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/locales/en_FR.yml b/config/locales/en_FR.yml index f34151ad66..c6b685d4cb 100644 --- a/config/locales/en_FR.yml +++ b/config/locales/en_FR.yml @@ -3019,6 +3019,8 @@ en_FR: tax_invoice: "TAX INVOICE" code: "Code" from: "From" + to: "Bill to" + shipping: "Shipping" form: distribution_fields: title: "Distribution" From b76a6d15a352c0075b60ea310efa50690a9d4a91 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sat, 28 Mar 2020 11:16:25 +0100 Subject: [PATCH 086/130] Improve date field query triggers Don't submit the request if the user is part-way through typing something in the date field and the date is (currently) invalid; this results in the date ranges being broken and triggering a query for *all* results (with no date range). --- .../line_items/controllers/line_items_controller.js.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index 6107760307..dd0d48bd8f 100644 --- a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -29,6 +29,8 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, formatted_start_date = moment($scope.startDate).format() formatted_end_date = moment($scope.endDate).add(1,'day').format() + return unless moment(formatted_start_date).isValid() and moment(formatted_start_date).isValid() + RequestMonitor.load $scope.orders = Orders.index( "q[state_not_eq]": "canceled", "q[completed_at_not_null]": "true", From d568b45d4af7a76def20a2515f95c0baeadbb9f4 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sat, 28 Mar 2020 11:18:07 +0100 Subject: [PATCH 087/130] Limit spamming the server with lots of requests This uses "debounce" to add a little waiting time whilst the user is part-way through selecting dates (or manually typing in the date filter) so we don't make too many unnecessary requests. --- app/views/spree/admin/orders/bulk_management.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 1fc72e9a9b..4a29e2bcc3 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -20,12 +20,12 @@ %label{ :for => 'start_date_filter' } = t("admin.start_date") %br - %input{ :class => "two columns alpha", :type => "text", :id => 'start_date_filter', 'ng-model' => 'startDate', 'datepicker' => "startDate", 'confirm-change' => "confirmRefresh()", 'ng-change' => 'refreshData()' } + %input{ :class => "two columns alpha", :type => "text", :id => 'start_date_filter', 'ng-model' => 'startDate', 'datepicker' => "startDate", 'confirm-change' => "confirmRefresh()", 'ng-change' => 'refreshData()', 'ng-model-options' => '{ debounce: 1000 }' } .date_filter{ :class => "two columns" } %label{ :for => 'end_date_filter' } = t("admin.end_date") %br - %input{ :class => "two columns alpha", :type => "text", :id => 'end_date_filter', 'ng-model' => 'endDate', 'datepicker' => "endDate", 'confirm-change' => "confirmRefresh()", 'ng-change' => 'refreshData()' } + %input{ :class => "two columns alpha", :type => "text", :id => 'end_date_filter', 'ng-model' => 'endDate', 'datepicker' => "endDate", 'confirm-change' => "confirmRefresh()", 'ng-change' => 'refreshData()', 'ng-model-options' => '{ debounce: 1000 }' } .one.column   .filter_select{ :class => "three columns" } %label{ :for => 'supplier_filter' } From 7c7f9551d6b1770072e0524762b276c6d2c663bc Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sat, 28 Mar 2020 15:17:31 +0100 Subject: [PATCH 088/130] Fix bug in order cycle select dropdown The date filters were being filled with the string: "Invalid date" because momentjs was not able to parse the input --- .../line_items/controllers/line_items_controller.js.coffee | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index dd0d48bd8f..e1fe4d650e 100644 --- a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -23,8 +23,11 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $scope.refreshData = -> unless !$scope.orderCycleFilter? || $scope.orderCycleFilter == 0 - $scope.startDate = moment(OrderCycles.byID[$scope.orderCycleFilter].orders_open_at).format('YYYY-MM-DD') - $scope.endDate = moment(OrderCycles.byID[$scope.orderCycleFilter].orders_close_at).startOf('day').format('YYYY-MM-DD') + start_date = OrderCycles.byID[$scope.orderCycleFilter].orders_open_at + end_date = OrderCycles.byID[$scope.orderCycleFilter].orders_close_at + format = "YYYY-MM-DD HH:mm:ss Z" + $scope.startDate = moment(start_date, format).format('YYYY-MM-DD') + $scope.endDate = moment(end_date, format).startOf('day').format('YYYY-MM-DD') formatted_start_date = moment($scope.startDate).format() formatted_end_date = moment($scope.endDate).add(1,'day').format() From 6817231f2983615032dc4855c142650c39e976c2 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Sun, 29 Mar 2020 06:42:36 +1100 Subject: [PATCH 089/130] Updating translations for config/locales/fr.yml --- config/locales/fr.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 4a8ddce9f6..789f986949 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -863,6 +863,12 @@ fr: cancel: "Annuler" back_to_list: "Retour à la liste" outgoing: + outgoing: "Produits sortants (mis en vente par/via un ou plusieurs hubs)" + distributor: "Distributeur" + products: "Produits" + tags: "Tags" + delivery_details: "Détails de livraison" + fees: "Commissions" previous: "Précédent" save: "Sauvegarder" save_and_back_to_list: "Sauvegarder et revenir à la liste" From 9af0a39305a17c25ffb52b3cdecab627feadc6a8 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Sun, 29 Mar 2020 06:43:01 +1100 Subject: [PATCH 090/130] Updating translations for config/locales/en_FR.yml --- config/locales/en_FR.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/locales/en_FR.yml b/config/locales/en_FR.yml index c6b685d4cb..d5c1dc2400 100644 --- a/config/locales/en_FR.yml +++ b/config/locales/en_FR.yml @@ -861,6 +861,12 @@ en_FR: cancel: "Cancel" back_to_list: "Back To List" outgoing: + outgoing: "Outgoing" + distributor: "Distributor" + products: "Products" + tags: "Tags" + delivery_details: "Delivery Details" + fees: "Fees" previous: "Previous" save: "Save" save_and_back_to_list: "Save and Back to List" From 375b4648dc82ac8af5e9628337ad9c17a19c7456 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sat, 28 Mar 2020 15:20:12 +0100 Subject: [PATCH 091/130] Add pagination to Bulk Order Management --- .../line_items_controller.js.coffee | 47 ++++++++++++------- .../resources/line_item_resource.js.coffee | 1 - .../resources/services/line_items.js.coffee | 20 ++++++-- .../admin/bulk_line_items_controller.rb | 40 +++++++++++++--- .../admin/orders/bulk_management.html.haml | 11 +++-- .../admin/bulk_line_items_controller_spec.rb | 16 +++---- .../admin/bulk_order_management_spec.rb | 3 +- 7 files changed, 96 insertions(+), 42 deletions(-) diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index e1fe4d650e..164fbdc33d 100644 --- a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -1,7 +1,8 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $http, $q, StatusMessage, Columns, SortOptions, Dereferencer, Orders, LineItems, Enterprises, OrderCycles, VariantUnitManager, RequestMonitor) -> $scope.initialized = false $scope.RequestMonitor = RequestMonitor - $scope.filteredLineItems = [] + $scope.pagination = LineItems.pagination + $scope.line_items = LineItems.all $scope.confirmDelete = true $scope.startDate = moment().startOf('day').subtract(7, 'days').format('YYYY-MM-DD') $scope.endDate = moment().startOf('day').format('YYYY-MM-DD') @@ -11,18 +12,24 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $scope.sharedResource = false $scope.columns = Columns.columns $scope.sorting = SortOptions + $scope.page = 1 + $scope.per_page = 50 $scope.confirmRefresh = -> LineItems.allSaved() || confirm(t("unsaved_changes_warning")) $scope.resetSelectFilters = -> - $scope.distributorFilter = 0 - $scope.supplierFilter = 0 - $scope.orderCycleFilter = 0 + $scope.distributorFilter = '' + $scope.supplierFilter = '' + $scope.orderCycleFilter = '' $scope.quickSearch = "" + $scope.changePage = (newPage) -> + $scope.page = newPage + $scope.refreshData() + $scope.refreshData = -> - unless !$scope.orderCycleFilter? || $scope.orderCycleFilter == 0 + unless !$scope.orderCycleFilter? || $scope.orderCycleFilter == '' start_date = OrderCycles.byID[$scope.orderCycleFilter].orders_open_at end_date = OrderCycles.byID[$scope.orderCycleFilter].orders_close_at format = "YYYY-MM-DD HH:mm:ss Z" @@ -37,15 +44,22 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, RequestMonitor.load $scope.orders = Orders.index( "q[state_not_eq]": "canceled", "q[completed_at_not_null]": "true", + "q[distributor_id_eq]": $scope.distributorFilter, + "q[order_cycle_id_eq]": $scope.orderCycleFilter, "q[completed_at_gteq]": formatted_start_date, "q[completed_at_lt]": formatted_end_date ) - RequestMonitor.load $scope.lineItems = LineItems.index( - "q[order][state_not_eq]": "canceled", - "q[order][completed_at_not_null]": "true", - "q[order][completed_at_gteq]": formatted_start_date, - "q[order][completed_at_lt]": formatted_end_date + RequestMonitor.load LineItems.index( + "q[order_state_not_eq]": "canceled", + "q[order_completed_at_not_null]": "true", + "q[order_distributor_id_eq]": $scope.distributorFilter, + "q[variant_product_supplier_id_eq]": $scope.supplierFilter, + "q[order_order_cycle_id_eq]": $scope.orderCycleFilter, + "q[order_completed_at_gteq]": formatted_start_date, + "q[order_completed_at_lt]": formatted_end_date, + page: $scope.page, + per_page: $scope.per_page ) unless $scope.initialized @@ -53,11 +67,11 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, RequestMonitor.load $scope.orderCycles = OrderCycles.index(ams_prefix: "basic", as: "distributor", "q[orders_close_at_gt]": "#{moment().subtract(90,'days').format()}") RequestMonitor.load $scope.suppliers = Enterprises.index(action: "visible", ams_prefix: "basic", "q[is_primary_producer_eq]": "true") - RequestMonitor.load $q.all([$scope.orders.$promise, $scope.distributors.$promise, $scope.orderCycles.$promise, $scope.suppliers.$promise, $scope.lineItems.$promise]).then -> + RequestMonitor.load $q.all([$scope.orders.$promise, $scope.distributors.$promise, $scope.orderCycles.$promise, $scope.suppliers.$promise, $scope.line_items.$promise]).then -> Dereferencer.dereferenceAttr $scope.orders, "distributor", Enterprises.byID Dereferencer.dereferenceAttr $scope.orders, "order_cycle", OrderCycles.byID - Dereferencer.dereferenceAttr $scope.lineItems, "supplier", Enterprises.byID - Dereferencer.dereferenceAttr $scope.lineItems, "order", Orders.byID + Dereferencer.dereferenceAttr $scope.line_items, "supplier", Enterprises.byID + Dereferencer.dereferenceAttr $scope.line_items, "order", Orders.byID $scope.bulk_order_form.$setPristine() StatusMessage.clear() unless $scope.initialized @@ -82,13 +96,12 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $scope.deleteLineItem = (lineItem) -> if ($scope.confirmDelete && confirm(t "are_you_sure")) || !$scope.confirmDelete - LineItems.delete lineItem, => - $scope.lineItems.splice $scope.lineItems.indexOf(lineItem), 1 + LineItems.delete lineItem - $scope.deleteLineItems = (lineItems) -> + $scope.deleteLineItems = (lineItemsToDelete) -> existingState = $scope.confirmDelete $scope.confirmDelete = false - $scope.deleteLineItem lineItem for lineItem in lineItems when lineItem.checked + $scope.deleteLineItem lineItem for lineItem in lineItemsToDelete when lineItem.checked $scope.confirmDelete = existingState $scope.allBoxesChecked = -> diff --git a/app/assets/javascripts/admin/resources/resources/line_item_resource.js.coffee b/app/assets/javascripts/admin/resources/resources/line_item_resource.js.coffee index 2ec5703c82..28b92bac21 100644 --- a/app/assets/javascripts/admin/resources/resources/line_item_resource.js.coffee +++ b/app/assets/javascripts/admin/resources/resources/line_item_resource.js.coffee @@ -2,7 +2,6 @@ angular.module("admin.resources").factory 'LineItemResource', ($resource) -> $resource('/admin/bulk_line_items/:id.json', {}, { 'index': method: 'GET' - isArray: true 'update': method: 'PUT' transformRequest: (data, headersGetter) => diff --git a/app/assets/javascripts/admin/resources/services/line_items.js.coffee b/app/assets/javascripts/admin/resources/services/line_items.js.coffee index 7c96caecd8..fb92e51890 100644 --- a/app/assets/javascripts/admin/resources/services/line_items.js.coffee +++ b/app/assets/javascripts/admin/resources/services/line_items.js.coffee @@ -1,20 +1,27 @@ angular.module("admin.resources").factory 'LineItems', ($q, LineItemResource) -> new class LineItems + all: [] byID: {} pristineByID: {} + pagination: {} index: (params={}, callback=null) -> - LineItemResource.index params, (data) => + request = LineItemResource.index params, (data) => @load(data) (callback || angular.noop)(data) + @all.$promise = request.$promise + @all resetData: -> + @all.length = 0 @byID = {} @pristineByID = {} - load: (lineItems) -> + load: (data) -> + angular.extend(@pagination, data.pagination) @resetData() - for lineItem in lineItems + for lineItem in data.line_items + @all.push lineItem @byID[lineItem.id] = lineItem @pristineByID[lineItem.id] = angular.copy(lineItem) @@ -25,8 +32,9 @@ angular.module("admin.resources").factory 'LineItems', ($q, LineItemResource) -> save: (lineItem) -> deferred = $q.defer() + lineItemResource = new LineItemResource(lineItem) lineItem.errors = {} - lineItem.$update({id: lineItem.id}) + lineItemResource.$update({id: lineItem.id}) .then( (data) => @pristineByID[lineItem.id] = angular.copy(lineItem) deferred.resolve(data) @@ -54,8 +62,10 @@ angular.module("admin.resources").factory 'LineItems', ($q, LineItemResource) -> delete: (lineItem, callback=null) -> deferred = $q.defer() - lineItem.$delete({id: lineItem.id}) + lineItemResource = new LineItemResource(lineItem) + lineItemResource.$delete({id: lineItem.id}) .then( (data) => + @all.splice(@all.indexOf(lineItem),1) delete @byID[lineItem.id] delete @pristineByID[lineItem.id] (callback || angular.noop)(data) diff --git a/app/controllers/admin/bulk_line_items_controller.rb b/app/controllers/admin/bulk_line_items_controller.rb index 86a2f539e2..aa2f28457f 100644 --- a/app/controllers/admin/bulk_line_items_controller.rb +++ b/app/controllers/admin/bulk_line_items_controller.rb @@ -4,18 +4,17 @@ module Admin # def index order_params = params[:q].andand.delete :order + orders = order_permissions.editable_orders.ransack(order_params).result - order_permissions = ::Permissions::Order.new(spree_current_user) - orders = order_permissions. - editable_orders.ransack(order_params).result - - line_items = order_permissions. + @line_items = order_permissions. editable_line_items.where(order_id: orders). includes(variant: { option_values: :option_type }). ransack(params[:q]).result. reorder('spree_line_items.order_id ASC, spree_line_items.id ASC') - render_as_json line_items + @line_items = @line_items.page(page).per(params[:per_page]) if using_pagination? + + render json: { line_items: serialized_line_items, pagination: pagination_data } end # PUT /admin/bulk_line_items/:id.json @@ -65,6 +64,12 @@ module Admin Api::Admin::LineItemSerializer end + def serialized_line_items + ActiveModel::ArraySerializer.new( + @line_items, each_serializer: serializer(nil) + ) + end + def authorize_update! authorize! :update, order authorize! :read, order @@ -73,5 +78,28 @@ module Admin def order @line_item.order end + + def order_permissions + ::Permissions::Order.new(spree_current_user) + end + + def using_pagination? + params[:per_page] + end + + def pagination_data + return unless using_pagination? + + { + results: @line_items.total_count, + pages: @line_items.num_pages, + page: page.to_i, + per_page: params[:per_page].to_i + } + end + + def page + params[:page] || 1 + end end end diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 4a29e2bcc3..f690e5c799 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -31,12 +31,12 @@ %label{ :for => 'supplier_filter' } = t("admin.producer") %br - %input#supplier_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'suppliers', blank: "{ id: 0, name: '#{t(:all)}' }", ng: { model: 'supplierFilter' } } + %input#supplier_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'suppliers', blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'supplierFilter', change: 'refreshData()' } } .filter_select{ :class => "three columns" } %label{ :for => 'distributor_filter' } = t("admin.shop") %br - %input#distributor_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'distributors', blank: "{ id: 0, name: '#{t(:all)}' }", ng: { model: 'distributorFilter' } } + %input#distributor_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'distributors', blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'distributorFilter', change: 'refreshData()' } } .filter_select{ :class => "three columns" } %label{ :for => 'order_cycle_filter' } = t("admin.order_cycle") @@ -94,7 +94,7 @@ %hr.divider.sixteen.columns.alpha.omega - .controls.sixteen.columns.alpha.omega{ ng: { hide: 'RequestMonitor.loading || lineItems.length == 0' } } + .controls.sixteen.columns.alpha.omega{ ng: { hide: 'RequestMonitor.loading || line_items.length == 0' } } %div.three.columns.alpha %input.fullwidth{ :type => "text", :id => 'quick_search', 'ng-model' => 'quickSearch', :placeholder => 'Quick Search' } = render 'admin/shared/bulk_actions_dropdown' @@ -157,7 +157,7 @@ = t("admin.orders.bulk_management.ask") %input{ :type => 'checkbox', 'ng-model' => "confirmDelete" } - %tr.line_item{ ng: { repeat: "line_item in filteredLineItems = ( lineItems | filter:quickSearch | selectFilter:supplierFilter:distributorFilter:orderCycleFilter | variantFilter:selectedUnitsProduct:selectedUnitsVariant:sharedResource | orderBy:sorting.predicate:sorting.reverse )", 'class-even' => "'even'", 'class-odd' => "'odd'", attr: { id: "li_{{line_item.id}}" } } } + %tr.line_item{ ng: { repeat: "line_item in filteredLineItems = ( line_items | filter:quickSearch | variantFilter:selectedUnitsProduct:selectedUnitsVariant:sharedResource | orderBy:sorting.predicate:sorting.reverse )", 'class-even' => "'even'", 'class-odd' => "'odd'", attr: { id: "li_{{line_item.id}}" } } } %td.bulk %input{ :type => "checkbox", :name => 'bulk', 'ng-model' => 'line_item.checked', 'ignore-dirty' => true } %td.order_no{ 'ng-show' => 'columns.order_no.visible' } {{ line_item.order.number }} @@ -184,3 +184,6 @@ %a{ ng: { href: "/admin/orders/{{line_item.order.number}}/edit" }, :class => "edit-order icon-edit no-text", 'confirm-link-click' => 'confirmRefresh()' } %td.actions %a{ 'ng-click' => "deleteLineItem(line_item)", :class => "delete-line-item icon-trash no-text" } + + .row + = render partial: 'admin/shared/angular_pagination' diff --git a/spec/controllers/admin/bulk_line_items_controller_spec.rb b/spec/controllers/admin/bulk_line_items_controller_spec.rb index 6b7fe5bff4..244bfaf2b7 100644 --- a/spec/controllers/admin/bulk_line_items_controller_spec.rb +++ b/spec/controllers/admin/bulk_line_items_controller_spec.rb @@ -36,22 +36,22 @@ describe Admin::BulkLineItemsController, type: :controller do end it "retrieves a list of line_items with appropriate attributes, including line items with appropriate attributes" do - keys = json_response.first.keys.map(&:to_sym) + keys = json_response['line_items'].first.keys.map(&:to_sym) expect(line_item_attributes.all?{ |attr| keys.include? attr }).to eq(true) end it "sorts line_items in ascending id line_item" do - ids = json_response.map{ |line_item| line_item['id'] } + ids = json_response['line_items'].map{ |line_item| line_item['id'] } expect(ids[0]).to be < ids[1] expect(ids[1]).to be < ids[2] end it "formats final_weight_volume as a float" do - expect(json_response.map{ |line_item| line_item['final_weight_volume'] }.all?{ |fwv| fwv.is_a?(Float) }).to eq(true) + expect(json_response['line_items'].map{ |line_item| line_item['final_weight_volume'] }.all?{ |fwv| fwv.is_a?(Float) }).to eq(true) end it "returns distributor object with id key" do - expect(json_response.map{ |line_item| line_item['supplier'] }.all?{ |d| d.key?('id') }).to eq(true) + expect(json_response['line_items'].map{ |line_item| line_item['supplier'] }.all?{ |d| d.key?('id') }).to eq(true) end end @@ -61,7 +61,7 @@ describe Admin::BulkLineItemsController, type: :controller do end it "retrives a list of line items which match the criteria" do - expect(json_response.map{ |line_item| line_item['id'] }).to eq [line_item2.id, line_item3.id] + expect(json_response['line_items'].map{ |line_item| line_item['id'] }).to eq [line_item2.id, line_item3.id] end end @@ -71,7 +71,7 @@ describe Admin::BulkLineItemsController, type: :controller do end it "retrives a list of line items whose orders match the criteria" do - expect(json_response.map{ |line_item| line_item['id'] }).to eq [line_item2.id, line_item3.id, line_item4.id] + expect(json_response['line_items'].map{ |line_item| line_item['id'] }).to eq [line_item2.id, line_item3.id, line_item4.id] end end end @@ -106,7 +106,7 @@ describe Admin::BulkLineItemsController, type: :controller do end it "retrieves a list of line_items" do - keys = json_response.first.keys.map(&:to_sym) + keys = json_response['line_items'].first.keys.map(&:to_sym) expect(line_item_attributes.all?{ |attr| keys.include? attr }).to eq(true) end end @@ -118,7 +118,7 @@ describe Admin::BulkLineItemsController, type: :controller do end it "retrieves a list of line_items" do - keys = json_response.first.keys.map(&:to_sym) + keys = json_response['line_items'].first.keys.map(&:to_sym) expect(line_item_attributes.all?{ |attr| keys.include? attr }).to eq(true) end end diff --git a/spec/features/admin/bulk_order_management_spec.rb b/spec/features/admin/bulk_order_management_spec.rb index ffe17400f4..a92afed022 100644 --- a/spec/features/admin/bulk_order_management_spec.rb +++ b/spec/features/admin/bulk_order_management_spec.rb @@ -494,7 +494,7 @@ feature ' it "shows a dialog and ignores changes when confirm dialog is accepted" do page.driver.accept_modal :confirm, text: "Unsaved changes exist and will be lost if you continue." do - fill_in "start_date_filter", with: (Date.current - 9).strftime("%F %T") + fill_in "start_date_filter", with: (Date.current - 9).strftime('%Y-%m-%d') end expect(page).to have_no_selector "#save-bar" within("tr#li_#{li2.id} td.quantity") do @@ -577,6 +577,7 @@ feature ' find("div#bulk-actions-dropdown").click find("div#bulk-actions-dropdown div.menu_item", text: "Delete Selected" ).click expect(page).to have_no_selector "tr#li_#{li1.id}" + expect(page).to have_selector "#quick_search" fill_in "quick_search", with: '' wait_until { request_monitor_finished 'LineItemsCtrl' } expect(page).to have_selector "tr#li_#{li2.id}" From 72f9da3ac4484ab5ad7ca8bb41630bed03fa5529 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sat, 28 Mar 2020 15:44:27 +0100 Subject: [PATCH 092/130] Fix regex validation and HTML "type" settings not allowing decimals for final_weight_volume `final_weight_volume` can be a decimal, and often *is* in production data. Not regarding them as valid was breaking the form submission in various cases --- app/views/spree/admin/orders/bulk_management.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index f690e5c799..01d3c70216 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -175,7 +175,7 @@ %span.error{ ng: { bind: 'line_item.errors.quantity' } } %td.max{ 'ng-show' => 'columns.max.visible' } {{ line_item.max_quantity }} %td.final_weight_volume{ 'ng-show' => 'columns.final_weight_volume.visible' } - %input.show-dirty{ :type => 'number', :name => 'final_weight_volume', :id => 'final_weight_volume', ng: { model: "line_item.final_weight_volume", readonly: "unitValueLessThanZero(line_item)", change: "weightAdjustedPrice(line_item)", required: "true", class: '{"update-error": line_item.errors.final_weight_volume}' }, min: 0, 'ng-pattern' => '/[1-9]+/' } + %input.show-dirty{ type: 'number', step: 'any', :name => 'final_weight_volume', :id => 'final_weight_volume', ng: { model: "line_item.final_weight_volume", readonly: "unitValueLessThanZero(line_item)", change: "weightAdjustedPrice(line_item)", required: "true", class: '{"update-error": line_item.errors.final_weight_volume}' }, min: 0, 'ng-pattern' => '/[0-9]*[.]?[0-9]+/' } %span.error{ ng: { bind: 'line_item.errors.final_weight_volume' } } %td.price{ 'ng-show' => 'columns.price.visible' } %input.show-dirty{ :type => 'text', :name => 'price', :id => 'price', :ng => { value: 'line_item.price * line_item.quantity | currency:""', readonly: "true", class: '{"update-error": line_item.errors.price}' } } From a07281910ba249223bdd34b397de3898617e4c92 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sat, 28 Mar 2020 22:52:42 +0100 Subject: [PATCH 093/130] Fix dropdown placeholders --- app/views/spree/admin/orders/bulk_management.html.haml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 01d3c70216..44d137e7d8 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -31,17 +31,17 @@ %label{ :for => 'supplier_filter' } = t("admin.producer") %br - %input#supplier_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'suppliers', blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'supplierFilter', change: 'refreshData()' } } + %input#supplier_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'suppliers', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'supplierFilter', change: 'refreshData()' } } .filter_select{ :class => "three columns" } %label{ :for => 'distributor_filter' } = t("admin.shop") %br - %input#distributor_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'distributors', blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'distributorFilter', change: 'refreshData()' } } + %input#distributor_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'distributors', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'distributorFilter', change: 'refreshData()' } } .filter_select{ :class => "three columns" } %label{ :for => 'order_cycle_filter' } = t("admin.order_cycle") %br - %input#order_cycle_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'orderCycles', blank: "{ id: 0, name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'orderCycleFilter', change: 'refreshData()' } } + %input#order_cycle_filter.ofn-select2.fullwidth{ type: 'number', 'min-search' => 5, data: 'orderCycles', placeholder: "#{t(:all)}", blank: "{ id: '', name: '#{t(:all)}' }", on: { selecting: "confirmRefresh" }, ng: { model: 'orderCycleFilter', change: 'refreshData()' } } .filter_clear{ :class => "two columns omega" } %label{ :for => 'clear_all_filters' } %br From 5c4a2c2790013e572f54ac56749ff7413dd43f53 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sat, 28 Mar 2020 21:38:58 +0100 Subject: [PATCH 094/130] Update javascript specs --- .../line_items_controller_spec.js.coffee | 60 +++++-------------- .../services/line_items_spec.js.coffee | 19 +++--- 2 files changed, 26 insertions(+), 53 deletions(-) diff --git a/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee b/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee index 3b04893b34..a1808b5264 100644 --- a/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee @@ -13,23 +13,23 @@ describe "LineItemsCtrl", -> compare: (actual, expected) -> { pass: angular.equals(actual, expected) } - beforeEach inject(($controller, $rootScope, $httpBackend, _$timeout_, _VariantUnitManager_, _Enterprises_, _Orders_, _LineItems_, _OrderCycles_) -> + beforeEach inject(($controller, $rootScope, $httpBackend, _$timeout_, _VariantUnitManager_, _Enterprises_, _Orders_, _OrderCycles_) -> scope = $rootScope.$new() ctrl = $controller $timeout = _$timeout_ httpBackend = $httpBackend Enterprises = _Enterprises_ Orders = _Orders_ - LineItems = _LineItems_ OrderCycles = _OrderCycles_ VariantUnitManager = _VariantUnitManager_ - momentMock = jasmine.createSpyObj('moment', ['format', 'startOf', 'endOf', 'subtract', 'add']) + momentMock = jasmine.createSpyObj('moment', ['format', 'startOf', 'endOf', 'subtract', 'add', 'isValid']) spyOn(window,"moment").and.returnValue momentMock momentMock.startOf.and.returnValue momentMock momentMock.endOf.and.returnValue momentMock momentMock.subtract.and.returnValue momentMock momentMock.add.and.returnValue momentMock momentMock.format.and.returnValue "SomeDate" + momentMock.isValid.and.returnValue true supplier = { id: 1, name: "Supplier" } distributor = { id: 5, name: "Distributor" } @@ -37,8 +37,11 @@ describe "LineItemsCtrl", -> order = { id: 9, order_cycle: { id: 4 }, distributor: { id: 5 }, number: "R123456" } lineItem = { id: 7, quantity: 3, order: { id: 9 }, supplier: { id: 1 } } - httpBackend.expectGET("/api/orders.json?q%5Bcompleted_at_gteq%5D=SomeDate&q%5Bcompleted_at_lt%5D=SomeDate&q%5Bcompleted_at_not_null%5D=true&q%5Bstate_not_eq%5D=canceled").respond {orders: [order], pagination: {page: 1, pages: 1, results: 1}} - httpBackend.expectGET("/admin/bulk_line_items.json?q%5Border%5D%5Bcompleted_at_gteq%5D=SomeDate&q%5Border%5D%5Bcompleted_at_lt%5D=SomeDate&q%5Border%5D%5Bcompleted_at_not_null%5D=true&q%5Border%5D%5Bstate_not_eq%5D=canceled").respond [lineItem] + LineItems = + index: jasmine.createSpy('index').and.returnValue(lineItem) + all: [lineItem] + + httpBackend.expectGET("/api/orders.json?q%5Bcompleted_at_gteq%5D=SomeDate&q%5Bcompleted_at_lt%5D=SomeDate&q%5Bcompleted_at_not_null%5D=true&q%5Bdistributor_id_eq%5D=&q%5Border_cycle_id_eq%5D=&q%5Bstate_not_eq%5D=canceled").respond {orders: [order], pagination: {page: 1, pages: 1, results: 1}} httpBackend.expectGET("/admin/enterprises/visible.json?ams_prefix=basic&q%5Bsells_in%5D%5B%5D=own&q%5Bsells_in%5D%5B%5D=any").respond [distributor] httpBackend.expectGET("/admin/order_cycles.json?ams_prefix=basic&as=distributor&q%5Borders_close_at_gt%5D=SomeDate").respond [orderCycle] httpBackend.expectGET("/admin/enterprises/visible.json?ams_prefix=basic&q%5Bis_primary_producer_eq%5D=true").respond [supplier] @@ -52,12 +55,6 @@ describe "LineItemsCtrl", -> it "the RequestMonitor will have a state of loading", -> expect(scope.RequestMonitor.loading).toBe true - it "will not have reset the select filters", -> - expect(scope.distributorFilter).toBeUndefined() - expect(scope.supplierFilter).toBeUndefined() - expect(scope.orderCycleFilter).toBeUndefined() - expect(scope.quickSearch).toBeUndefined() - it "will not have reset the form state to pristine", -> expect(scope.bulk_order_form.$setPristine.calls.count()).toBe 0 @@ -83,49 +80,20 @@ describe "LineItemsCtrl", -> expect(scope.orders).toDeepEqual [ { id: 9, order_cycle: orderCycle, distributor: distributor, number: "R123456" } ] it "gets line_items, with dereferenced orders and suppliers", -> - expect(scope.lineItems).toDeepEqual [ { id: 7, quantity: 3, order: scope.orders[0], supplier: supplier } ] + expect(scope.line_items).toDeepEqual [ { id: 7, quantity: 3, order: scope.orders[0], supplier: supplier } ] it "the RequestMonitor will have a state of loaded", -> expect(scope.RequestMonitor.loading).toBe false it "resets the select filters", -> - expect(scope.distributorFilter).toBe 0 - expect(scope.supplierFilter).toBe 0 - expect(scope.orderCycleFilter).toBe 0 + expect(scope.distributorFilter).toBe '' + expect(scope.supplierFilter).toBe '' + expect(scope.orderCycleFilter).toBe '' expect(scope.quickSearch).toBe = "" it "resets the form state to pristine", -> expect(scope.bulk_order_form.$setPristine.calls.count()).toBe 1 - describe "deleting a line item", -> - order = line_item1 = line_item2 = null - - beforeEach inject((LineItemResource) -> - spyOn(window,"confirm").and.returnValue true - order = { number: "R12345678" } - line_item1 = new LineItemResource({ id: 1, order: order }) - line_item2 = new LineItemResource({ id: 2, order: order }) - scope.lineItems= [ line_item1, line_item2 ] - ) - - describe "where the request is successful", -> - beforeEach -> - httpBackend.expectDELETE("/admin/bulk_line_items/1.json").respond "nothing" - scope.deleteLineItem line_item1 - httpBackend.flush() - - it "removes the deleted item from the line_items array", -> - expect(scope.lineItems).toEqual [line_item2] - - describe "where the request is unsuccessful", -> - beforeEach -> - httpBackend.expectDELETE("/admin/bulk_line_items/1.json").respond 404, "NO CONTENT" - scope.deleteLineItem line_item1 - httpBackend.flush() - - it "does not remove line_item from the line_items array", -> - expect(scope.lineItems).toEqual [line_item1, line_item2] - describe "deleting 'checked' line items", -> line_item1 = line_item2 = line_item3 = line_item4 = null @@ -134,11 +102,11 @@ describe "LineItemsCtrl", -> line_item2 = { name: "line item 2", checked: true } line_item3 = { name: "line item 3", checked: false } line_item4 = { name: "line item 4", checked: true } - scope.lineItems = [ line_item1, line_item2, line_item3, line_item4 ] + scope.line_items = [ line_item1, line_item2, line_item3, line_item4 ] it "calls deletedLineItem for each 'checked' line item", -> spyOn(scope, "deleteLineItem") - scope.deleteLineItems(scope.lineItems) + scope.deleteLineItems(scope.line_items) expect(scope.deleteLineItem).toHaveBeenCalledWith(line_item2) expect(scope.deleteLineItem).toHaveBeenCalledWith(line_item4) expect(scope.deleteLineItem).not.toHaveBeenCalledWith(line_item1) diff --git a/spec/javascripts/unit/admin/line_items/services/line_items_spec.js.coffee b/spec/javascripts/unit/admin/line_items/services/line_items_spec.js.coffee index 7367e06d88..08456b2748 100644 --- a/spec/javascripts/unit/admin/line_items/services/line_items_spec.js.coffee +++ b/spec/javascripts/unit/admin/line_items/services/line_items_spec.js.coffee @@ -1,5 +1,5 @@ describe "LineItems service", -> - LineItems = LineItemResource = lineItems = $httpBackend = null + LineItems = LineItemResource = lineItems = $httpBackend = $rootScope = $timeout = null beforeEach -> module 'admin.lineItems' @@ -15,24 +15,27 @@ describe "LineItems service", -> $httpBackend = _$httpBackend_ describe "#index", -> - result = response = null + result = response = line_item = null beforeEach -> - response = [{ id: 5, name: 'LineItem 1'}] + line_item = { id: 5, name: 'LineItem 1'} + response = { line_items: [line_item] } $httpBackend.expectGET('/admin/bulk_line_items.json').respond 200, response result = LineItems.index() $httpBackend.flush() it "stores returned data in @byID, with ids as keys", -> # LineItemResource returns instances of Resource rather than raw objects - expect(LineItems.byID).toDeepEqual { 5: response[0] } + expect(LineItems.byID).toDeepEqual { 5: response['line_items'][0] } it "stores returned data in @pristineByID, with ids as keys", -> - expect(LineItems.pristineByID).toDeepEqual { 5: response[0] } + expect(LineItems.pristineByID).toDeepEqual { 5: response['line_items'][0] } + + it "stores returned data in @all, as an array", -> + expect(LineItems.all).toDeepEqual [line_item] it "returns an array of line items", -> - expect(result).toDeepEqual response - + expect(result).toDeepEqual [line_item] describe "#save", -> describe "success", -> @@ -115,6 +118,7 @@ describe "LineItems service", -> lineItem = new LineItemResource({ id: 15, order: { number: '12345678'} }) LineItems.pristineByID[15] = lineItem LineItems.byID[15] = lineItem + LineItems.all = [lineItem] $httpBackend.expectDELETE('/admin/bulk_line_items/15.json').respond 200, { id: 15, name: 'LineItem 1'} LineItems.delete(lineItem, callback).then( -> resolved = true).catch( -> rejected = true) $httpBackend.flush() @@ -122,6 +126,7 @@ describe "LineItems service", -> it "updates the pristine copy of the lineItem", -> expect(LineItems.pristineByID[15]).toBeUndefined() expect(LineItems.byID[15]).toBeUndefined() + expect(LineItems.all).toEqual([]) it "runs the callback", -> expect(callback).toHaveBeenCalled() From fe58121c7f34d84234dc6364e779c9dc1e7c84be Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sat, 28 Mar 2020 23:18:30 +0100 Subject: [PATCH 095/130] Fix clear filters button --- .../controllers/line_items_controller.js.coffee | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index 164fbdc33d..09dfe592c9 100644 --- a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -12,6 +12,10 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $scope.sharedResource = false $scope.columns = Columns.columns $scope.sorting = SortOptions + $scope.distributorFilter = '' + $scope.supplierFilter = '' + $scope.orderCycleFilter = '' + $scope.quickSearch = '' $scope.page = 1 $scope.per_page = 50 @@ -22,7 +26,9 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $scope.distributorFilter = '' $scope.supplierFilter = '' $scope.orderCycleFilter = '' - $scope.quickSearch = "" + $scope.quickSearch = '' + $scope.page = 1 + $scope.refreshData() $scope.changePage = (newPage) -> $scope.page = newPage @@ -76,8 +82,6 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, StatusMessage.clear() unless $scope.initialized $scope.initialized = true - $timeout -> - $scope.resetSelectFilters() $scope.$watch 'bulk_order_form.$dirty', (newVal, oldVal) -> if newVal == true From aba1b5b67a79553c1f9a3e04fa0c03f9ea955dbc Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sun, 29 Mar 2020 12:53:22 +0200 Subject: [PATCH 096/130] Add pagination specs and refactor --- .../admin/bulk_line_items_controller_spec.rb | 41 ++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/spec/controllers/admin/bulk_line_items_controller_spec.rb b/spec/controllers/admin/bulk_line_items_controller_spec.rb index 244bfaf2b7..941b44eb0c 100644 --- a/spec/controllers/admin/bulk_line_items_controller_spec.rb +++ b/spec/controllers/admin/bulk_line_items_controller_spec.rb @@ -41,9 +41,8 @@ describe Admin::BulkLineItemsController, type: :controller do end it "sorts line_items in ascending id line_item" do - ids = json_response['line_items'].map{ |line_item| line_item['id'] } - expect(ids[0]).to be < ids[1] - expect(ids[1]).to be < ids[2] + expect(line_item_ids[0]).to be < line_item_ids[1] + expect(line_item_ids[1]).to be < line_item_ids[2] end it "formats final_weight_volume as a float" do @@ -61,7 +60,7 @@ describe Admin::BulkLineItemsController, type: :controller do end it "retrives a list of line items which match the criteria" do - expect(json_response['line_items'].map{ |line_item| line_item['id'] }).to eq [line_item2.id, line_item3.id] + expect(line_item_ids).to eq [line_item2.id, line_item3.id] end end @@ -71,7 +70,7 @@ describe Admin::BulkLineItemsController, type: :controller do end it "retrives a list of line items whose orders match the criteria" do - expect(json_response['line_items'].map{ |line_item| line_item['id'] }).to eq [line_item2.id, line_item3.id, line_item4.id] + expect(line_item_ids).to eq [line_item2.id, line_item3.id, line_item4.id] end end end @@ -123,6 +122,32 @@ describe Admin::BulkLineItemsController, type: :controller do end end end + + context "paginating" do + before do + allow(controller).to receive_messages spree_current_user: create(:admin_user) + end + + context "with pagination args" do + it "returns paginated results" do + spree_get :index, { page: 1, per_page: 2 }, format: :json + + expect(line_item_ids).to eq [line_item1.id, line_item2.id] + expect(json_response['pagination']).to eq( + { 'page' => 1, 'per_page' => 2, 'pages' => 2, 'results' => 4 } + ) + end + + it "returns paginated results for a second page" do + spree_get :index, { page: 2, per_page: 2 }, format: :json + + expect(line_item_ids).to eq [line_item3.id, line_item4.id] + expect(json_response['pagination']).to eq( + { 'page' => 2, 'per_page' => 2, 'pages' => 2, 'results' => 4 } + ) + end + end + end end describe '#update' do @@ -259,4 +284,10 @@ describe Admin::BulkLineItemsController, type: :controller do end end end + + private + + def line_item_ids + json_response['line_items'].map{ |line_item| line_item['id'] } + end end From 95963c5732eca68a628ca92c2f49320eec186584 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Sun, 29 Mar 2020 19:32:43 +0200 Subject: [PATCH 097/130] Refactor Angular line_items_controller --- .../line_items_controller.js.coffee | 57 ++++++++++++------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index 09dfe592c9..6de9360c25 100644 --- a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -12,22 +12,21 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $scope.sharedResource = false $scope.columns = Columns.columns $scope.sorting = SortOptions - $scope.distributorFilter = '' - $scope.supplierFilter = '' - $scope.orderCycleFilter = '' - $scope.quickSearch = '' $scope.page = 1 $scope.per_page = 50 $scope.confirmRefresh = -> LineItems.allSaved() || confirm(t("unsaved_changes_warning")) - $scope.resetSelectFilters = -> + $scope.resetFilters = -> $scope.distributorFilter = '' $scope.supplierFilter = '' $scope.orderCycleFilter = '' $scope.quickSearch = '' $scope.page = 1 + + $scope.resetSelectFilters = -> + $scope.resetFilters() $scope.refreshData() $scope.changePage = (newPage) -> @@ -36,43 +35,57 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $scope.refreshData = -> unless !$scope.orderCycleFilter? || $scope.orderCycleFilter == '' - start_date = OrderCycles.byID[$scope.orderCycleFilter].orders_open_at - end_date = OrderCycles.byID[$scope.orderCycleFilter].orders_close_at - format = "YYYY-MM-DD HH:mm:ss Z" - $scope.startDate = moment(start_date, format).format('YYYY-MM-DD') - $scope.endDate = moment(end_date, format).startOf('day').format('YYYY-MM-DD') + $scope.setOrderCycleDateRange() - formatted_start_date = moment($scope.startDate).format() - formatted_end_date = moment($scope.endDate).add(1,'day').format() + $scope.formattedStartDate = moment($scope.startDate).format() + $scope.formattedEndDate = moment($scope.endDate).add(1,'day').format() - return unless moment(formatted_start_date).isValid() and moment(formatted_start_date).isValid() + return unless moment($scope.formattedStartDate).isValid() and moment($scope.formattedEndDate).isValid() + $scope.loadOrders() + $scope.loadLineItems() + + unless $scope.initialized + $scope.loadAssociatedData() + + $scope.dereferenceLoadedData() + + $scope.setOrderCycleDateRange = -> + start_date = OrderCycles.byID[$scope.orderCycleFilter].orders_open_at + end_date = OrderCycles.byID[$scope.orderCycleFilter].orders_close_at + format = "YYYY-MM-DD HH:mm:ss Z" + $scope.startDate = moment(start_date, format).format('YYYY-MM-DD') + $scope.endDate = moment(end_date, format).startOf('day').format('YYYY-MM-DD') + + $scope.loadOrders = -> RequestMonitor.load $scope.orders = Orders.index( "q[state_not_eq]": "canceled", "q[completed_at_not_null]": "true", "q[distributor_id_eq]": $scope.distributorFilter, "q[order_cycle_id_eq]": $scope.orderCycleFilter, - "q[completed_at_gteq]": formatted_start_date, - "q[completed_at_lt]": formatted_end_date + "q[completed_at_gteq]": $scope.formattedStartDate, + "q[completed_at_lt]": $scope.formattedEndDate ) + $scope.loadLineItems = -> RequestMonitor.load LineItems.index( "q[order_state_not_eq]": "canceled", "q[order_completed_at_not_null]": "true", "q[order_distributor_id_eq]": $scope.distributorFilter, "q[variant_product_supplier_id_eq]": $scope.supplierFilter, "q[order_order_cycle_id_eq]": $scope.orderCycleFilter, - "q[order_completed_at_gteq]": formatted_start_date, - "q[order_completed_at_lt]": formatted_end_date, + "q[order_completed_at_gteq]": $scope.formattedStartDate, + "q[order_completed_at_lt]": $scope.formattedEndDate, page: $scope.page, per_page: $scope.per_page ) - unless $scope.initialized - RequestMonitor.load $scope.distributors = Enterprises.index(action: "visible", ams_prefix: "basic", "q[sells_in][]": ["own", "any"]) - RequestMonitor.load $scope.orderCycles = OrderCycles.index(ams_prefix: "basic", as: "distributor", "q[orders_close_at_gt]": "#{moment().subtract(90,'days').format()}") - RequestMonitor.load $scope.suppliers = Enterprises.index(action: "visible", ams_prefix: "basic", "q[is_primary_producer_eq]": "true") + $scope.loadAssociatedData = -> + RequestMonitor.load $scope.distributors = Enterprises.index(action: "visible", ams_prefix: "basic", "q[sells_in][]": ["own", "any"]) + RequestMonitor.load $scope.orderCycles = OrderCycles.index(ams_prefix: "basic", as: "distributor", "q[orders_close_at_gt]": "#{moment().subtract(90,'days').format()}") + RequestMonitor.load $scope.suppliers = Enterprises.index(action: "visible", ams_prefix: "basic", "q[is_primary_producer_eq]": "true") + $scope.dereferenceLoadedData = -> RequestMonitor.load $q.all([$scope.orders.$promise, $scope.distributors.$promise, $scope.orderCycles.$promise, $scope.suppliers.$promise, $scope.line_items.$promise]).then -> Dereferencer.dereferenceAttr $scope.orders, "distributor", Enterprises.byID Dereferencer.dereferenceAttr $scope.orders, "order_cycle", OrderCycles.byID @@ -80,6 +93,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, Dereferencer.dereferenceAttr $scope.line_items, "order", Orders.byID $scope.bulk_order_form.$setPristine() StatusMessage.clear() + unless $scope.initialized $scope.initialized = true @@ -176,4 +190,5 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, lineItem.final_weight_volume = LineItems.pristineByID[lineItem.id].final_weight_volume * lineItem.quantity / LineItems.pristineByID[lineItem.id].quantity $scope.weightAdjustedPrice(lineItem) + $scope.resetFilters() $scope.refreshData() From b6da0e20928f5261ef21f74e886eeea3f7175f08 Mon Sep 17 00:00:00 2001 From: Eduardo Date: Sun, 29 Mar 2020 20:32:55 -0300 Subject: [PATCH 098/130] remove @payment_method explict variable from the view --- .../spree/admin/payment_methods/_provider_settings.html.haml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/spree/admin/payment_methods/_provider_settings.html.haml b/app/views/spree/admin/payment_methods/_provider_settings.html.haml index 38f2ff06e9..cbf24630b2 100644 --- a/app/views/spree/admin/payment_methods/_provider_settings.html.haml +++ b/app/views/spree/admin/payment_methods/_provider_settings.html.haml @@ -1,4 +1,3 @@ -= @payment_method - case @payment_method - when Spree::Gateway::StripeConnect = render 'stripe_connect' From f7c047b798f474c647bb41426435817497249199 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Mon, 30 Mar 2020 20:33:27 +0200 Subject: [PATCH 099/130] Memoize ScopeVariantToHub to avoid fetching the hub's overrides each time --- app/services/variants_stock_levels.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/services/variants_stock_levels.rb b/app/services/variants_stock_levels.rb index 2865d44e39..7a36658f31 100644 --- a/app/services/variants_stock_levels.rb +++ b/app/services/variants_stock_levels.rb @@ -48,7 +48,11 @@ class VariantsStockLevels def scoped_variant(distributor, variant) return variant if distributor.blank? - OpenFoodNetwork::ScopeVariantToHub.new(distributor).scope(variant) + scoper(distributor).scope(variant) variant end + + def scoper(distributor) + @scoper ||= OpenFoodNetwork::ScopeVariantToHub.new(distributor) + end end From 09c8819e5a573f87c42863bbc8ee24860ff5c305 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Mon, 30 Mar 2020 20:36:45 +0200 Subject: [PATCH 100/130] Remove unnecessary Bugsnag calls The Bugsnag notification was just here to see if this was dead code. It's not. --- app/services/variants_stock_levels.rb | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/app/services/variants_stock_levels.rb b/app/services/variants_stock_levels.rb index 7a36658f31..73f6e68278 100644 --- a/app/services/variants_stock_levels.rb +++ b/app/services/variants_stock_levels.rb @@ -14,23 +14,11 @@ class VariantsStockLevels variant_stock_levels[variant_id] = { quantity: 0, max_quantity: 0, on_hand: variant.on_hand, on_demand: variant.on_demand } end - # The code above is most probably dead code, this bugsnag notification will confirm it - notify_bugsnag(order, requested_variant_ids, order_variant_ids) if missing_variant_ids.present? - variant_stock_levels end private - def notify_bugsnag(order, requested_variant_ids, order_variant_ids) - error_msg = "VariantsStockLevels.call with variants in the request that are not in the order" - Bugsnag.notify(RuntimeError.new(error_msg), - requested_variant_ids: requested_variant_ids.as_json, - order_variant_ids: order_variant_ids.as_json, - order: order.as_json, - line_items: order.line_items.as_json) - end - def variant_stock_levels(line_items) Hash[ line_items.map do |line_item| From 28473c9087cead38a9ce46e0b2e312c38d3b3941 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Tue, 31 Mar 2020 15:48:52 +1100 Subject: [PATCH 101/130] Add Bugsnag JS logging --- app/views/layouts/_bugsnag.html.haml | 3 +++ app/views/layouts/darkswarm.html.haml | 1 + config/application.yml.example | 3 +++ 3 files changed, 7 insertions(+) create mode 100644 app/views/layouts/_bugsnag.html.haml diff --git a/app/views/layouts/_bugsnag.html.haml b/app/views/layouts/_bugsnag.html.haml new file mode 100644 index 0000000000..20c4304c84 --- /dev/null +++ b/app/views/layouts/_bugsnag.html.haml @@ -0,0 +1,3 @@ +%script{src: "//d2wy8f7a9ursnm.cloudfront.net/v6/bugsnag.min.js"} +:javascript + window.bugsnagClient = bugsnag('#{ENV["BUGSNAG_JS_KEY"]}'); diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml index 317f40907d..210c89b0f9 100644 --- a/app/views/layouts/darkswarm.html.haml +++ b/app/views/layouts/darkswarm.html.haml @@ -37,6 +37,7 @@ #footer %loading + = render "layouts/bugsnag" if ENV["BUGSNAG_JS_KEY"].present? %script{:src => "https://js.stripe.com/v3/", :type => "text/javascript"} %script{src: "//maps.googleapis.com/maps/api/js?libraries=places,geometry#{ ENV['GOOGLE_MAPS_API_KEY'] ? '&key=' + ENV['GOOGLE_MAPS_API_KEY'] : ''} "} = javascript_include_tag "darkswarm/all" diff --git a/config/application.yml.example b/config/application.yml.example index 28d271e111..0cbb866279 100644 --- a/config/application.yml.example +++ b/config/application.yml.example @@ -37,6 +37,9 @@ SMTP_PASSWORD: 'f00d' # MAILS_FROM: hello@example.com # MAIL_BCC: manager@example.com +# Javascript error reporting via Bugsnag. +#BUGSNAG_JS_KEY: "" + # SingleSignOn login for Discourse # # DISCOURSE_SSO_SECRET should be a random string. It must be the same as provided to your Discourse instance. From 03e229da08dc46113c25b2ac08ebeb0041039c1e Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 31 Mar 2020 12:25:42 +0200 Subject: [PATCH 102/130] Add bugsnag js script to admin layout above all.js --- app/views/spree/admin/shared/_head.html.haml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/spree/admin/shared/_head.html.haml b/app/views/spree/admin/shared/_head.html.haml index 375830063b..5f5a9d2ec7 100644 --- a/app/views/spree/admin/shared/_head.html.haml +++ b/app/views/spree/admin/shared/_head.html.haml @@ -12,6 +12,7 @@ %link{:href => "//fonts.googleapis.com/css?family=Open+Sans:400italic,600italic,400,600&subset=latin,cyrillic,greek,vietnamese", :rel => "stylesheet", :type => "text/css"} = stylesheet_link_tag 'admin/all' += render "layouts/bugsnag" if ENV["BUGSNAG_JS_KEY"].present? = javascript_include_tag 'admin/all' = render "spree/admin/shared/translations" From 30bf9257abdf6a0c4b402fe4307a91efcdd88863 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 31 Mar 2020 12:55:03 +0200 Subject: [PATCH 103/130] Move conditional inside partial and use default key as fallback --- app/views/layouts/_bugsnag.html.haml | 8 +++++--- app/views/layouts/darkswarm.html.haml | 2 +- app/views/spree/admin/shared/_head.html.haml | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/views/layouts/_bugsnag.html.haml b/app/views/layouts/_bugsnag.html.haml index 20c4304c84..a5e9ec32a1 100644 --- a/app/views/layouts/_bugsnag.html.haml +++ b/app/views/layouts/_bugsnag.html.haml @@ -1,3 +1,5 @@ -%script{src: "//d2wy8f7a9ursnm.cloudfront.net/v6/bugsnag.min.js"} -:javascript - window.bugsnagClient = bugsnag('#{ENV["BUGSNAG_JS_KEY"]}'); +- bugsnag_js_key = ENV['BUGSNAG_JS_KEY'] || ENV['BUGSNAG_API_KEY'] +- if bugsnag_js_key.present? + %script{src: "//d2wy8f7a9ursnm.cloudfront.net/v6/bugsnag.min.js"} + :javascript + window.bugsnagClient = bugsnag("#{bugsnag_js_key}"); diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml index 210c89b0f9..afc72caf83 100644 --- a/app/views/layouts/darkswarm.html.haml +++ b/app/views/layouts/darkswarm.html.haml @@ -37,7 +37,7 @@ #footer %loading - = render "layouts/bugsnag" if ENV["BUGSNAG_JS_KEY"].present? + = render "layouts/bugsnag" %script{:src => "https://js.stripe.com/v3/", :type => "text/javascript"} %script{src: "//maps.googleapis.com/maps/api/js?libraries=places,geometry#{ ENV['GOOGLE_MAPS_API_KEY'] ? '&key=' + ENV['GOOGLE_MAPS_API_KEY'] : ''} "} = javascript_include_tag "darkswarm/all" diff --git a/app/views/spree/admin/shared/_head.html.haml b/app/views/spree/admin/shared/_head.html.haml index 5f5a9d2ec7..7446aa2f40 100644 --- a/app/views/spree/admin/shared/_head.html.haml +++ b/app/views/spree/admin/shared/_head.html.haml @@ -12,7 +12,7 @@ %link{:href => "//fonts.googleapis.com/css?family=Open+Sans:400italic,600italic,400,600&subset=latin,cyrillic,greek,vietnamese", :rel => "stylesheet", :type => "text/css"} = stylesheet_link_tag 'admin/all' -= render "layouts/bugsnag" if ENV["BUGSNAG_JS_KEY"].present? += render "layouts/bugsnag" = javascript_include_tag 'admin/all' = render "spree/admin/shared/translations" From e200ece2803caaf3d830dfb95c5d7b501fba38ff Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 31 Mar 2020 12:57:12 +0200 Subject: [PATCH 104/130] Rename partial to bugsnag_js for clarity --- app/views/layouts/{_bugsnag.html.haml => _bugsnag_js.html.haml} | 0 app/views/layouts/darkswarm.html.haml | 2 +- app/views/spree/admin/shared/_head.html.haml | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename app/views/layouts/{_bugsnag.html.haml => _bugsnag_js.html.haml} (100%) diff --git a/app/views/layouts/_bugsnag.html.haml b/app/views/layouts/_bugsnag_js.html.haml similarity index 100% rename from app/views/layouts/_bugsnag.html.haml rename to app/views/layouts/_bugsnag_js.html.haml diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml index afc72caf83..fa42b8f745 100644 --- a/app/views/layouts/darkswarm.html.haml +++ b/app/views/layouts/darkswarm.html.haml @@ -37,7 +37,7 @@ #footer %loading - = render "layouts/bugsnag" + = render "layouts/bugsnag_js" %script{:src => "https://js.stripe.com/v3/", :type => "text/javascript"} %script{src: "//maps.googleapis.com/maps/api/js?libraries=places,geometry#{ ENV['GOOGLE_MAPS_API_KEY'] ? '&key=' + ENV['GOOGLE_MAPS_API_KEY'] : ''} "} = javascript_include_tag "darkswarm/all" diff --git a/app/views/spree/admin/shared/_head.html.haml b/app/views/spree/admin/shared/_head.html.haml index 7446aa2f40..71e3170e59 100644 --- a/app/views/spree/admin/shared/_head.html.haml +++ b/app/views/spree/admin/shared/_head.html.haml @@ -12,7 +12,7 @@ %link{:href => "//fonts.googleapis.com/css?family=Open+Sans:400italic,600italic,400,600&subset=latin,cyrillic,greek,vietnamese", :rel => "stylesheet", :type => "text/css"} = stylesheet_link_tag 'admin/all' -= render "layouts/bugsnag" += render "layouts/bugsnag_js" = javascript_include_tag 'admin/all' = render "spree/admin/shared/translations" From 79d6d7cc9ea46d7435d9183de4bc20a880348c21 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Tue, 31 Mar 2020 22:09:16 +1100 Subject: [PATCH 105/130] Updating translations for config/locales/de_DE.yml --- config/locales/de_DE.yml | 113 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) diff --git a/config/locales/de_DE.yml b/config/locales/de_DE.yml index 589dd9bb33..6d4705dfdb 100644 --- a/config/locales/de_DE.yml +++ b/config/locales/de_DE.yml @@ -50,6 +50,8 @@ de_DE: shipping_method_ids: "Lieferart" payment_method_ids: "Zahlungsarten" errors: + messages: + inclusion: "ist in der Liste nicht enthalten" models: subscription_validator: attributes: @@ -244,6 +246,8 @@ de_DE: notes: Anmerkungen error: Fehler processing_payment: "Bezahlung wird verarbeitet..." + no_pending_payments: "Keine ausstehenden Zahlungen" + invalid_payment_state: "Ungültiger Zahlungsstatus" filter_results: Ergebnisse filtern quantity: Menge pick_up: Abholen @@ -700,6 +704,11 @@ de_DE: enable_subscriptions_false: "deaktiviert" enable_subscriptions_true: "aktiviert" shopfront_message: "Laden-Nachricht" + shopfront_message_placeholder: > + Eine optionale Nachricht, um Kunden willkommen zu heißen und zu erklären, + wie Sie bei Ihnen einkaufen können. Wenn hier Text eingegeben wird, + wird dieser in einem Home-Tab in Ihrem Shop angezeigt, wenn Kunden zum + ersten Mal ihren Shop besuchen. shopfront_message_link_tooltip: "Link einfügen / bearbeiten" shopfront_message_link_prompt: "Bitte geben Sie eine einzufügende URL ein" shopfront_closed_message: "Laden Geschlossen Nachricht" @@ -854,6 +863,12 @@ de_DE: cancel: "Abbrechen" back_to_list: "Zurück zur Liste" outgoing: + outgoing: "Ausgehend" + distributor: "Verteiler" + products: "Produkte" + tags: "Stichwörter" + delivery_details: "Lieferdetails" + fees: "Gebühren" previous: "Bisherige" save: "Speichern" save_and_back_to_list: "Speichern und zurück zur Liste" @@ -1100,10 +1115,13 @@ de_DE: destroy_attachment_does_not_exist: "Logo existiert nicht" enterprise_promo_image: destroy_attachment_does_not_exist: "Webebild existiert nicht" + orders: + failed_to_update: "Bestellung konnte nicht aktualisiert werden" checkout: already_ordered: cart: "Warenkorb" message_html: "Sie haben bereits eine Bestellung für diesen Bestellzyklus. Überprüfen Sie den %{cart}, um die Artikel zu sehen, die Sie zuvor bestellt haben. Sie können Artikel auch stornieren, solange der Bestellzyklus geöffnet ist." + failed: "Die Bestellung ist fehlgeschlagen. Bitte geben Sie uns Bescheid, damit wir Ihre Bestellung trotzdem bearbeiten können." shops: hubs: show_closed_shops: "Geschlossene Läden anzeigen" @@ -1274,6 +1292,7 @@ de_DE: saving_credit_card: Kreditkarte speichern ... card_has_been_removed: "Ihre Karte wurde entfernt (Nummer: %{number})" card_could_not_be_removed: Die Karte konnte nicht entfernt werden + invalid_credit_card: "Ungültige Kreditkarte" ie_warning_headline: "Ihr Browser ist veraltet :-(" ie_warning_text: "Für das beste Open-Food-Network-Erlebnis empfehlen wir dringend, Ihren Browser zu aktualisieren:" ie_warning_chrome: Chrome herunterladen @@ -1361,7 +1380,7 @@ de_DE: checkout_default_bill_address: "Als Standard-Rechnungsadresse speichern" checkout_shipping: Versandinformation checkout_default_ship_address: "Als Standardversandadresse speichern" - checkout_method_free: ?? + checkout_method_free: kostenlos checkout_address_same: Lieferadresse wie Rechnungsadresse? checkout_ready_for: "Bereit am:" checkout_instructions: "Kommentare oder spezielle Anweisungen?" @@ -1475,6 +1494,7 @@ de_DE: shopping_oc_closed_description: "Bitte warten Sie, bis der nächste Zyklus beginnt (oder kontaktieren Sie uns direkt, um zu sehen, ob wir verspätete Bestellungen annehmen können)" shopping_oc_last_closed: "Der letzte Zyklus wurde vor %{distance_of_time} geschlossen" shopping_oc_next_open: "Der nächste Zyklus wird in %{distance_of_time} geöffnet" + shopping_oc_select: "Wählen..." shopping_tabs_home: "Startseite" shopping_tabs_shop: "Laden" shopping_tabs_about: "Über Uns" @@ -1848,6 +1868,7 @@ de_DE: headline: "Fertig!" thanks: "Vielen Dank, dass Sie die Details für %{enterprise} ausgefüllt haben." login: "Sie können Ihr Unternehmen jederzeit ändern oder aktualisieren, indem Sie sich bei Open Food Network anmelden und zum Administrator wechseln." + action: "Gehen Sie zum Enterprise Dashboard" back: "Zurück" continue: "Fortsetzen" action_or: "ODER" @@ -1933,6 +1954,7 @@ de_DE: tax_category: "Steuerkategorie" calculator: "Rechner" calculator_values: "Rechnerwerte" + calculator_settings_warning: "Wenn Sie den Gebühren-Typ ändern, müssen Sie zuerst speichern, bevor Sie die Gebühren-Einstellungen bearbeiten können" flat_percent_per_item: "Flache Prozent (pro Artikel)" flat_rate_per_item: "Pauschale (pro Stück)" flat_rate_per_order: "Pauschalpreis pro Bestellung)" @@ -2263,6 +2285,7 @@ de_DE: enterprise_register_success_notice: "Herzliche Glückwünsche! Registrierung für %{enterprise} ist abgeschlossen!" enterprise_bulk_update_success_notice: "Unternehmen wurden erfolgreich aktualisiert" enterprise_bulk_update_error: 'Update fehlgeschlagen' + enterprise_shop_show_error: "Der gesuchte Shop existiert nicht oder ist auf OFN inaktiv. Bitte schauen Sie nach anderen Shops!" order_cycles_create_notice: 'Ihr Bestellzyklus wurde erstellt.' order_cycles_update_notice: 'Ihr Bestellzyklus wurde aktualisiert.' order_cycles_bulk_update_notice: 'Bestellzyklen wurden aktualisiert.' @@ -2417,6 +2440,12 @@ de_DE: severity: Schwere description: Beschreibung resolve: Entschlossenheit + exchange_products: + load_more_variants: "Weitere Varianten laden" + load_all_variants: "Alle Varianten laden" + select_all_variants: "Wählen Sie alle %{total_number_of_variants}-Varianten aus" + variants_loaded: "%{num_of_variants_loaded} von %{total_number_of_variants} Varianten geladen" + loading_variants: "lade Varianten" tag_rules: shipping_method_tagged_top: "Versandarten markiert" shipping_method_tagged_bottom: "sind:" @@ -2499,6 +2528,7 @@ de_DE: customer_placeholder: "Kunde@beispiel.org" valid_email_error: "Bitte geben Sie eine gültige E-Mail-Adresse ein" subscriptions: + error_saving: "Fehler beim Speichern des Abonnements" new: please_select_a_shop: "Bitte wählen Sie einen Laden" insufficient_stock: "Nicht genügend Lagerbestand verfügbar, nur noch %{on_hand} verfügbar" @@ -2575,9 +2605,78 @@ de_DE: have_an_account: "Hast du schon ein Konto?" action_login: "Jetzt einloggen." inflections: + each: + one: "jeder" + other: "pro Stück" + bunch: + one: "Bündel" + other: "Bündel" + pack: + one: "Pack" + other: "Packungen" + box: + one: "Box" + other: "Kisten" bottle: one: "Flasche" other: "Flaschen" + jar: + one: "Krug" + other: "Gläser" + head: + one: "Kopf" + other: "Köpfe" + bag: + one: "Tasche" + other: "Beutel" + loaf: + one: "Laib" + other: "Laibe" + single: + one: "Einzeln" + other: "Einzel" + tub: + one: "Wanne" + other: "Wannen" + punnet: + one: "Körbchen" + other: "Körbchen" + packet: + one: "Paket" + other: "Pakete" + item: + one: "Artikel" + other: "Artikel" + dozen: + one: "Dutzend" + other: "Dutzende" + unit: + one: "Einheit" + other: "Einheiten" + serve: + one: "Portion" + other: "Portionen" + tray: + one: "Schale" + other: "Schalen" + piece: + one: "Stück" + other: "Stücke" + pot: + one: "Topf" + other: "Töpfe" + bundle: + one: "bündeln" + other: "Bündel" + flask: + one: "Flasche" + other: "Flaschen" + basket: + one: "Korb" + other: "Körbe" + sack: + one: "Sack" + other: "Säcke" producers: signup: start_free_profile: "Beginnen Sie mit einem kostenlosen Profil und erweitern Sie es, wenn Sie fertig sind!" @@ -2643,6 +2742,7 @@ de_DE: status: "Status" new: "Neu" start: "Start" + end: "Ende" stop: "Halt" first: "Zuerst" previous: "Bisherige" @@ -2819,6 +2919,8 @@ de_DE: zipcode: Postleitzahl weight: Gewicht (pro kg) error_user_destroy_with_orders: "Benutzer mit abgeschlossenen Bestellungen dürfen nicht gelöscht werden" + cannot_create_payment_without_payment_methods: "Sie können keine Zahlung für eine Bestellung erstellen, ohne dass Zahlungsmethoden definiert sind." + please_define_payment_methods: "Bitte definieren Sie zunächst die Zahlungsmethoden." options: "Optionen" actions: update: "Aktualisieren" @@ -2905,6 +3007,7 @@ de_DE: capture: "Erfassung" ship: "Liefern" edit: "Bearbeiten" + order_not_updated: "Die Bestellung konnte nicht aktualisiert werden" note: "Hinweis" first: "Zuerst" last: "Letzte" @@ -2927,6 +3030,8 @@ de_DE: tax_invoice: "Steuerrechnung" code: "Code" from: "Von" + to: "Rechnungsempfänger" + shipping: "Versand" form: distribution_fields: title: "Verteilung" @@ -3124,6 +3229,12 @@ de_DE: used_saved_card: "Verwende eine gespeicherte Karte:" or_enter_new_card: "Oder geben Sie Details für eine neue Karte ein:" remember_this_card: Erinnerst du dich an diese Karte? + stripe_sca: + choose_one: Wähle Sie eine Option + enter_new_card: Geben Sie Details für eine neue Karte ein + used_saved_card: "Verwenden Sie eine gespeicherte Karte:" + or_enter_new_card: "Oder geben Sie Details für eine neue Karte ein:" + remember_this_card: Diese Karte speichern? date_picker: format: '% Y-% m-%d' js_format: 'JJ-MM-TT' From f437d0f8a05578499964b8e2401ada31a6e6d248 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Tue, 31 Mar 2020 15:59:17 +0200 Subject: [PATCH 106/130] Report environment correctly in Bugsnag JS --- app/views/layouts/_bugsnag_js.html.haml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/views/layouts/_bugsnag_js.html.haml b/app/views/layouts/_bugsnag_js.html.haml index a5e9ec32a1..b9f3f5a736 100644 --- a/app/views/layouts/_bugsnag_js.html.haml +++ b/app/views/layouts/_bugsnag_js.html.haml @@ -2,4 +2,9 @@ - if bugsnag_js_key.present? %script{src: "//d2wy8f7a9ursnm.cloudfront.net/v6/bugsnag.min.js"} :javascript - window.bugsnagClient = bugsnag("#{bugsnag_js_key}"); + window.bugsnagClient = bugsnag({ + apiKey: "#{bugsnag_js_key}", + beforeSend: function (report) { + report.app.releaseStage = "#{Rails.env}" + } + }); From 6a1c541479b024bb120c5f9475d71afdaddf1814 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Mon, 30 Mar 2020 19:59:40 +0100 Subject: [PATCH 107/130] Remove specific error color on checkout page so that the error message takes the default foundation error color which is white --- app/assets/stylesheets/darkswarm/checkout.css.scss | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/assets/stylesheets/darkswarm/checkout.css.scss b/app/assets/stylesheets/darkswarm/checkout.css.scss index 458fd24d5e..56c4f17170 100644 --- a/app/assets/stylesheets/darkswarm/checkout.css.scss +++ b/app/assets/stylesheets/darkswarm/checkout.css.scss @@ -109,9 +109,4 @@ checkout { } } } - - .error { - color: #c82020; - } } - From 9c17a912154a7bb2ca780060ae56ade4433a346f Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Wed, 1 Apr 2020 05:07:08 +1100 Subject: [PATCH 108/130] Updating translations for config/locales/en_GB.yml --- config/locales/en_GB.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/locales/en_GB.yml b/config/locales/en_GB.yml index b1f9ba7ec3..78ab3801e2 100644 --- a/config/locales/en_GB.yml +++ b/config/locales/en_GB.yml @@ -861,6 +861,12 @@ en_GB: cancel: "Cancel" back_to_list: "Back To List" outgoing: + outgoing: "Outgoing" + distributor: "Distributor" + products: "Products" + tags: "Tags" + delivery_details: "Delivery Details" + fees: "Fees" previous: "Previous" save: "Save" save_and_back_to_list: "Save and Back to List" From b22ad244f9578ead048fcbed3435cc2b22cedac8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2020 19:40:01 +0000 Subject: [PATCH 109/130] Bump ddtrace from 0.33.1 to 0.34.0 Bumps [ddtrace](https://github.com/DataDog/dd-trace-rb) from 0.33.1 to 0.34.0. - [Release notes](https://github.com/DataDog/dd-trace-rb/releases) - [Changelog](https://github.com/DataDog/dd-trace-rb/blob/master/CHANGELOG.md) - [Commits](https://github.com/DataDog/dd-trace-rb/compare/v0.33.1...v0.34.0) Signed-off-by: dependabot-preview[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index e05a795406..743b91a4d9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -216,7 +216,7 @@ GEM activerecord (>= 3.2.0, < 5.0) fog (~> 1.0) rails (>= 3.2.0, < 5.0) - ddtrace (0.33.1) + ddtrace (0.34.0) msgpack debugger-linecache (1.2.0) deface (1.0.2) From b898ce1ae1583ef0dacb607b8166934b2002bd8d Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Wed, 25 Mar 2020 17:33:29 +0000 Subject: [PATCH 110/130] Make checkout controller add flash error if order contains any type of error Here we add translations for a particular case where the credit card expiry date is in the past --- app/controllers/checkout_controller.rb | 7 +++---- config/locales/en.yml | 6 ++++++ spec/controllers/checkout_controller_spec.rb | 6 +++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/controllers/checkout_controller.rb b/app/controllers/checkout_controller.rb index 65d308bc0d..987f8d50d7 100644 --- a/app/controllers/checkout_controller.rb +++ b/app/controllers/checkout_controller.rb @@ -165,7 +165,7 @@ class CheckoutController < Spree::StoreController checkout_succeeded redirect_to(order_path(@order)) && return else - flash[:error] = order_workflow_error + flash[:error] = order_error checkout_failed end end @@ -179,8 +179,6 @@ class CheckoutController < Spree::StoreController @order.select_shipping_method(shipping_method_id) if @order.state == "delivery" next if advance_order_state(@order) - - flash[:error] = order_workflow_error return update_failed end @@ -205,7 +203,7 @@ class CheckoutController < Spree::StoreController false end - def order_workflow_error + def order_error if @order.errors.present? @order.errors.full_messages.to_sentence else @@ -245,6 +243,7 @@ class CheckoutController < Spree::StoreController end def update_failed + flash[:error] = order_error if flash.empty? checkout_failed update_failed_response end diff --git a/config/locales/en.yml b/config/locales/en.yml index 86c8f1611e..d8cf8784cd 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -40,6 +40,8 @@ en: shipping_category_id: "Shipping Category" variant_unit: "Variant Unit" variant_unit_name: "Variant Unit Name" + spree/credit_card: + base: "Credit Card" order_cycle: orders_close_at: Close date errors: @@ -50,6 +52,10 @@ en: taken: "There's already an account for this email. Please login or reset your password." spree/order: no_card: There are no authorised credit cards available to charge + spree/credit_card: + attributes: + base: + card_expired: "has expired" order_cycle: attributes: orders_close_at: diff --git a/spec/controllers/checkout_controller_spec.rb b/spec/controllers/checkout_controller_spec.rb index b11ca1ab78..ccbf458674 100644 --- a/spec/controllers/checkout_controller_spec.rb +++ b/spec/controllers/checkout_controller_spec.rb @@ -197,13 +197,13 @@ describe CheckoutController, type: :controller do allow(controller).to receive(:current_order).and_return(order) end - it "returns errors" do + it "returns errors and flash if order.update_attributes fails" do spree_post :update, format: :json, order: {} expect(response.status).to eq(400) - expect(response.body).to eq({ errors: assigns[:order].errors, flash: {} }.to_json) + expect(response.body).to eq({ errors: assigns[:order].errors, flash: { error: order.errors.full_messages.to_sentence } }.to_json) end - it "returns flash" do + it "returns errors and flash if order.next fails" do allow(order).to receive(:update_attributes).and_return true allow(order).to receive(:next).and_return false spree_post :update, format: :json, order: {} From ce2a164c666961ca69340265595afae5bf0f275f Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Tue, 31 Mar 2020 13:37:03 +0100 Subject: [PATCH 111/130] Stop using f_form_for Add labels for some fields, this was done automatically by rails foundation helper --- app/views/checkout/_form.html.haml | 2 +- app/views/checkout/_shipping.html.haml | 3 ++- app/views/user_passwords/edit.html.haml | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/views/checkout/_form.html.haml b/app/views/checkout/_form.html.haml index 901fe64a00..9a705ef4b5 100644 --- a/app/views/checkout/_form.html.haml +++ b/app/views/checkout/_form.html.haml @@ -3,7 +3,7 @@ = inject_available_payment_methods = inject_saved_credit_cards -= f_form_for current_order, += form_for current_order, html: {name: "checkout", id: "checkout_form", novalidate: true, diff --git a/app/views/checkout/_shipping.html.haml b/app/views/checkout/_shipping.html.haml index c137f2cdee..1e50dc33f8 100644 --- a/app/views/checkout/_shipping.html.haml +++ b/app/views/checkout/_shipping.html.haml @@ -52,7 +52,8 @@ .row .small-12.columns - = f.text_area :special_instructions, label: t(:checkout_instructions), size: "60x4", "ng-model" => "order.special_instructions" + %label{ for: 'order_special_instructions'}= t(:checkout_instructions) + = f.text_area :special_instructions, size: "60x4", "ng-model" => "order.special_instructions" .row .small-12.columns.text-right diff --git a/app/views/user_passwords/edit.html.haml b/app/views/user_passwords/edit.html.haml index 3042161881..eb1d9df0bc 100644 --- a/app/views/user_passwords/edit.html.haml +++ b/app/views/user_passwords/edit.html.haml @@ -1,4 +1,4 @@ -= f_form_for @spree_user, :as => :spree_user, :url => spree.spree_user_password_path, :method => :put do |f| += form_for @spree_user, :as => :spree_user, :url => spree.spree_user_password_path, :method => :put do |f| = render :partial => 'spree/shared/error_messages', :locals => { :target => @spree_user } %fieldset .row @@ -6,9 +6,11 @@ %legend= t(:change_my_password) .row .small-12.medium-6.large-4.columns.medium-centered.large-centered + %label{ for: 'spree_user_password'}= t(:password) = f.password_field :password .row .small-12.medium-6.large-4.columns.medium-centered.large-centered + %label{ for: 'spree_user_password_confirmation'}= t(:password_confirmation) = f.password_field :password_confirmation = f.hidden_field :reset_password_token .row From c3d25bf163a22d9c631e8ddc6c51387d4c3bda45 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Tue, 31 Mar 2020 17:02:01 +0100 Subject: [PATCH 112/130] Make checkout controller send bugsnag alerts on every checkout problem There are two new situations here: we will see order.errors after update_attributes fails but before order restart; and we will see how often the order is not complete when the workflow finishes (maybe none) --- app/controllers/checkout_controller.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/controllers/checkout_controller.rb b/app/controllers/checkout_controller.rb index 987f8d50d7..26c566e9a6 100644 --- a/app/controllers/checkout_controller.rb +++ b/app/controllers/checkout_controller.rb @@ -53,9 +53,8 @@ class CheckoutController < Spree::StoreController rescue Spree::Core::GatewayError => e rescue_from_spree_gateway_error(e) rescue StandardError => e - Bugsnag.notify(e) flash[:error] = I18n.t("checkout.failed") - update_failed + update_failed(e) end # Clears the cached order. Required for #current_order to return a new order @@ -179,6 +178,7 @@ class CheckoutController < Spree::StoreController @order.select_shipping_method(shipping_method_id) if @order.state == "delivery" next if advance_order_state(@order) + return update_failed end @@ -216,7 +216,7 @@ class CheckoutController < Spree::StoreController checkout_succeeded update_succeeded_response else - update_failed + update_failed(RuntimeError.new("Order not complete after the checkout workflow")) end end @@ -242,7 +242,9 @@ class CheckoutController < Spree::StoreController end end - def update_failed + def update_failed(error = RuntimeError.new(order_error)) + Bugsnag.notify(error) + flash[:error] = order_error if flash.empty? checkout_failed update_failed_response From 957b398a549a569d9946ba6fff720dfc057d7664 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Mon, 30 Mar 2020 19:31:47 +0100 Subject: [PATCH 113/130] Add call to $evalAsync() after Loading and FlashLoader are updated so that a angular digest is triggered This is required so that Loading.clear triggers a refresh and makes its placeholder to be cleared --- .../darkswarm/services/stripe_elements.js.coffee | 6 ++++++ .../darkswarm/services/stripe_elements_spec.js.coffee | 9 ++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee b/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee index acd220f092..6ec274cfec 100644 --- a/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee +++ b/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee @@ -15,6 +15,7 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) -> if(response.error) Loading.clear() RailsFlashLoader.loadFlash({error: t("error") + ": #{response.error.message}"}) + @triggerAngularDigest() else secrets.token = response.token.id secrets.cc_type = @mapCC(response.token.card.brand) @@ -32,12 +33,17 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) -> if(response.error) Loading.clear() RailsFlashLoader.loadFlash({error: t("error") + ": #{response.error.message}"}) + @triggerAngularDigest() else secrets.token = response.paymentMethod.id secrets.cc_type = response.paymentMethod.card.brand secrets.card = response.paymentMethod.card submit() + triggerAngularDigest: -> + # $evalAsync is improved way of triggering a digest without calling $apply + $rootScope.$evalAsync() + # Maps the brand returned by Stripe to that required by activemerchant mapCC: (ccType) -> switch ccType diff --git a/spec/javascripts/unit/darkswarm/services/stripe_elements_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/stripe_elements_spec.js.coffee index 0252298f82..7fd1d471dd 100644 --- a/spec/javascripts/unit/darkswarm/services/stripe_elements_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/stripe_elements_spec.js.coffee @@ -46,11 +46,10 @@ describe 'StripeElements Service', -> it "doesn't submit the form, shows an error message instead", inject (Loading, RailsFlashLoader) -> spyOn(Loading, "clear") spyOn(RailsFlashLoader, "loadFlash") - StripeElements.requestToken(secrets, submit) - $rootScope.$digest() # required for #then to by called - expect(submit).not.toHaveBeenCalled() - expect(Loading.clear).toHaveBeenCalled() - expect(RailsFlashLoader.loadFlash).toHaveBeenCalledWith({error: "Error: There was a problem"}) + StripeElements.requestToken(secrets, submit).then (data) -> + expect(submit).not.toHaveBeenCalled() + expect(Loading.clear).toHaveBeenCalled() + expect(RailsFlashLoader.loadFlash).toHaveBeenCalledWith({error: "Error: There was a problem"}) describe 'mapCC', -> it "maps the brand returned by Stripe to that required by activemerchant", -> From ad42b1b4857343ed22a1b5d6d86ee73ce7233f6e Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 1 Apr 2020 13:39:29 +0200 Subject: [PATCH 114/130] Remove pagination limits in BOM We can re-assess this later, but for now it looks like some of the BOM functionality won't work if results are returned across multiple pages. --- .../controllers/line_items_controller.js.coffee | 12 +----------- .../spree/admin/orders/bulk_management.html.haml | 3 --- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee index 6de9360c25..46a6598e4f 100644 --- a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -1,7 +1,6 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $http, $q, StatusMessage, Columns, SortOptions, Dereferencer, Orders, LineItems, Enterprises, OrderCycles, VariantUnitManager, RequestMonitor) -> $scope.initialized = false $scope.RequestMonitor = RequestMonitor - $scope.pagination = LineItems.pagination $scope.line_items = LineItems.all $scope.confirmDelete = true $scope.startDate = moment().startOf('day').subtract(7, 'days').format('YYYY-MM-DD') @@ -12,8 +11,6 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $scope.sharedResource = false $scope.columns = Columns.columns $scope.sorting = SortOptions - $scope.page = 1 - $scope.per_page = 50 $scope.confirmRefresh = -> LineItems.allSaved() || confirm(t("unsaved_changes_warning")) @@ -23,16 +20,11 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, $scope.supplierFilter = '' $scope.orderCycleFilter = '' $scope.quickSearch = '' - $scope.page = 1 $scope.resetSelectFilters = -> $scope.resetFilters() $scope.refreshData() - $scope.changePage = (newPage) -> - $scope.page = newPage - $scope.refreshData() - $scope.refreshData = -> unless !$scope.orderCycleFilter? || $scope.orderCycleFilter == '' $scope.setOrderCycleDateRange() @@ -75,9 +67,7 @@ angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $timeout, "q[variant_product_supplier_id_eq]": $scope.supplierFilter, "q[order_order_cycle_id_eq]": $scope.orderCycleFilter, "q[order_completed_at_gteq]": $scope.formattedStartDate, - "q[order_completed_at_lt]": $scope.formattedEndDate, - page: $scope.page, - per_page: $scope.per_page + "q[order_completed_at_lt]": $scope.formattedEndDate ) $scope.loadAssociatedData = -> diff --git a/app/views/spree/admin/orders/bulk_management.html.haml b/app/views/spree/admin/orders/bulk_management.html.haml index 44d137e7d8..4eb6a67aa0 100644 --- a/app/views/spree/admin/orders/bulk_management.html.haml +++ b/app/views/spree/admin/orders/bulk_management.html.haml @@ -184,6 +184,3 @@ %a{ ng: { href: "/admin/orders/{{line_item.order.number}}/edit" }, :class => "edit-order icon-edit no-text", 'confirm-link-click' => 'confirmRefresh()' } %td.actions %a{ 'ng-click' => "deleteLineItem(line_item)", :class => "delete-line-item icon-trash no-text" } - - .row - = render partial: 'admin/shared/angular_pagination' From 9bdb396b866470cce65625f8843ddb211b95a7ab Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Thu, 2 Apr 2020 01:54:42 +1100 Subject: [PATCH 115/130] Updating translations for config/locales/ca.yml --- config/locales/ca.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 8033d2018f..bad388b5e4 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -246,6 +246,8 @@ ca: notes: Notes error: Error processing_payment: "S'està processant el pagament..." + no_pending_payments: "No hi ha pagaments pendents" + invalid_payment_state: "Estat de pagament no vàlid" filter_results: Aplicar filtre quantity: Quantitat pick_up: Recollida @@ -862,6 +864,12 @@ ca: cancel: "Cancel·lar" back_to_list: "Tornar a la llista" outgoing: + outgoing: "Sortint" + distributor: "Distribuïdora" + products: "Productes " + tags: "Etiquetes" + delivery_details: "Detalls de lliurament" + fees: "Comissions" previous: "Anterior" save: "Desa" save_and_back_to_list: "Desa i torna a la llista" @@ -1487,6 +1495,7 @@ ca: shopping_oc_closed_description: "Si us plau espera fins que s'obri el pròxim cicle (o posa't en contacte amb nosaltres directament per veure si podem acceptar alguna comanda fora de temps)" shopping_oc_last_closed: "L'últim cicle va tancar fa %{distance_of_time} " shopping_oc_next_open: "El següent cicle s'obre en %{distance_of_time}" + shopping_oc_select: "Selecciona ..." shopping_tabs_home: "Inici" shopping_tabs_shop: "Botiga" shopping_tabs_about: "Sobre" @@ -1860,6 +1869,7 @@ ca: headline: "Acabat!" thanks: "Gràcies per omplir els detalls de%{enterprise}." login: "Pots canviar o actualitzar la teva organització en qualsevol moment accedint a Katuma i anant a Admin." + action: "Vés al panell de control de l'organització" back: "Enrere" continue: "Continua" action_or: "O" @@ -2629,6 +2639,12 @@ ca: tub: one: "tina" other: "cubells" + punnet: + one: "puntet" + other: "capses" + packet: + one: "paquet" + other: "paquets" item: one: "article" other: "articles" @@ -3015,6 +3031,8 @@ ca: tax_invoice: "FACTURA D'IMPOSTOS" code: "Codi" from: "De" + to: "Facturar a" + shipping: "Enviament" form: distribution_fields: title: "Distribució" From 6083d91d3e068be3f64fa7dd54845e4c3de7a7d2 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Thu, 2 Apr 2020 01:57:50 +1100 Subject: [PATCH 116/130] Updating translations for config/locales/ca.yml --- config/locales/ca.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/ca.yml b/config/locales/ca.yml index bad388b5e4..737c9ba7dc 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -868,7 +868,7 @@ ca: distributor: "Distribuïdora" products: "Productes " tags: "Etiquetes" - delivery_details: "Detalls de lliurament" + delivery_details: "Detalls d'enviament" fees: "Comissions" previous: "Anterior" save: "Desa" From 73c4eedd0600caa73a9181a9cb179f5b2ebd449f Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Wed, 25 Mar 2020 12:16:39 +0000 Subject: [PATCH 117/130] Translate credit card brand so that active merchant code handles the payment correctly Adds a simple console.log statement in case there is an error adding the card --- .../admin/payments/services/stripe_elements.js.coffee | 4 +++- .../javascripts/darkswarm/services/stripe_elements.js.coffee | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/admin/payments/services/stripe_elements.js.coffee b/app/assets/javascripts/admin/payments/services/stripe_elements.js.coffee index 9ecf2b1db1..e777ec2910 100644 --- a/app/assets/javascripts/admin/payments/services/stripe_elements.js.coffee +++ b/app/assets/javascripts/admin/payments/services/stripe_elements.js.coffee @@ -14,6 +14,7 @@ angular.module("admin.payments").factory 'AdminStripeElements', ($rootScope, Sta @stripe.createToken(@card, cardData).then (response) => if(response.error) StatusMessage.display 'error', response.error.message + console.log(JSON.stringify(response.error)) else secrets.token = response.token.id secrets.cc_type = @mapCC(response.token.card.brand) @@ -29,9 +30,10 @@ angular.module("admin.payments").factory 'AdminStripeElements', ($rootScope, Sta @stripe.createPaymentMethod({ type: 'card', card: @card }, @card, cardData).then (response) => if(response.error) StatusMessage.display 'error', response.error.message + console.log(JSON.stringify(response.error)) else secrets.token = response.paymentMethod.id - secrets.cc_type = response.paymentMethod.card.brand + secrets.cc_type = @mapCC(response.paymentMethod.card.brand) secrets.card = response.paymentMethod.card submit() diff --git a/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee b/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee index 6ec274cfec..9ac093124f 100644 --- a/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee +++ b/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee @@ -16,6 +16,7 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) -> Loading.clear() RailsFlashLoader.loadFlash({error: t("error") + ": #{response.error.message}"}) @triggerAngularDigest() + console.log(JSON.stringify(response.error)) else secrets.token = response.token.id secrets.cc_type = @mapCC(response.token.card.brand) @@ -34,9 +35,10 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) -> Loading.clear() RailsFlashLoader.loadFlash({error: t("error") + ": #{response.error.message}"}) @triggerAngularDigest() + console.log(JSON.stringify(response.error)) else secrets.token = response.paymentMethod.id - secrets.cc_type = response.paymentMethod.card.brand + secrets.cc_type = @mapCC(response.paymentMethod.card.brand) secrets.card = response.paymentMethod.card submit() From 0e268a171f39af6eb6fe8d340c9d2661dedaaf90 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Wed, 1 Apr 2020 17:22:17 +0100 Subject: [PATCH 118/130] Switch to console.error so we get a bugsnag alert everytime a user has a problem with their card Add paymentMethodsAPI specific mapping function, we had some errors in production with mastercards probably caused by ActiveMerchant not handling the card type correctly --- .../services/stripe_elements.js.coffee | 22 ++++++++++++------ .../services/stripe_elements.js.coffee | 22 ++++++++++++------ .../services/stripe_elements_spec.js.coffee | 23 ++++++++++++------- 3 files changed, 45 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/admin/payments/services/stripe_elements.js.coffee b/app/assets/javascripts/admin/payments/services/stripe_elements.js.coffee index e777ec2910..d5c20e8668 100644 --- a/app/assets/javascripts/admin/payments/services/stripe_elements.js.coffee +++ b/app/assets/javascripts/admin/payments/services/stripe_elements.js.coffee @@ -14,10 +14,10 @@ angular.module("admin.payments").factory 'AdminStripeElements', ($rootScope, Sta @stripe.createToken(@card, cardData).then (response) => if(response.error) StatusMessage.display 'error', response.error.message - console.log(JSON.stringify(response.error)) + console.error(JSON.stringify(response.error)) else secrets.token = response.token.id - secrets.cc_type = @mapCC(response.token.card.brand) + secrets.cc_type = @mapTokenApiCardBrand(response.token.card.brand) secrets.card = response.token.card submit() @@ -30,16 +30,16 @@ angular.module("admin.payments").factory 'AdminStripeElements', ($rootScope, Sta @stripe.createPaymentMethod({ type: 'card', card: @card }, @card, cardData).then (response) => if(response.error) StatusMessage.display 'error', response.error.message - console.log(JSON.stringify(response.error)) + console.error(JSON.stringify(response.error)) else secrets.token = response.paymentMethod.id - secrets.cc_type = @mapCC(response.paymentMethod.card.brand) + secrets.cc_type = @mapPaymentMethodsApiCardBrand(response.paymentMethod.card.brand) secrets.card = response.paymentMethod.card submit() - # Maps the brand returned by Stripe to that required by activemerchant - mapCC: (ccType) -> - switch ccType + # Maps the brand returned by Stripe's tokenAPI to that required by activemerchant + mapTokenApiCardBrand: (cardBrand) -> + switch cardBrand when 'MasterCard' then return 'master' when 'Visa' then return 'visa' when 'American Express' then return 'american_express' @@ -47,6 +47,14 @@ angular.module("admin.payments").factory 'AdminStripeElements', ($rootScope, Sta when 'JCB' then return 'jcb' when 'Diners Club' then return 'diners_club' + # Maps the brand returned by Stripe's paymentMethodsAPI to that required by activemerchant + mapPaymentMethodsApiCardBrand: (cardBrand) -> + switch cardBrand + when 'mastercard' then return 'master' + when 'amex' then return 'american_express' + when 'diners' then return 'diners_club' + else return cardBrand # a few brands are equal, for example, visa + # It doesn't matter if any of these are nil, all are optional. makeCardData: (secrets) -> {'name': secrets.name, diff --git a/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee b/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee index 9ac093124f..46a6babf0d 100644 --- a/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee +++ b/app/assets/javascripts/darkswarm/services/stripe_elements.js.coffee @@ -16,10 +16,10 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) -> Loading.clear() RailsFlashLoader.loadFlash({error: t("error") + ": #{response.error.message}"}) @triggerAngularDigest() - console.log(JSON.stringify(response.error)) + console.error(JSON.stringify(response.error)) else secrets.token = response.token.id - secrets.cc_type = @mapCC(response.token.card.brand) + secrets.cc_type = @mapTokenApiCardBrand(response.token.card.brand) secrets.card = response.token.card submit() @@ -35,10 +35,10 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) -> Loading.clear() RailsFlashLoader.loadFlash({error: t("error") + ": #{response.error.message}"}) @triggerAngularDigest() - console.log(JSON.stringify(response.error)) + console.error(JSON.stringify(response.error)) else secrets.token = response.paymentMethod.id - secrets.cc_type = @mapCC(response.paymentMethod.card.brand) + secrets.cc_type = @mapPaymentMethodsApiCardBrand(response.paymentMethod.card.brand) secrets.card = response.paymentMethod.card submit() @@ -46,9 +46,9 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) -> # $evalAsync is improved way of triggering a digest without calling $apply $rootScope.$evalAsync() - # Maps the brand returned by Stripe to that required by activemerchant - mapCC: (ccType) -> - switch ccType + # Maps the brand returned by Stripe's tokenAPI to that required by activemerchant + mapTokenApiCardBrand: (cardBrand) -> + switch cardBrand when 'MasterCard' then return 'master' when 'Visa' then return 'visa' when 'American Express' then return 'american_express' @@ -56,6 +56,14 @@ Darkswarm.factory 'StripeElements', ($rootScope, Loading, RailsFlashLoader) -> when 'JCB' then return 'jcb' when 'Diners Club' then return 'diners_club' + # Maps the brand returned by Stripe's paymentMethodsAPI to that required by activemerchant + mapPaymentMethodsApiCardBrand: (cardBrand) -> + switch cardBrand + when 'mastercard' then return 'master' + when 'amex' then return 'american_express' + when 'diners' then return 'diners_club' + else return cardBrand # a few brands are equal, for example, visa + # It doesn't matter if any of these are nil, all are optional. makeCardData: (secrets) -> {'name': secrets.name, diff --git a/spec/javascripts/unit/darkswarm/services/stripe_elements_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/stripe_elements_spec.js.coffee index 7fd1d471dd..821df4b153 100644 --- a/spec/javascripts/unit/darkswarm/services/stripe_elements_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/stripe_elements_spec.js.coffee @@ -51,11 +51,18 @@ describe 'StripeElements Service', -> expect(Loading.clear).toHaveBeenCalled() expect(RailsFlashLoader.loadFlash).toHaveBeenCalledWith({error: "Error: There was a problem"}) - describe 'mapCC', -> - it "maps the brand returned by Stripe to that required by activemerchant", -> - expect(StripeElements.mapCC('MasterCard')).toEqual "master" - expect(StripeElements.mapCC('Visa')).toEqual "visa" - expect(StripeElements.mapCC('American Express')).toEqual "american_express" - expect(StripeElements.mapCC('Discover')).toEqual "discover" - expect(StripeElements.mapCC('JCB')).toEqual "jcb" - expect(StripeElements.mapCC('Diners Club')).toEqual "diners_club" + describe 'mapTokenApiCardBrand', -> + it "maps the brand returned by Stripe's tokenAPI to that required by activemerchant", -> + expect(StripeElements.mapTokenApiCardBrand('MasterCard')).toEqual "master" + expect(StripeElements.mapTokenApiCardBrand('Visa')).toEqual "visa" + expect(StripeElements.mapTokenApiCardBrand('American Express')).toEqual "american_express" + expect(StripeElements.mapTokenApiCardBrand('Discover')).toEqual "discover" + expect(StripeElements.mapTokenApiCardBrand('JCB')).toEqual "jcb" + expect(StripeElements.mapTokenApiCardBrand('Diners Club')).toEqual "diners_club" + + describe 'mapPaymentMethodsApiCardBrand', -> + it "maps the brand returned by Stripe's paymentMethodsAPI to that required by activemerchant", -> + expect(StripeElements.mapPaymentMethodsApiCardBrand('mastercard')).toEqual "master" + expect(StripeElements.mapPaymentMethodsApiCardBrand('amex')).toEqual "american_express" + expect(StripeElements.mapPaymentMethodsApiCardBrand('diners')).toEqual "diners_club" + expect(StripeElements.mapPaymentMethodsApiCardBrand('visa')).toEqual "visa" From d38da02113ab8b634b03782a2d4454928e4ee4c6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 1 Apr 2020 19:20:42 +0000 Subject: [PATCH 119/130] Bump rubocop from 0.80.1 to 0.81.0 Bumps [rubocop](https://github.com/rubocop-hq/rubocop) from 0.80.1 to 0.81.0. - [Release notes](https://github.com/rubocop-hq/rubocop/releases) - [Changelog](https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop-hq/rubocop/compare/v0.80.1...v0.81.0) Signed-off-by: dependabot-preview[bot] --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 0f324f463e..42be3c27d3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -594,14 +594,14 @@ GEM rspec-retry (0.6.2) rspec-core (> 3.3) rspec-support (3.9.2) - rubocop (0.80.1) + rubocop (0.81.0) jaro_winkler (~> 1.5.1) parallel (~> 1.10) parser (>= 2.7.0.1) rainbow (>= 2.2.2, < 4.0) rexml ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 1.7) + unicode-display_width (>= 1.4.0, < 2.0) rubocop-rails (2.5.0) activesupport rack (>= 1.1) @@ -661,7 +661,7 @@ GEM tzinfo (0.3.56) uglifier (4.2.0) execjs (>= 0.3.0, < 3) - unicode-display_width (1.6.1) + unicode-display_width (1.7.0) unicorn (5.5.4) kgio (~> 2.6) raindrops (~> 0.7) From 2230d83729db4700fb7471a01a16d1f78f344391 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Thu, 2 Apr 2020 08:41:25 +1100 Subject: [PATCH 120/130] Updating translations for config/locales/pt_BR.yml --- config/locales/pt_BR.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/config/locales/pt_BR.yml b/config/locales/pt_BR.yml index 04c707defb..87be993fe0 100644 --- a/config/locales/pt_BR.yml +++ b/config/locales/pt_BR.yml @@ -861,6 +861,12 @@ pt_BR: cancel: "Cancelar" back_to_list: "Voltar Para a Lista" outgoing: + outgoing: "Saída" + distributor: "Distribuidor" + products: "Produtos" + tags: "Etiqueta" + delivery_details: "Sobre a Entrega" + fees: "Taxas" previous: "Anterior" save: "Salvar" save_and_back_to_list: "Salvar e Voltar para a Lista" @@ -1628,7 +1634,7 @@ pt_BR: sell_hubs_detail: "Crie um perfil para a sua iniciativa ou organização na Open Food Brasil. A qualquer momento você poderá fazer um upgrade para um mercado de multi-produtores. " sell_groups_detail: "Organize um lista personalizada de negócios (cultivos, cooperativas, comércios, etc.) para sua região ou organização. " sell_user_guide: "Saiba mais acessando nosso guia. " - sell_listing_price: "A listagem no OFN é gratuita. A abertura e administração de uma loja no OFN custa US $ 500 em vendas mensais. Se você vender mais, poderá escolher sua contribuição da comunidade entre 1% e 3% das vendas. Para mais detalhes sobre preços, visite a seção Plataforma do Software através do link Sobre no menu superior." + sell_listing_price: "A OFB está em sua versão beta. Por acreditarmos que a plataforma pode colaborar com a circulação e a comercialização de comida de verdade no período de quarentena imposto pela atual pandemia, sua utilização permanecerá gratuita nos próximos meses." sell_embed: "Também podemos incorporar uma loja da Open Food Brasil em seu próprio site personalizado ou criar um site específico para a sua região. " sell_ask_services: "Pergunte-nos sobre nossos serviços." shops_title: Lojas @@ -3301,7 +3307,7 @@ pt_BR: Se o URL acima não funcionar, tente copiar e colá-lo em seu navegador. Se continuar a ter problemas, por favor não hesite em contactar-nos. confirmation_instructions: - subject: Confirme sua conta OFN + subject: Confirme sua conta na OFB users: form: account_settings: Configurações da conta From e08606a3108ed65a9ca55bb66ccd2c4d767f36f3 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Thu, 2 Apr 2020 18:02:57 +1100 Subject: [PATCH 121/130] Updating translations for config/locales/en_FR.yml --- config/locales/en_FR.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/locales/en_FR.yml b/config/locales/en_FR.yml index d5c1dc2400..5665f4422b 100644 --- a/config/locales/en_FR.yml +++ b/config/locales/en_FR.yml @@ -19,6 +19,8 @@ en_FR: shipping_category_id: "Shipping Category" variant_unit: "Variant Unit" variant_unit_name: "Variant Unit Name" + spree/credit_card: + base: "Credit Card" order_cycle: orders_close_at: Close date errors: @@ -29,6 +31,10 @@ en_FR: taken: "There's already an account for this email. Please login or reset your password." spree/order: no_card: There are no authorised credit cards available to charge + spree/credit_card: + attributes: + base: + card_expired: "has expired" order_cycle: attributes: orders_close_at: From b1f8f910114bb45cd59c1611a69dd7b6ce0d78c6 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Thu, 2 Apr 2020 18:03:11 +1100 Subject: [PATCH 122/130] Updating translations for config/locales/fr.yml --- config/locales/fr.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 789f986949..7566b07d58 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -19,6 +19,8 @@ fr: shipping_category_id: "Condition de transport" variant_unit: "Unité" variant_unit_name: "Unité de la variante" + spree/credit_card: + base: "Carte de crédit" order_cycle: orders_close_at: Date de fermeture errors: @@ -29,6 +31,10 @@ fr: taken: "Un compte existe déjà pour cet e-mail. Connectez-vous ou demandez un nouveau mot de passe." spree/order: no_card: Aucune carte de paiement autorisée disponible + spree/credit_card: + attributes: + base: + card_expired: "a expiré" order_cycle: attributes: orders_close_at: From 40f8cf660c3a343e6cc3bd3c72c6564d6655f032 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Thu, 2 Apr 2020 13:21:52 +0200 Subject: [PATCH 123/130] Update vendor/assets/angular-google-maps.min.js Fixes an issue where if the js library from maps.googleapis.com failed to load in the , all of our subsequent Angular would completely break. See: https://github.com/angular-ui/angular-google-maps Note: `bluebird.js` is a new dependency of `angular-google-maps.js`. --- .../javascripts/darkswarm/all.js.coffee | 1 + .../javascripts/darkswarm/darkswarm.js.coffee | 2 +- .../directives/map_osm_tiles.js.coffee | 2 +- .../darkswarm/directives/map_search.js.coffee | 2 +- app/views/groups/show.html.haml | 4 +- app/views/map/index.html.haml | 4 +- spec/javascripts/application_spec.js | 2 +- .../javascripts/angular-google-maps.min.js | 13 +- vendor/assets/javascripts/bluebird.js | 5347 +++++++++++++++++ 9 files changed, 5363 insertions(+), 14 deletions(-) create mode 100644 vendor/assets/javascripts/bluebird.js diff --git a/app/assets/javascripts/darkswarm/all.js.coffee b/app/assets/javascripts/darkswarm/all.js.coffee index b62eb915af..566ba5d996 100644 --- a/app/assets/javascripts/darkswarm/all.js.coffee +++ b/app/assets/javascripts/darkswarm/all.js.coffee @@ -9,6 +9,7 @@ #= require angular-animate #= require angular-resource #= require lodash.underscore.js +#= require bluebird.js #= require angular-scroll.min.js #= require angular-google-maps.min.js #= require ../shared/mm-foundation-tpls-0.9.0-20180826174721.min.js diff --git a/app/assets/javascripts/darkswarm/darkswarm.js.coffee b/app/assets/javascripts/darkswarm/darkswarm.js.coffee index 01a93a3dd1..d26b884045 100644 --- a/app/assets/javascripts/darkswarm/darkswarm.js.coffee +++ b/app/assets/javascripts/darkswarm/darkswarm.js.coffee @@ -7,7 +7,7 @@ window.Darkswarm = angular.module("Darkswarm", [ 'templates', 'ngSanitize', 'ngAnimate', - 'google-maps', + 'uiGmapgoogle-maps', 'duScroll', 'angularFileUpload', 'angularSlideables' diff --git a/app/assets/javascripts/darkswarm/directives/map_osm_tiles.js.coffee b/app/assets/javascripts/darkswarm/directives/map_osm_tiles.js.coffee index deb7186d95..394236ce61 100644 --- a/app/assets/javascripts/darkswarm/directives/map_osm_tiles.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/map_osm_tiles.js.coffee @@ -1,6 +1,6 @@ Darkswarm.directive 'mapOsmTiles', ($timeout) -> restrict: 'E' - require: '^googleMap' + require: '^uiGmapGoogleMap' scope: {} link: (scope, elem, attrs, ctrl) -> $timeout => diff --git a/app/assets/javascripts/darkswarm/directives/map_search.js.coffee b/app/assets/javascripts/darkswarm/directives/map_search.js.coffee index af82766d62..322fa4bb28 100644 --- a/app/assets/javascripts/darkswarm/directives/map_search.js.coffee +++ b/app/assets/javascripts/darkswarm/directives/map_search.js.coffee @@ -1,7 +1,7 @@ Darkswarm.directive 'mapSearch', ($timeout, Search) -> # Install a basic search field in a map restrict: 'E' - require: ['^googleMap', 'ngModel'] + require: ['^uiGmapGoogleMap', 'ngModel'] replace: true template: '' scope: {} diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 76a83fd734..e8318faef9 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -34,10 +34,10 @@ select: "select(\'map\')"} .map-container %map{"ng-if" => "(isActive(\'/map\') && (mapShowed = true)) || mapShowed"} - %google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"} + %ui-gmap-google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"} %map-osm-tiles %map-search - %markers{models: "mapMarkers", fit: "true", + %ui-gmap-markers{models: "mapMarkers", fit: "true", coords: "'self'", icon: "'icon'", click: "'reveal'"} %tab{heading: t(:groups_about), diff --git a/app/views/map/index.html.haml b/app/views/map/index.html.haml index 48e05d2d0a..294fa45f8e 100644 --- a/app/views/map/index.html.haml +++ b/app/views/map/index.html.haml @@ -6,10 +6,10 @@ .map-container{"fill-vertical" => true} %map{"ng-controller" => "MapCtrl"} - %google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"} + %ui-gmap-google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"} %map-osm-tiles %map-search - %markers{models: "OfnMap.enterprises", fit: "true", + %ui-gmap-markers{models: "OfnMap.enterprises", fit: "true", coords: "'self'", icon: "'icon'", click: "'reveal'"} .map-footer diff --git a/spec/javascripts/application_spec.js b/spec/javascripts/application_spec.js index 37194fc2b5..47368298f8 100644 --- a/spec/javascripts/application_spec.js +++ b/spec/javascripts/application_spec.js @@ -16,5 +16,5 @@ //= require i18n angular.module('templates', []) -angular.module('google-maps', []); +angular.module('uiGmapgoogle-maps', []); angular.module('duScroll', []); diff --git a/vendor/assets/javascripts/angular-google-maps.min.js b/vendor/assets/javascripts/angular-google-maps.min.js index 84af2d8ff7..295fe9cc13 100644 --- a/vendor/assets/javascripts/angular-google-maps.min.js +++ b/vendor/assets/javascripts/angular-google-maps.min.js @@ -1,8 +1,9 @@ -/*! angular-google-maps 1.1.0 2014-05-17 +/*! angular-google-maps 2.0.0 2014-10-09 * AngularJS directives for Google Maps - * git: https://github.com/nlaplante/angular-google-maps.git + * git: https://github.com/angular-ui/angular-google-maps.git */ -function InfoBox(a){a=a||{},google.maps.OverlayView.apply(this,arguments),this.content_=a.content||"",this.disableAutoPan_=a.disableAutoPan||!1,this.maxWidth_=a.maxWidth||0,this.pixelOffset_=a.pixelOffset||new google.maps.Size(0,0),this.position_=a.position||new google.maps.LatLng(0,0),this.zIndex_=a.zIndex||null,this.boxClass_=a.boxClass||"infoBox",this.boxStyle_=a.boxStyle||{},this.closeBoxMargin_=a.closeBoxMargin||"2px",this.closeBoxURL_=a.closeBoxURL||"http://www.google.com/intl/en_us/mapfiles/close.gif",""===a.closeBoxURL&&(this.closeBoxURL_=""),this.infoBoxClearance_=a.infoBoxClearance||new google.maps.Size(1,1),"undefined"==typeof a.visible&&(a.visible="undefined"==typeof a.isHidden?!0:!a.isHidden),this.isHidden_=!a.visible,this.alignBottom_=a.alignBottom||!1,this.pane_=a.pane||"floatPane",this.enableEventPropagation_=a.enableEventPropagation||!1,this.div_=null,this.closeListener_=null,this.moveListener_=null,this.contextListener_=null,this.eventListeners_=null,this.fixedWidthSet_=null}function ClusterIcon(a,b){a.getMarkerClusterer().extend(ClusterIcon,google.maps.OverlayView),this.cluster_=a,this.className_=a.getMarkerClusterer().getClusterClass(),this.styles_=b,this.center_=null,this.div_=null,this.sums_=null,this.visible_=!1,this.setMap(a.getMap())}function Cluster(a){this.markerClusterer_=a,this.map_=a.getMap(),this.gridSize_=a.getGridSize(),this.minClusterSize_=a.getMinimumClusterSize(),this.averageCenter_=a.getAverageCenter(),this.markers_=[],this.center_=null,this.bounds_=null,this.clusterIcon_=new ClusterIcon(this,a.getStyles())}function MarkerClusterer(a,b,c){this.extend(MarkerClusterer,google.maps.OverlayView),b=b||[],c=c||{},this.markers_=[],this.clusters_=[],this.listeners_=[],this.activeMap_=null,this.ready_=!1,this.gridSize_=c.gridSize||60,this.minClusterSize_=c.minimumClusterSize||2,this.maxZoom_=c.maxZoom||null,this.styles_=c.styles||[],this.title_=c.title||"",this.zoomOnClick_=!0,void 0!==c.zoomOnClick&&(this.zoomOnClick_=c.zoomOnClick),this.averageCenter_=!1,void 0!==c.averageCenter&&(this.averageCenter_=c.averageCenter),this.ignoreHidden_=!1,void 0!==c.ignoreHidden&&(this.ignoreHidden_=c.ignoreHidden),this.enableRetinaIcons_=!1,void 0!==c.enableRetinaIcons&&(this.enableRetinaIcons_=c.enableRetinaIcons),this.imagePath_=c.imagePath||MarkerClusterer.IMAGE_PATH,this.imageExtension_=c.imageExtension||MarkerClusterer.IMAGE_EXTENSION,this.imageSizes_=c.imageSizes||MarkerClusterer.IMAGE_SIZES,this.calculator_=c.calculator||MarkerClusterer.CALCULATOR,this.batchSize_=c.batchSize||MarkerClusterer.BATCH_SIZE,this.batchSizeIE_=c.batchSizeIE||MarkerClusterer.BATCH_SIZE_IE,this.clusterClass_=c.clusterClass||"cluster",-1!==navigator.userAgent.toLowerCase().indexOf("msie")&&(this.batchSize_=this.batchSizeIE_),this.setupStyles_(),this.addMarkers(b,!0),this.setMap(a)}function inherits(a,b){function c(){}c.prototype=b.prototype,a.superClass_=b.prototype,a.prototype=new c,a.prototype.constructor=a}function MarkerLabel_(a,b){this.marker_=a,this.handCursorURL_=a.handCursorURL,this.labelDiv_=document.createElement("div"),this.labelDiv_.style.cssText="position: absolute; overflow: hidden;",this.eventDiv_=document.createElement("div"),this.eventDiv_.style.cssText=this.labelDiv_.style.cssText,this.eventDiv_.setAttribute("onselectstart","return false;"),this.eventDiv_.setAttribute("ondragstart","return false;"),this.crossDiv_=MarkerLabel_.getSharedCross(b)}function MarkerWithLabel(a){a=a||{},a.labelContent=a.labelContent||"",a.labelAnchor=a.labelAnchor||new google.maps.Point(0,0),a.labelClass=a.labelClass||"markerLabels",a.labelStyle=a.labelStyle||{},a.labelInBackground=a.labelInBackground||!1,"undefined"==typeof a.labelVisible&&(a.labelVisible=!0),"undefined"==typeof a.raiseOnDrag&&(a.raiseOnDrag=!0),"undefined"==typeof a.clickable&&(a.clickable=!0),"undefined"==typeof a.draggable&&(a.draggable=!1),"undefined"==typeof a.optimized&&(a.optimized=!1),a.crossImage=a.crossImage||"http"+("https:"===document.location.protocol?"s":"")+"://maps.gstatic.com/intl/en_us/mapfiles/drag_cross_67_16.png",a.handCursor=a.handCursor||"http"+("https:"===document.location.protocol?"s":"")+"://maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur",a.optimized=!1,this.label=new MarkerLabel_(this,a.crossImage,a.handCursor),google.maps.Marker.apply(this,arguments)}(function(){angular.module("google-maps.directives.api.utils",[]),angular.module("google-maps.directives.api.managers",[]),angular.module("google-maps.directives.api.models.child",["google-maps.directives.api.utils"]),angular.module("google-maps.directives.api.models.parent",["google-maps.directives.api.managers","google-maps.directives.api.models.child"]),angular.module("google-maps.directives.api",["google-maps.directives.api.models.parent"]),angular.module("google-maps",["google-maps.directives.api"]).factory("debounce",["$timeout",function(a){return function(b){var c;return c=0,function(){var d,e,f;return f=this,d=arguments,c++,e=function(a){return function(){return a===c?b.apply(f,d):void 0}}(c),a(e,0,!0)}}}])}).call(this),function(){angular.element(document).ready(function(){return(google||("undefined"!=typeof google&&null!==google?google.maps:void 0)||null!=google.maps.InfoWindow)&&(google.maps.InfoWindow.prototype._open=google.maps.InfoWindow.prototype.open,google.maps.InfoWindow.prototype._close=google.maps.InfoWindow.prototype.close,google.maps.InfoWindow.prototype._isOpen=!1,google.maps.InfoWindow.prototype.open=function(a,b){this._isOpen=!0,this._open(a,b)},google.maps.InfoWindow.prototype.close=function(){this._isOpen=!1,this._close()},google.maps.InfoWindow.prototype.isOpen=function(a){return null==a&&(a=void 0),null==a?this._isOpen:this._isOpen=a},window.InfoBox)?(window.InfoBox.prototype._open=window.InfoBox.prototype.open,window.InfoBox.prototype._close=window.InfoBox.prototype.close,window.InfoBox.prototype._isOpen=!1,window.InfoBox.prototype.open=function(a,b){this._isOpen=!0,this._open(a,b)},window.InfoBox.prototype.close=function(){this._isOpen=!1,this._close()},window.InfoBox.prototype.isOpen=function(a){return null==a&&(a=void 0),null==a?this._isOpen:this._isOpen=a}):void 0})}.call(this),function(){_.intersectionObjects=function(a,b,c){var d;return null==c&&(c=void 0),d=_.map(a,function(a){return _.find(b,function(b){return null!=c?c(a,b):_.isEqual(a,b)})}),_.filter(d,function(a){return null!=a})},_.containsObject=_.includeObject=function(a,b,c){return null==c&&(c=void 0),null===a?!1:_.any(a,function(a){return null!=c?c(a,b):_.isEqual(a,b)})},_.differenceObjects=function(a,b,c){return null==c&&(c=void 0),_.filter(a,function(a){return!_.containsObject(b,a)})},_.withoutObjects=function(a,b){return _.differenceObjects(a,b)},_.indexOfObject=function(a,b,c,d){var e,f;if(null==a)return-1;if(e=0,f=a.length,d){if("number"!=typeof d)return e=_.sortedIndex(a,b),a[e]===b?e:-1;e=0>d?Math.max(0,f+d):d}for(;f>e;){if(null!=c){if(c(a[e],b))return e}else if(_.isEqual(a[e],b))return e;e++}return-1},_["extends"]=function(a){return _.reduce(a,function(a,b){return _.extend(a,b)},{})}}.call(this),function(){var a;a={each:function(a,b,c,d,e,f){var g;return null==e&&(e=100),null==f&&(f=0),void 0===a||(null!=a?a.length:void 0)<=0?void c():(g=function(){var h,i;for(h=e,i=f;h--&&ib;b++)if(b in this&&this[b]===a)return b;return-1};angular.module("google-maps.directives.api.utils").factory("BaseObject",function(){var b,c;return c=["extended","included"],b=function(){function b(){}return b.extend=function(b){var d,e,f;for(d in b)e=b[d],a.call(c,d)<0&&(this[d]=e);return null!=(f=b.extended)&&f.apply(this),this},b.include=function(b){var d,e,f;for(d in b)e=b[d],a.call(c,d)<0&&(this.prototype[d]=e);return null!=(f=b.included)&&f.apply(this),this},b}()})}.call(this),function(){angular.module("google-maps.directives.api.utils").factory("ChildEvents",function(){return{onChildCreation:function(){}}})}.call(this),function(){var a={}.hasOwnProperty,b=function(b,c){function d(){this.constructor=b}for(var e in c)a.call(c,e)&&(b[e]=c[e]);return d.prototype=c.prototype,b.prototype=new d,b.__super__=c.prototype,b};angular.module("google-maps.directives.api.utils").factory("FitHelper",["BaseObject","Logger",function(a){var c,d;return c=function(a){function c(){return d=c.__super__.constructor.apply(this,arguments)}return b(c,a),c.prototype.fit=function(a,b){var c,d;return b&&a&&a.length>0?(c=new google.maps.LatLngBounds,d=!1,_async.each(a,function(a){return a?(d||(d=!0),c.extend(a.getPosition())):void 0},function(){return d?b.fitBounds(c):void 0})):void 0},c}(a)}])}.call(this),function(){angular.module("google-maps.directives.api.utils").service("GmapUtil",["Logger","$compile",function(a,b){var c,d;return c=function(a){return Array.isArray(a)&&2===a.length?new google.maps.LatLng(a[1],a[0]):angular.isDefined(a.type)&&"Point"===a.type?new google.maps.LatLng(a.coordinates[1],a.coordinates[0]):new google.maps.LatLng(a.latitude,a.longitude)},d=function(a){if(angular.isUndefined(a))return!1;if(_.isArray(a)){if(2===a.length)return!0}else if(null!=a&&(null!=a?a.type:void 0)){if("Point"===a.type&&_.isArray(a.coordinates)&&2===a.coordinates.length)return!0}else if(a&&angular.isDefined((null!=a?a.latitude:void 0)&&angular.isDefined(null!=a?a.longitude:void 0)))return!0;return!1},{getLabelPositionPoint:function(a){var b,c;return void 0===a?void 0:(a=/^([-\d\.]+)\s([-\d\.]+)$/.exec(a),b=parseFloat(a[1]),c=parseFloat(a[2]),null!=b&&null!=c?new google.maps.Point(b,c):void 0)},createMarkerOptions:function(a,b,e,f){var g;return null==f&&(f=void 0),null==e&&(e={}),g=angular.extend({},e,{position:null!=e.position?e.position:c(a),icon:null!=e.icon?e.icon:b,visible:null!=e.visible?e.visible:d(a)}),null!=f&&(g.map=f),g},createWindowOptions:function(d,e,f,g){return null!=f&&null!=g&&null!=b?angular.extend({},g,{content:this.buildContent(e,g,f),position:null!=g.position?g.position:angular.isObject(d)?d.getPosition():c(e.coords)}):g?g:(a.error("infoWindow defaults not defined"),f?void 0:a.error("infoWindow content not defined"))},buildContent:function(a,c,d){var e,f;return null!=c.content?f=c.content:null!=b?(e=b(d)(a),e.length>0&&(f=e[0])):f=d,f},defaultDelay:50,isTrue:function(a){return angular.isDefined(a)&&null!==a&&a===!0||"1"===a||"y"===a||"true"===a},isFalse:function(a){return-1!==["false","FALSE",0,"n","N","no","NO"].indexOf(a)},getCoords:c,validateCoords:d,validatePath:function(a){var b,c;if(c=0,angular.isUndefined(a.type)){if(!Array.isArray(a)||a.length<2)return!1;for(;cc;c++)b=a[c],e.push(this.add(b));return e},f.prototype.remove=function(a,b){var c,d;return this.handleOptDraw(a,b,!1),b?(c=void 0,null!=this.gMarkers.indexOf?c=this.gMarkers.indexOf(a):(d=0,_.find(this.gMarkers,function(b){d+=1,b===a&&(c=d)})),null!=c?this.gMarkers.splice(c,1):void 0):void 0},f.prototype.removeMany=function(){var a=this;return this.gMarkers.forEach(function(b){return a.remove(b)})},f.prototype.draw=function(){var a,b=this;return a=[],this.gMarkers.forEach(function(c){return c.isDrawn?void 0:c.doAdd?c.setMap(b.gMap):a.push(c)}),a.forEach(function(a){return b.remove(a,!0)})},f.prototype.clear=function(){var a,b,c,d;for(d=this.gMarkers,b=0,c=d.length;c>b;b++)a=d[b],a.setMap(null);return delete this.gMarkers,this.gMarkers=[]},f.prototype.handleOptDraw=function(a,b,c){return b===!0?(a.setMap(c?this.gMap:null),a.isDrawn=!0):(a.isDrawn=!1,a.doAdd=c)},f.prototype.fit=function(){return f.__super__.fit.call(this,this.gMarkers,this.gMap)},f}(d)}])}.call(this),function(){angular.module("google-maps").factory("array-sync",["add-events",function(a){return function(b,c,d){var e,f,g,h,i,j,k,l,m;return h=!1,l=c.$eval(d),c["static"]||(i={set_at:function(a){var c;if(!h&&(c=b.getAt(a)))return c.lng&&c.lat?(l[a].latitude=c.lat(),l[a].longitude=c.lng()):l[a]=c},insert_at:function(a){var c;if(!h&&(c=b.getAt(a)))return c.lng&&c.lat?l.splice(a,0,{latitude:c.lat(),longitude:c.lng()}):l.splice(a,0,c)},remove_at:function(a){return h?void 0:l.splice(a,1)}},"Polygon"===l.type?e=l.coordinates[0]:"LineString"===l.type&&(e=l.coordinates),f={set_at:function(a){var c;if(!h&&(c=b.getAt(a),c&&c.lng&&c.lat))return e[a][1]=c.lat(),e[a][0]=c.lng()},insert_at:function(a){var c;if(!h&&(c=b.getAt(a),c&&c.lng&&c.lat))return e.splice(a,0,[c.lng(),c.lat()])},remove_at:function(a){return h?void 0:e.splice(a,1)}},k=a(b,angular.isUndefined(l.type)?i:f)),j=function(a){var c,d,e,f,g,i,j;if(h=!0,g=b,a){for(c=0,i=g.getLength(),e=a.length,d=Math.min(i,e),f=void 0;d>c;)j=g.getAt(c),f=a[c],"function"==typeof f.equals?f.equals(j)||g.setAt(c,f):(j.lat()!==f.latitude||j.lng()!==f.longitude)&&g.setAt(c,new google.maps.LatLng(f.latitude,f.longitude)),c++;for(;e>c;)f=a[c],g.push("function"==typeof f.lat&&"function"==typeof f.lng?f:new google.maps.LatLng(f.latitude,f.longitude)),c++;for(;i>c;)g.pop(),c++}return h=!1},g=function(a){var c,d,e,f,g,i,j,k;if(h=!0,i=b,a){for("Polygon"===l.type?c=a.coordinates[0]:"LineString"===l.type&&(c=a.coordinates),d=0,j=i.getLength(),f=c.length,e=Math.min(j,f),g=void 0;e>d;)k=i.getAt(d),g=c[d],(k.lat()!==g[1]||k.lng()!==g[0])&&i.setAt(d,new google.maps.LatLng(g[1],g[0])),d++;for(;f>d;)g=c[d],i.push(new google.maps.LatLng(g[1],g[0])),d++;for(;j>d;)i.pop(),d++}return h=!1},c["static"]||(m=angular.isUndefined(l.type)?c.$watchCollection(d,j):c.$watch(d,g)),function(){return k&&(k(),k=null),m?(m(),m=null):void 0}}}])}.call(this),function(){angular.module("google-maps").factory("add-events",["$timeout",function(a){var b,c;return b=function(b,c,d){return google.maps.event.addListener(b,c,function(){return d.apply(this,arguments),a(function(){},!0)})},c=function(a,c,d){var e;return d?b(a,c,d):(e=[],angular.forEach(c,function(c,d){return e.push(b(a,d,c))}),function(){return angular.forEach(e,function(a){return google.maps.event.removeListener(a)}),e=null})}}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.child").factory("MarkerLabelChildModel",["BaseObject","GmapUtil",function(b,d){var e;return e=function(b){function e(b,c){this.destroy=a(this.destroy,this),this.draw=a(this.draw,this),this.setPosition=a(this.setPosition,this),this.setZIndex=a(this.setZIndex,this),this.setVisible=a(this.setVisible,this),this.setAnchor=a(this.setAnchor,this),this.setMandatoryStyles=a(this.setMandatoryStyles,this),this.setStyles=a(this.setStyles,this),this.setContent=a(this.setContent,this),this.setTitle=a(this.setTitle,this),this.getSharedCross=a(this.getSharedCross,this);var d,f,g;e.__super__.constructor.call(this),d=this,this.marker=b,this.marker.set("labelContent",c.labelContent),this.marker.set("labelAnchor",this.getLabelPositionPoint(c.labelAnchor)),this.marker.set("labelClass",c.labelClass||"labels"),this.marker.set("labelStyle",c.labelStyle||{opacity:100}),this.marker.set("labelInBackground",c.labelInBackground||!1),c.labelVisible||this.marker.set("labelVisible",!0),c.raiseOnDrag||this.marker.set("raiseOnDrag",!0),c.clickable||this.marker.set("clickable",!0),c.draggable||this.marker.set("draggable",!1),c.optimized||this.marker.set("optimized",!1),c.crossImage=null!=(f=c.crossImage)?f:document.location.protocol+"//maps.gstatic.com/intl/en_us/mapfiles/drag_cross_67_16.png",c.handCursor=null!=(g=c.handCursor)?g:document.location.protocol+"//maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur",this.markerLabel=new MarkerLabel_(this.marker,c.crossImage,c.handCursor),this.marker.set("setMap",function(a){return google.maps.Marker.prototype.setMap.apply(this,arguments),d.markerLabel.setMap(a)}),this.marker.setMap(this.marker.getMap())}return c(e,b),e.include(d),e.prototype.getSharedCross=function(a){return this.markerLabel.getSharedCross(a)},e.prototype.setTitle=function(){return this.markerLabel.setTitle()},e.prototype.setContent=function(){return this.markerLabel.setContent()},e.prototype.setStyles=function(){return this.markerLabel.setStyles()},e.prototype.setMandatoryStyles=function(){return this.markerLabel.setMandatoryStyles()},e.prototype.setAnchor=function(){return this.markerLabel.setAnchor()},e.prototype.setVisible=function(){return this.markerLabel.setVisible()},e.prototype.setZIndex=function(){return this.markerLabel.setZIndex()},e.prototype.setPosition=function(){return this.markerLabel.setPosition()},e.prototype.draw=function(){return this.markerLabel.draw()},e.prototype.destroy=function(){return null!=this.markerLabel.labelDiv_.parentNode&&null!=this.markerLabel.eventDiv_.parentNode?this.markerLabel.onRemove():void 0},e}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.child").factory("MarkerChildModel",["ModelKey","GmapUtil","Logger","$injector","MarkerEventHelper",function(b,d,e,f,g){var h;return h=function(b){function h(b,c,d,f,g,i,j,k){var l,m=this;this.model=b,this.parentScope=c,this.gMap=d,this.$timeout=f,this.defaults=g,this.doClick=i,this.gMarkerManager=j,this.idKey=k,this.watchDestroy=a(this.watchDestroy,this),this.setLabelOptions=a(this.setLabelOptions,this),this.isLabelDefined=a(this.isLabelDefined,this),this.setOptions=a(this.setOptions,this),this.setIcon=a(this.setIcon,this),this.setCoords=a(this.setCoords,this),this.destroy=a(this.destroy,this),this.maybeSetScopeValue=a(this.maybeSetScopeValue,this),this.createMarker=a(this.createMarker,this),this.setMyScope=a(this.setMyScope,this),l=this,this.model[this.idKey]&&(this.id=this.model[this.idKey]),this.iconKey=this.parentScope.icon,this.coordsKey=this.parentScope.coords,this.clickKey=this.parentScope.click(),this.labelContentKey=this.parentScope.labelContent,this.optionsKey=this.parentScope.options,this.labelOptionsKey=this.parentScope.labelOptions,h.__super__.constructor.call(this,this.parentScope.$new(!1)),this.scope.model=this.model,this.setMyScope(this.model,void 0,!0),this.createMarker(this.model),this.scope.$watch("model",function(a,b){return a!==b?m.setMyScope(a,b):void 0},!0),this.$log=e,this.$log.info(l),this.watchDestroy(this.scope)}return c(h,b),h.include(d),h.include(g),h.prototype.setMyScope=function(a,b,c){var d=this;return null==b&&(b=void 0),null==c&&(c=!1),this.maybeSetScopeValue("icon",a,b,this.iconKey,this.evalModelHandle,c,this.setIcon),this.maybeSetScopeValue("coords",a,b,this.coordsKey,this.evalModelHandle,c,this.setCoords),this.maybeSetScopeValue("labelContent",a,b,this.labelContentKey,this.evalModelHandle,c),_.isFunction(this.clickKey)&&f?this.scope.click=function(){return f.invoke(d.clickKey,void 0,{$markerModel:a})}:(this.maybeSetScopeValue("click",a,b,this.clickKey,this.evalModelHandle,c),this.createMarker(a,b,c))},h.prototype.createMarker=function(a,b,c){var d=this;return null==b&&(b=void 0),null==c&&(c=!1),this.maybeSetScopeValue("options",a,b,this.optionsKey,function(a,b){var c;return void 0===a?void 0:(c="self"===b?a:a[b],void 0===c?c=void 0===b?d.defaults:d.scope.options:c)},c,this.setOptions)},h.prototype.maybeSetScopeValue=function(a,b,c,d,e,f,g){var h,i;return null==g&&(g=void 0),void 0===c?(this.scope[a]=e(b,d),void(f||null!=g&&g(this.scope))):(i=e(c,d),h=e(b,d),h===i||this.scope[a]===h||(this.scope[a]=h,f)?void 0:(null!=g&&g(this.scope),this.gMarkerManager.draw()))},h.prototype.destroy=function(){return this.scope.$destroy()},h.prototype.setCoords=function(a){return a.$id===this.scope.$id&&void 0!==this.gMarker?null!=a.coords?this.validateCoords(this.scope.coords)?(this.gMarker.setPosition(this.getCoords(a.coords)),this.gMarker.setVisible(this.validateCoords(a.coords)),this.gMarkerManager.remove(this.gMarker),this.gMarkerManager.add(this.gMarker)):void this.$log.error("MarkerChildMarker cannot render marker as scope.coords as no position on marker: "+JSON.stringify(this.model)):this.gMarkerManager.remove(this.gMarker):void 0},h.prototype.setIcon=function(a){return a.$id===this.scope.$id&&void 0!==this.gMarker?(this.gMarkerManager.remove(this.gMarker),this.gMarker.setIcon(a.icon),this.gMarkerManager.add(this.gMarker),this.gMarker.setPosition(this.getCoords(a.coords)),this.gMarker.setVisible(this.validateCoords(a.coords))):void 0},h.prototype.setOptions=function(a){var b,c=this;if(a.$id===this.scope.$id&&(null!=this.gMarker&&(this.gMarkerManager.remove(this.gMarker),delete this.gMarker),null!=(b=a.coords)?b:"function"==typeof a.icon?a.icon(null!=a.options):void 0))return this.opts=this.createMarkerOptions(a.coords,a.icon,a.options),delete this.gMarker,this.gMarker=this.isLabelDefined(a)?new MarkerWithLabel(this.setLabelOptions(this.opts,a)):new google.maps.Marker(this.opts),this.setEvents(this.gMarker,this.parentScope,this.model),this.id&&(this.gMarker.key=this.id),this.gMarkerManager.add(this.gMarker),google.maps.event.addListener(this.gMarker,"click",function(){return c.doClick&&null!=c.scope.click?c.scope.click():void 0})},h.prototype.isLabelDefined=function(a){return null!=a.labelContent},h.prototype.setLabelOptions=function(a,b){return a.labelAnchor=this.getLabelPositionPoint(b.labelAnchor),a.labelClass=b.labelClass,a.labelContent=b.labelContent,a},h.prototype.watchDestroy=function(a){var b=this;return a.$on("$destroy",function(){var a,c;return null!=b.gMarker&&(google.maps.event.clearListeners(b.gMarker,"click"),(null!=(c=b.parentScope)?c.events:void 0)&&_.isArray(b.parentScope.events)&&b.parentScope.events.forEach(function(a,b){return google.maps.event.clearListeners(this.gMarker,b)}),b.gMarkerManager.remove(b.gMarker,!0),delete b.gMarker),a=void 0})},h}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api").factory("PolylineChildModel",["BaseObject","Logger","$timeout","array-sync","GmapUtil",function(b,d,e,f,g){var h,i;return h=d,i=function(b){function d(b,c,d,e,g){var i,j,k,l=this;this.scope=b,this.attrs=c,this.map=d,this.defaults=e,this.model=g,this.buildOpts=a(this.buildOpts,this),k=this,j=this.convertPathPoints(this.scope.path),this.polyline=new google.maps.Polyline(this.buildOpts(j)),this.isTrue(this.attrs.fit)&&extendMapBounds(d,j),!b["static"]&&angular.isDefined(b.editable)&&b.$watch("editable",function(a,b){return a!==b?this.polyline.setEditable(a):void 0}),angular.isDefined(b.draggable)&&b.$watch("draggable",function(a,b){return a!==b?this.polyline.setDraggable(a):void 0}),angular.isDefined(b.visible)&&b.$watch("visible",function(a,b){return a!==b?this.polyline.setVisible(a):void 0}),angular.isDefined(b.geodesic)&&b.$watch("geodesic",function(a,b){return a!==b?this.polyline.setOptions(this.buildOpts(this.polyline.getPath())):void 0 -}),angular.isDefined(b.stroke)&&angular.isDefined(b.stroke.weight)&&b.$watch("stroke.weight",function(a,b){return a!==b?this.polyline.setOptions(this.buildOpts(this.polyline.getPath())):void 0}),angular.isDefined(b.stroke)&&angular.isDefined(b.stroke.color)&&b.$watch("stroke.color",function(a,b){return a!==b?this.polyline.setOptions(this.buildOpts(this.polyline.getPath())):void 0}),angular.isDefined(b.icons)&&b.$watch("icons",function(a,b){return a!==b?this.polyline.setOptions(this.buildOpts(this.polyline.getPath())):void 0}),i=f(this.polyline.getPath(),b,"path"),b.$on("$destroy",function(){return l.polyline.setMap(null),l.polyline=null,l.scope=null,i?(i(),i=null):void 0}),h.info(this)}return c(d,b),d.include(g),d.prototype.buildOpts=function(a){var b,c=this;return b=angular.extend({},this.defaults,{map:this.map,path:a,icons:this.scope.icons,strokeColor:this.scope.stroke&&this.scope.stroke.color,strokeOpacity:this.scope.stroke&&this.scope.stroke.opacity,strokeWeight:this.scope.stroke&&this.scope.stroke.weight}),angular.forEach({clickable:!0,draggable:!1,editable:!1,geodesic:!1,visible:!0,"static":!1},function(a,d){return b[d]=angular.isUndefined(c.scope[d])||null===c.scope[d]?a:c.scope[d]}),b["static"]&&(b.editable=!1),b},d.prototype.destroy=function(){return this.scope.$destroy()},d}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.child").factory("WindowChildModel",["BaseObject","GmapUtil","Logger","$compile","$http","$templateCache",function(b,d,e,f,g,h){var i;return i=function(b){function i(b,c,d,f,g,h,i,j,k){this.model=b,this.scope=c,this.opts=d,this.isIconVisibleOnClick=f,this.mapCtrl=g,this.markerCtrl=h,this.element=i,this.needToManualDestroy=null!=j?j:!1,this.markerIsVisibleAfterWindowClose=null!=k?k:!0,this.destroy=a(this.destroy,this),this.remove=a(this.remove,this),this.hideWindow=a(this.hideWindow,this),this.getLatestPosition=a(this.getLatestPosition,this),this.showWindow=a(this.showWindow,this),this.handleClick=a(this.handleClick,this),this.watchCoords=a(this.watchCoords,this),this.watchShow=a(this.watchShow,this),this.createGWin=a(this.createGWin,this),this.watchElement=a(this.watchElement,this),this.googleMapsHandles=[],this.$log=e,this.createGWin(),null!=this.markerCtrl&&this.markerCtrl.setClickable(!0),this.handleClick(),this.watchElement(),this.watchShow(),this.watchCoords(),this.$log.info(this)}return c(i,b),i.include(d),i.prototype.watchElement=function(){var a=this;return this.scope.$watch(function(){var b;if(a.element&&a.html)return a.html!==a.element.html()&&a.gWin?(null!=(b=a.opts)&&(b.content=void 0),a.remove(),a.createGWin(),a.showHide()):void 0})},i.prototype.createGWin=function(){var a,b=this;return null==this.gWin&&(a={},null!=this.opts&&(this.scope.coords&&(this.opts.position=this.getCoords(this.scope.coords)),a=this.opts),this.element&&(this.html=_.isObject(this.element)?this.element.html():this.element),this.opts=this.createWindowOptions(this.markerCtrl,this.scope,this.html,a)),null==this.opts||this.gWin?void 0:(this.gWin=this.opts.boxClass&&window.InfoBox&&"function"==typeof window.InfoBox?new window.InfoBox(this.opts):new google.maps.InfoWindow(this.opts),this.googleMapsHandles.push(google.maps.event.addListener(this.gWin,"closeclick",function(){var a;return null!=(a=b.markerCtrl)&&a.setVisible(b.markerIsVisibleAfterWindowClose),b.gWin.isOpen(!1),null!=b.scope.closeClick?b.scope.closeClick():void 0})))},i.prototype.watchShow=function(){var a=this;return this.scope.$watch("show",function(b,c){return b!==c?b?a.showWindow():a.hideWindow():null!=a.gWin&&b&&!a.gWin.getMap()?a.showWindow():void 0},!0)},i.prototype.watchCoords=function(){var a,b=this;return a=null!=this.markerCtrl?this.scope.$parent:this.scope,a.$watch("coords",function(a,c){var d;if(a!==c){if(null==a)return b.hideWindow();if(!b.validateCoords(a))return void b.$log.error("WindowChildMarker cannot render marker as scope.coords as no position on marker: "+JSON.stringify(b.model));if(d=b.getCoords(a),b.gWin.setPosition(d),b.opts)return b.opts.position=d}},!0)},i.prototype.handleClick=function(){var a=this;return null!=this.markerCtrl?this.googleMapsHandles.push(google.maps.event.addListener(this.markerCtrl,"click",function(){var b;return null==a.gWin&&a.createGWin(),b=a.markerCtrl.getPosition(),null!=a.gWin&&(a.gWin.setPosition(b),a.opts&&(a.opts.position=b),a.showWindow()),a.initialMarkerVisibility=a.markerCtrl.getVisible(),a.markerCtrl.setVisible(a.isIconVisibleOnClick)})):void 0},i.prototype.showWindow=function(){var a,b=this;return a=function(){return!b.gWin||!b.scope.show&&null!=b.scope.show||b.gWin.isOpen()?void 0:b.gWin.open(b.mapCtrl)},this.scope.templateUrl?(this.gWin&&g.get(this.scope.templateUrl,{cache:h}).then(function(a){var c,d;return d=b.scope.$new(),angular.isDefined(b.scope.templateParameter)&&(d.parameter=b.scope.templateParameter),c=f(a.data)(d),b.gWin.setContent(c[0])}),a()):a()},i.prototype.showHide=function(){return this.scope.show?this.showWindow():this.hideWindow()},i.prototype.getLatestPosition=function(){return null!=this.gWin&&null!=this.markerCtrl?this.gWin.setPosition(this.markerCtrl.getPosition()):void 0},i.prototype.hideWindow=function(){return null!=this.gWin&&this.gWin.isOpen()?this.gWin.close():void 0},i.prototype.remove=function(){return this.hideWindow(),_.each(this.googleMapsHandles,function(a){return google.maps.event.removeListener(a)}),this.googleMapsHandles.length=0,delete this.gWin},i.prototype.destroy=function(a){var b;return null==a&&(a=!1),this.remove(),null!=this.scope&&(this.needToManualDestroy||a)&&this.scope.$destroy(),b=void 0},i}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.parent").factory("IMarkerParentModel",["ModelKey","Logger",function(b,d){var e;return e=function(b){function e(b,c,f,g,h){var i,j=this;if(this.scope=b,this.element=c,this.attrs=f,this.mapCtrl=g,this.$timeout=h,this.linkInit=a(this.linkInit,this),this.onDestroy=a(this.onDestroy,this),this.onWatch=a(this.onWatch,this),this.watch=a(this.watch,this),this.validateScope=a(this.validateScope,this),this.onTimeOut=a(this.onTimeOut,this),e.__super__.constructor.call(this,this.scope),i=this,this.$log=d,!this.validateScope(b))throw new String("Unable to construct IMarkerParentModel due to invalid scope");this.doClick=angular.isDefined(f.click),null!=b.options&&(this.DEFAULTS=b.options),this.$timeout(function(){return j.onTimeOut(b),j.watch("coords",j.scope),j.watch("icon",j.scope),j.watch("options",j.scope),b.$on("$destroy",function(){return j.onDestroy(b)})})}return c(e,b),e.prototype.DEFAULTS={},e.prototype.onTimeOut=function(){},e.prototype.validateScope=function(a){var b;return null==a?(this.$log.error(this.constructor.name+": invalid scope used"),!1):(b=null!=a.coords,b?b:(this.$log.error(this.constructor.name+": no valid coords attribute found"),!1))},e.prototype.watch=function(a,b){var c,d=this;return c=function(c,e){return c!==e?d.onWatch(a,b,c,e):void 0},b.$watch(a,c,!0)},e.prototype.onWatch=function(){throw new String("OnWatch Not Implemented!!")},e.prototype.onDestroy=function(){throw new String("OnDestroy Not Implemented!!")},e.prototype.linkInit=function(){throw new String("LinkInit Not Implemented!!")},e}(b)}])}.call(this),function(){var a={}.hasOwnProperty,b=function(b,c){function d(){this.constructor=b}for(var e in c)a.call(c,e)&&(b[e]=c[e]);return d.prototype=c.prototype,b.prototype=new d,b.__super__=c.prototype,b};angular.module("google-maps.directives.api.models.parent").factory("IWindowParentModel",["ModelKey","GmapUtil","Logger",function(a,c,d){var e;return e=function(a){function e(a,b,c,f,g,h,i,j){var k;e.__super__.constructor.call(this,a),k=this,this.$log=d,this.$timeout=g,this.$compile=h,this.$http=i,this.$templateCache=j,null!=a.options&&(this.DEFAULTS=a.options)}return b(e,a),e.include(c),e.prototype.DEFAULTS={},e}(a)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.parent").factory("LayerParentModel",["BaseObject","Logger",function(b,d){var e;return e=function(b){function e(b,c,e,f,g,h,i){var j=this;return this.scope=b,this.element=c,this.attrs=e,this.mapCtrl=f,this.$timeout=g,this.onLayerCreated=null!=h?h:void 0,this.$log=null!=i?i:d,this.createGoogleLayer=a(this.createGoogleLayer,this),null==this.attrs.type?void this.$log.info("type attribute for the layer directive is mandatory. Layer creation aborted!!"):(this.createGoogleLayer(),this.gMap=void 0,this.doShow=!0,void this.$timeout(function(){return j.gMap=f.getMap(),angular.isDefined(j.attrs.show)&&(j.doShow=j.scope.show),j.doShow&&null!=j.gMap&&j.layer.setMap(j.gMap),j.scope.$watch("show",function(a,b){return a!==b?(j.doShow=a,j.layer.setMap(a?j.gMap:null)):void 0},!0),j.scope.$watch("options",function(a,b){return a!==b?(j.layer.setMap(null),j.layer=null,j.createGoogleLayer()):void 0},!0),j.scope.$on("$destroy",function(){return j.layer.setMap(null)})}))}return c(e,b),e.prototype.createGoogleLayer=function(){var a=this;return this.layer=null==this.attrs.options?void 0===this.attrs.namespace?new google.maps[this.attrs.type]:new google.maps[this.attrs.namespace][this.attrs.type]:void 0===this.attrs.namespace?new google.maps[this.attrs.type](this.scope.options):new google.maps[this.attrs.namespace][this.attrs.type](this.scope.options),this.$timeout(function(){var b;return null!=a.layer&&null!=a.onLayerCreated&&(b=a.onLayerCreated(a.scope,a.layer))?b(a.layer):void 0})},e}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.parent").factory("MarkerParentModel",["IMarkerParentModel","GmapUtil","MarkerEventHelper",function(b,d,e){var f;return f=function(b){function f(b,c,d,e,g,h,i){var j;this.gMarkerManager=h,this.doFit=i,this.onDestroy=a(this.onDestroy,this),this.setGMarker=a(this.setGMarker,this),this.onWatch=a(this.onWatch,this),this.onTimeOut=a(this.onTimeOut,this),f.__super__.constructor.call(this,b,c,d,e,g),j=this}return c(f,b),f.include(d),f.include(e),f.prototype.onTimeOut=function(a){var b,c=this;return b=this.createMarkerOptions(a.coords,a.icon,a.options,this.mapCtrl.getMap()),this.setGMarker(new google.maps.Marker(b)),google.maps.event.addListener(this.scope.gMarker,"click",function(){return c.doClick&&null!=a.click?c.$timeout(function(){return c.scope.click()}):void 0}),this.setEvents(this.scope.gMarker,a,a),this.$log.info(this)},f.prototype.onWatch=function(a,b){switch(a){case"coords":return this.validateCoords(b.coords)&&null!=this.scope.gMarker?(this.scope.gMarker.setMap(this.mapCtrl.getMap()),this.scope.gMarker.setPosition(this.getCoords(b.coords)),this.scope.gMarker.setVisible(this.validateCoords(b.coords)),this.scope.gMarker.setOptions(b.options)):this.scope.gMarker.setMap(null);case"icon":if(null!=b.icon&&this.validateCoords(b.coords)&&null!=this.scope.gMarker)return this.scope.gMarker.setOptions(b.options),this.scope.gMarker.setIcon(b.icon),this.scope.gMarker.setMap(null),this.scope.gMarker.setMap(this.mapCtrl.getMap()),this.scope.gMarker.setPosition(this.getCoords(b.coords)),this.scope.gMarker.setVisible(this.validateCoords(b.coords));break;case"options":if(this.validateCoords(b.coords)&&null!=b.icon&&b.options)return null!=this.scope.gMarker&&this.scope.gMarker.setMap(null),this.setGMarker(new google.maps.Marker(this.createMarkerOptions(b.coords,b.icon,b.options,this.mapCtrl.getMap())))}},f.prototype.setGMarker=function(a){return this.scope.gMarker&&(delete this.scope.gMarker,this.gMarkerManager.remove(this.scope.gMarker,!1)),this.scope.gMarker=a,this.scope.gMarker&&(this.gMarkerManager.add(this.scope.gMarker,!1),this.doFit)?this.gMarkerManager.fit():void 0},f.prototype.onDestroy=function(){var a;return this.scope.gMarker?(this.scope.gMarker.setMap(null),this.gMarkerManager.remove(this.scope.gMarker,!1),delete this.scope.gMarker,a=void 0):void(a=void 0)},f}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.parent").factory("MarkersParentModel",["IMarkerParentModel","ModelsWatcher","PropMap","MarkerChildModel","ClustererMarkerManager","MarkerManager",function(b,d,e,f,g,h){var i;return i=function(b){function i(b,c,d,f,g){this.onDestroy=a(this.onDestroy,this),this.newChildMarker=a(this.newChildMarker,this),this.pieceMealMarkers=a(this.pieceMealMarkers,this),this.reBuildMarkers=a(this.reBuildMarkers,this),this.createMarkersFromScratch=a(this.createMarkersFromScratch,this),this.validateScope=a(this.validateScope,this),this.onWatch=a(this.onWatch,this),this.onTimeOut=a(this.onTimeOut,this);var h,j=this;i.__super__.constructor.call(this,b,c,d,f,g),h=this,this.scope.markerModels=new e,this.$timeout=g,this.$log.info(this),this.doRebuildAll=null!=this.scope.doRebuildAll?this.scope.doRebuildAll:!0,this.setIdKey(b),this.scope.$watch("doRebuildAll",function(a,b){return a!==b?j.doRebuildAll=a:void 0})}return c(i,b),i.include(d),i.prototype.onTimeOut=function(a){return this.watch("models",a),this.watch("doCluster",a),this.watch("clusterOptions",a),this.watch("clusterEvents",a),this.watch("fit",a),this.watch("idKey",a),this.gMarkerManager=void 0,this.createMarkersFromScratch(a)},i.prototype.onWatch=function(a,b,c,d){return"idKey"===a&&c!==d&&(this.idKey=c),this.doRebuildAll?this.reBuildMarkers(b):this.pieceMealMarkers(b)},i.prototype.validateScope=function(a){var b;return b=angular.isUndefined(a.models)||void 0===a.models,b&&this.$log.error(this.constructor.name+": no valid models attribute found"),i.__super__.validateScope.call(this,a)||b},i.prototype.createMarkersFromScratch=function(a){var b=this;return a.doCluster?(a.clusterEvents&&(this.clusterInternalOptions=_.once(function(){var c,d,e,f;return c=b,b.origClusterEvents?void 0:(b.origClusterEvents={click:null!=(d=a.clusterEvents)?d.click:void 0,mouseout:null!=(e=a.clusterEvents)?e.mouseout:void 0,mouseover:null!=(f=a.clusterEvents)?f.mouseover:void 0},_.extend(a.clusterEvents,{click:function(a){return c.maybeExecMappedEvent(a,"click")},mouseout:function(a){return c.maybeExecMappedEvent(a,"mouseout")},mouseover:function(a){return c.maybeExecMappedEvent(a,"mouseover")}}))})()),a.clusterOptions||a.clusterEvents?void 0===this.gMarkerManager?this.gMarkerManager=new g(this.mapCtrl.getMap(),void 0,a.clusterOptions,this.clusterInternalOptions):this.gMarkerManager.opt_options!==a.clusterOptions&&(this.gMarkerManager=new g(this.mapCtrl.getMap(),void 0,a.clusterOptions,this.clusterInternalOptions)):this.gMarkerManager=new g(this.mapCtrl.getMap())):this.gMarkerManager=new h(this.mapCtrl.getMap()),_async.each(a.models,function(c){return b.newChildMarker(c,a)},function(){return b.gMarkerManager.draw(),a.fit?b.gMarkerManager.fit():void 0})},i.prototype.reBuildMarkers=function(a){return a.doRebuild||void 0===a.doRebuild?(this.onDestroy(a),this.createMarkersFromScratch(a)):void 0},i.prototype.pieceMealMarkers=function(a){var b=this;return null!=this.scope.models&&this.scope.models.length>0&&this.scope.markerModels.length>0?this.figureOutState(this.idKey,a,this.scope.markerModels,this.modelKeyComparison,function(c){var d;return d=c,_async.each(d.removals,function(a){return null!=a?(a.destroy(),b.scope.markerModels.remove(a.id)):void 0},function(){return _async.each(d.adds,function(c){return b.newChildMarker(c,a)},function(){return b.gMarkerManager.draw(),a.markerModels=b.scope.markerModels})})}):this.reBuildMarkers(a)},i.prototype.newChildMarker=function(a,b){var c;return null==a[this.idKey]?void this.$log.error("Marker model has no id to assign a child to. This is required for performance. Please assign id, or redirect id to a different key."):(this.$log.info("child",c,"markers",this.scope.markerModels),c=new f(a,b,this.mapCtrl,this.$timeout,this.DEFAULTS,this.doClick,this.gMarkerManager,this.idKey),this.scope.markerModels.put(a[this.idKey],c),c)},i.prototype.onDestroy=function(){return _.each(this.scope.markerModels.values(),function(a){return null!=a?a.destroy():void 0}),delete this.scope.markerModels,this.scope.markerModels=new e,null!=this.gMarkerManager?this.gMarkerManager.clear():void 0},i.prototype.maybeExecMappedEvent=function(a,b){var c,d;return _.isFunction(null!=(d=this.scope.clusterEvents)?d[b]:void 0)&&(c=this.mapClusterToMarkerModels(a),this.origClusterEvents[b])?this.origClusterEvents[b](c.cluster,c.mapped):void 0},i.prototype.mapClusterToMarkerModels=function(a){var b,c,d=this;return b=a.getMarkers(),c=b.map(function(a){return d.scope.markerModels[a.key].model}),{cluster:a,mapped:c}},i}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.parent").factory("PolylinesParentModel",["$timeout","Logger","ModelKey","ModelsWatcher","PropMap","PolylineChildModel",function(b,d,e,f,g,h){var i;return i=function(e){function i(c,e,f,h,j){var k,l=this;this.scope=c,this.element=e,this.attrs=f,this.gMap=h,this.defaults=j,this.modelKeyComparison=a(this.modelKeyComparison,this),this.setChildScope=a(this.setChildScope,this),this.createChild=a(this.createChild,this),this.pieceMeal=a(this.pieceMeal,this),this.createAllNew=a(this.createAllNew,this),this.watchIdKey=a(this.watchIdKey,this),this.createChildScopes=a(this.createChildScopes,this),this.watchOurScope=a(this.watchOurScope,this),this.watchDestroy=a(this.watchDestroy,this),this.rebuildAll=a(this.rebuildAll,this),this.doINeedToWipe=a(this.doINeedToWipe,this),this.watchModels=a(this.watchModels,this),this.watch=a(this.watch,this),i.__super__.constructor.call(this,c),k=this,this.$log=d,this.plurals=new g,this.scopePropNames=["path","stroke","clickable","draggable","editable","geodesic","icons","visible"],_.each(this.scopePropNames,function(a){return this[a+"Key"]=void 0}),this.models=void 0,this.firstTime=!0,this.$log.info(this),b(function(){return l.watchOurScope(c),l.createChildScopes()})}return c(i,e),i.include(f),i.prototype.watch=function(a,b,c){var d=this;return a.$watch(b,function(a,e){return a!==e?(d[c]="function"==typeof a?a():a,_async.each(_.values(d.plurals),function(a){return a.scope[b]="self"===d[c]?a:a[d[c]]},function(){})):void 0})},i.prototype.watchModels=function(a){var b=this;return a.$watch("models",function(c,d){return _.isEqual(c,d)?void 0:b.doINeedToWipe(c)?b.rebuildAll(a,!0,!0):b.createChildScopes(!1)},!0)},i.prototype.doINeedToWipe=function(a){var b;return b=null!=a?0===a.length:!0,this.plurals.length>0&&b},i.prototype.rebuildAll=function(a,b,c){var d=this;return _async.each(this.plurals.values(),function(a){return a.destroy()},function(){return c&&delete d.plurals,d.plurals=new g,b?d.createChildScopes():void 0})},i.prototype.watchDestroy=function(a){var b=this;return a.$on("$destroy",function(){return b.rebuildAll(a,!1,!0)})},i.prototype.watchOurScope=function(a){var b=this;return _.each(this.scopePropNames,function(c){var d;return d=c+"Key",b[d]="function"==typeof a[c]?a[c]():a[c],b.watch(a,c,d)})},i.prototype.createChildScopes=function(a){return null==a&&(a=!0),angular.isUndefined(this.scope.models)?void this.$log.error("No models to create polylines from! I Need direct models!"):null!=this.gMap&&null!=this.scope.models?(this.watchIdKey(this.scope),a?this.createAllNew(this.scope,!1):this.pieceMeal(this.scope,!1)):void 0},i.prototype.watchIdKey=function(a){var b=this;return this.setIdKey(a),a.$watch("idKey",function(c,d){return c!==d&&null==c?(b.idKey=c,b.rebuildAll(a,!0,!0)):void 0})},i.prototype.createAllNew=function(a,b){var c=this;return null==b&&(b=!1),this.models=a.models,this.firstTime&&(this.watchModels(a),this.watchDestroy(a)),_async.each(a.models,function(a){return c.createChild(a,c.gMap)},function(){return c.firstTime=!1})},i.prototype.pieceMeal=function(a,b){var c=this;return null==b&&(b=!0),this.models=a.models,null!=a&&null!=a.models&&a.models.length>0&&this.plurals.length>0?this.figureOutState(this.idKey,a,this.plurals,this.modelKeyComparison,function(a){var b;return b=a,_async.each(b.removals,function(a){var b;return b=c.plurals[a],null!=b?(b.destroy(),c.plurals.remove(a)):void 0},function(){return _async.each(b.adds,function(a){return c.createChild(a,c.gMap)},function(){})})}):this.rebuildAll(this.scope,!0,!0)},i.prototype.createChild=function(a,b){var c,d,e=this;return d=this.scope.$new(!1),this.setChildScope(d,a),d.$watch("model",function(a,b){return a!==b?e.setChildScope(d,a):void 0},!0),d["static"]=this.scope["static"],c=new h(d,this.attrs,b,this.defaults,a),null==a[this.idKey]?void this.$log.error("Polyline model has no id to assign a child to. This is required for performance. Please assign id, or redirect id to a different key."):(this.plurals.put(a[this.idKey],c),c)},i.prototype.setChildScope=function(a,b){var c=this;return _.each(this.scopePropNames,function(d){var e,f;return e=d+"Key",f="self"===c[e]?b:b[c[e]],f!==a[d]?a[d]=f:void 0}),a.model=b},i.prototype.modelKeyComparison=function(a,b){return _.isEqual(this.evalModelHandle(a,this.scope.path),this.evalModelHandle(b,this.scope.path))},i}(e)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.parent").factory("WindowsParentModel",["IWindowParentModel","ModelsWatcher","PropMap","WindowChildModel","Linked",function(b,d,e,f,g){var h;return h=function(b){function h(b,c,d,f,i,j,k,l,m){var n,o=this;this.$interpolate=m,this.interpolateContent=a(this.interpolateContent,this),this.setChildScope=a(this.setChildScope,this),this.createWindow=a(this.createWindow,this),this.setContentKeys=a(this.setContentKeys,this),this.pieceMealWindows=a(this.pieceMealWindows,this),this.createAllNewWindows=a(this.createAllNewWindows,this),this.watchIdKey=a(this.watchIdKey,this),this.createChildScopesWindows=a(this.createChildScopesWindows,this),this.watchOurScope=a(this.watchOurScope,this),this.watchDestroy=a(this.watchDestroy,this),this.rebuildAll=a(this.rebuildAll,this),this.doINeedToWipe=a(this.doINeedToWipe,this),this.watchModels=a(this.watchModels,this),this.watch=a(this.watch,this),h.__super__.constructor.call(this,b,c,d,f,i,j,k,l),n=this,this.windows=new e,this.scopePropNames=["show","coords","templateUrl","templateParameter","isIconVisibleOnClick","closeClick"],_.each(this.scopePropNames,function(a){return this[a+"Key"]=void 0}),this.linked=new g(b,c,d,f),this.models=void 0,this.contentKeys=void 0,this.isIconVisibleOnClick=void 0,this.firstTime=!0,this.$log.info(n),this.parentScope=void 0,this.$timeout(function(){return o.watchOurScope(b),o.doRebuildAll=null!=o.scope.doRebuildAll?o.scope.doRebuildAll:!0,b.$watch("doRebuildAll",function(a,b){return a!==b?o.doRebuildAll=a:void 0}),o.createChildScopesWindows()},50)}return c(h,b),h.include(d),h.prototype.watch=function(a,b,c){var d=this;return a.$watch(b,function(a,e){return a!==e?(d[c]="function"==typeof a?a():a,_async.each(_.values(d.windows),function(a){return a.scope[b]="self"===d[c]?a:a[d[c]]},function(){})):void 0})},h.prototype.watchModels=function(a){var b=this;return a.$watch("models",function(c,d){return _.isEqual(c,d)?void 0:b.doRebuildAll||b.doINeedToWipe(c)?b.rebuildAll(a,!0,!0):b.createChildScopesWindows(!1)})},h.prototype.doINeedToWipe=function(a){var b;return b=null!=a?0===a.length:!0,this.windows.length>0&&b},h.prototype.rebuildAll=function(a,b,c){var d=this;return _async.each(this.windows.values(),function(a){return a.destroy()},function(){return c&&delete d.windows,d.windows=new e,b?d.createChildScopesWindows():void 0})},h.prototype.watchDestroy=function(a){var b=this;return a.$on("$destroy",function(){return b.rebuildAll(a,!1,!0)})},h.prototype.watchOurScope=function(a){var b=this;return _.each(this.scopePropNames,function(c){var d;return d=c+"Key",b[d]="function"==typeof a[c]?a[c]():a[c],b.watch(a,c,d)})},h.prototype.createChildScopesWindows=function(a){var b,c;return null==a&&(a=!0),this.isIconVisibleOnClick=!0,angular.isDefined(this.linked.attrs.isiconvisibleonclick)&&(this.isIconVisibleOnClick=this.linked.scope.isIconVisibleOnClick),this.gMap=this.linked.ctrls[0].getMap(),null!=this.linked.ctrls[1]&&(b=this.linked.ctrls.length>1?this.linked.ctrls[1].getMarkersScope():void 0),c=angular.isUndefined(this.linked.scope.models),!c||void 0!==b&&void 0!==b.markerModels&&void 0!==b.models?null!=this.gMap?null!=this.linked.scope.models?(this.watchIdKey(this.linked.scope),a?this.createAllNewWindows(this.linked.scope,!1):this.pieceMealWindows(this.linked.scope,!1)):(this.parentScope=b,this.watchIdKey(this.parentScope),a?this.createAllNewWindows(b,!0,"markerModels",!1):this.pieceMealWindows(b,!0,"markerModels",!1)):void 0:void this.$log.error("No models to create windows from! Need direct models or models derrived from markers!")},h.prototype.watchIdKey=function(a){var b=this;return this.setIdKey(a),a.$watch("idKey",function(c,d){return c!==d&&null==c?(b.idKey=c,b.rebuildAll(a,!0,!0)):void 0})},h.prototype.createAllNewWindows=function(a,b,c,d){var e=this;return null==c&&(c="models"),null==d&&(d=!1),this.models=a.models,this.firstTime&&(this.watchModels(a),this.watchDestroy(a)),this.setContentKeys(a.models),_async.each(a.models,function(d){var f;return f=b?a[c][[d[e.idKey]]].gMarker:void 0,e.createWindow(d,f,e.gMap)},function(){return e.firstTime=!1})},h.prototype.pieceMealWindows=function(a,b,c,d){var e=this;return null==c&&(c="models"),null==d&&(d=!0),this.models=a.models,null!=a&&null!=a.models&&a.models.length>0&&this.windows.length>0?this.figureOutState(this.idKey,a,this.windows,this.modelKeyComparison,function(b){var d;return d=b,_async.each(d.removals,function(a){return null!=a?(a.destroy(),e.windows.remove(a.id)):void 0},function(){return _async.each(d.adds,function(b){var d;return d=a[c][b[e.idKey]].gMarker,e.createWindow(b,d,e.gMap)},function(){})})}):this.rebuildAll(this.scope,!0,!0)},h.prototype.setContentKeys=function(a){return a.length>0?this.contentKeys=Object.keys(a[0]):void 0},h.prototype.createWindow=function(a,b,c){var d,e,g,h,i=this;return e=this.linked.scope.$new(!1),this.setChildScope(e,a),e.$watch("model",function(a,b){return a!==b?i.setChildScope(e,a):void 0},!0),g={html:function(){return i.interpolateContent(i.linked.element.html(),a)}},h=this.createWindowOptions(b,e,g.html(),this.DEFAULTS),d=new f(a,e,h,this.isIconVisibleOnClick,c,b,g,!0,!0),null==a[this.idKey]?void this.$log.error("Window model has no id to assign a child to. This is required for performance. Please assign id, or redirect id to a different key."):(this.windows.put(a[this.idKey],d),d)},h.prototype.setChildScope=function(a,b){var c=this;return _.each(this.scopePropNames,function(d){var e,f;return e=d+"Key",f="self"===c[e]?b:b[c[e]],f!==a[d]?a[d]=f:void 0}),a.model=b},h.prototype.interpolateContent=function(a,b){var c,d,e,f,g,h;if(void 0!==this.contentKeys&&0!==this.contentKeys.length){for(c=this.$interpolate(a),d={},h=this.contentKeys,f=0,g=h.length;g>f;f++)e=h[f],d[e]=b[e];return c(d)}},h}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api").factory("ILabel",["BaseObject","Logger",function(b,d){var e;return e=function(b){function e(b){this.link=a(this.link,this);var c;c=this,this.restrict="ECMA",this.replace=!0,this.template=void 0,this.require=void 0,this.transclude=!0,this.priority=-100,this.scope={labelContent:"=content",labelAnchor:"@anchor",labelClass:"@class",labelStyle:"=style"},this.$log=d,this.$timeout=b}return c(e,b),e.prototype.link=function(){throw new Exception("Not Implemented!!")},e}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api").factory("IMarker",["Logger","BaseObject",function(b,d){var e;return e=function(d){function e(c){this.link=a(this.link,this);var d;d=this,this.$log=b,this.$timeout=c,this.restrict="ECMA",this.require="^googleMap",this.priority=-1,this.transclude=!0,this.replace=!0,this.scope={coords:"=coords",icon:"=icon",click:"&click",options:"=options",events:"=events",fit:"=fit"}}return c(e,d),e.prototype.controller=["$scope","$element",function(){throw new Exception("Not Implemented!!")}],e.prototype.link=function(){throw new Exception("Not implemented!!")},e}(d)}])}.call(this),function(){var a={}.hasOwnProperty,b=function(b,c){function d(){this.constructor=b}for(var e in c)a.call(c,e)&&(b[e]=c[e]);return d.prototype=c.prototype,b.prototype=new d,b.__super__=c.prototype,b};angular.module("google-maps.directives.api").factory("IPolyline",["GmapUtil","BaseObject","Logger",function(a,c,d){var e;return e=function(c){function e(){var a;a=this}return b(e,c),e.include(a),e.prototype.restrict="ECA",e.prototype.replace=!0,e.prototype.require="^googleMap",e.prototype.scope={path:"=",stroke:"=",clickable:"=",draggable:"=",editable:"=",geodesic:"=",icons:"=",visible:"=","static":"="},e.prototype.DEFAULTS={},e.prototype.$log=d,e}(c)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api").factory("IWindow",["BaseObject","ChildEvents","Logger",function(b,d,e){var f;return f=function(b){function f(b,c,d,f){var g;this.$timeout=b,this.$compile=c,this.$http=d,this.$templateCache=f,this.link=a(this.link,this),g=this,this.restrict="ECMA",this.template=void 0,this.transclude=!0,this.priority=-100,this.require=void 0,this.replace=!0,this.scope={coords:"=coords",show:"=show",templateUrl:"=templateurl",templateParameter:"=templateparameter",isIconVisibleOnClick:"=isiconvisibleonclick",closeClick:"&closeclick",options:"=options"},this.$log=e}return c(f,b),f.include(d),f.prototype.link=function(){throw new Exception("Not Implemented!!")},f}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api").factory("Map",["$timeout","Logger","GmapUtil","BaseObject",function(b,d,e,f){"use strict"; -var g,h,i;return g=d,h={mapTypeId:google.maps.MapTypeId.ROADMAP},i=function(b){function d(){this.link=a(this.link,this);var b;b=this}return c(d,b),d.include(e),d.prototype.restrict="ECMA",d.prototype.transclude=!0,d.prototype.replace=!1,d.prototype.template='
',d.prototype.scope={center:"=center",zoom:"=zoom",dragging:"=dragging",control:"=",windows:"=windows",options:"=options",events:"=events",styles:"=styles",bounds:"=bounds"},d.prototype.controller=["$scope",function(a){return{getMap:function(){return a.map}}}],d.prototype.link=function(a,b,c){var d,e,f,i,j,k,l,m,n=this;if(!this.validateCoords(a.center))return void g.error("angular-google-maps: could not find a valid center property");if(!angular.isDefined(a.zoom))return void g.error("angular-google-maps: map zoom property not set");if(e=angular.element(b),e.addClass("angular-google-map"),j={options:{}},c.options&&(j.options=a.options),c.styles&&(j.styles=a.styles),c.type&&(l=c.type.toUpperCase(),google.maps.MapTypeId.hasOwnProperty(l)?j.mapTypeId=google.maps.MapTypeId[c.type.toUpperCase()]:g.error('angular-google-maps: invalid map type "'+c.type+'"')),m=new google.maps.Map(e.find("div")[1],angular.extend({},h,j,{center:this.getCoords(a.center),draggable:this.isTrue(c.draggable),zoom:a.zoom,bounds:a.bounds})),d=!1,google.maps.event.addListener(m,"dragstart",function(){return d=!0,_.defer(function(){return a.$apply(function(a){return null!=a.dragging?a.dragging=d:void 0})})}),google.maps.event.addListener(m,"dragend",function(){return d=!1,_.defer(function(){return a.$apply(function(a){return null!=a.dragging?a.dragging=d:void 0})})}),google.maps.event.addListener(m,"drag",function(){var b;return b=m.center,_.defer(function(){return a.$apply(function(a){return angular.isDefined(a.center.type)?(a.center.coordinates[1]=b.lat(),a.center.coordinates[0]=b.lng()):(a.center.latitude=b.lat(),a.center.longitude=b.lng())})})}),google.maps.event.addListener(m,"zoom_changed",function(){return a.zoom!==m.zoom?_.defer(function(){return a.$apply(function(a){return a.zoom=m.zoom})}):void 0}),k=!1,google.maps.event.addListener(m,"center_changed",function(){var b;return b=m.center,k?void 0:_.defer(function(){return a.$apply(function(a){if(!m.dragging)if(angular.isDefined(a.center.type)){if(a.center.coordinates[1]!==b.lat()&&(a.center.coordinates[1]=b.lat()),a.center.coordinates[0]!==b.lng())return a.center.coordinates[0]=b.lng()}else if(a.center.latitude!==b.lat()&&(a.center.latitude=b.lat()),a.center.longitude!==b.lng())return a.center.longitude=b.lng()})})}),google.maps.event.addListener(m,"idle",function(){var b,c,d;return b=m.getBounds(),c=b.getNorthEast(),d=b.getSouthWest(),_.defer(function(){return a.$apply(function(a){return null!==a.bounds&&void 0!==a.bounds&&void 0!==a.bounds?(a.bounds.northeast={latitude:c.lat(),longitude:c.lng()},a.bounds.southwest={latitude:d.lat(),longitude:d.lng()}):void 0})})}),angular.isDefined(a.events)&&null!==a.events&&angular.isObject(a.events)){i=function(b){return function(){return a.events[b].apply(a,[m,b,arguments])}};for(f in a.events)a.events.hasOwnProperty(f)&&angular.isFunction(a.events[f])&&google.maps.event.addListener(m,f,i(f))}return a.map=m,null!=c.control&&null!=a.control&&(a.control.refresh=function(a){var b;if(null!=m)return google.maps.event.trigger(m,"resize"),null!=(null!=a?a.latitude:void 0)&&null!=(null!=a?a.latitude:void 0)?(b=n.getCoords(a),n.isTrue(c.pan)?m.panTo(b):m.setCenter(b)):void 0},a.control.getGMap=function(){return m}),a.$watch("center",function(b,e){var f;return f=n.getCoords(b),b===e||f.lat()===m.center.lat()&&f.lng()===m.center.lng()?void 0:(k=!0,d||(n.validateCoords(b)||g.error("Invalid center for newValue: "+JSON.stringify(b)),n.isTrue(c.pan)&&a.zoom===m.zoom?m.panTo(f):m.setCenter(f)),k=!1)},!0),a.$watch("zoom",function(a,b){return a!==b&&a!==m.zoom?_.defer(function(){return m.setZoom(a)}):void 0}),a.$watch("bounds",function(a,b){var c,d,e;if(a!==b)return null==a.northeast.latitude||null==a.northeast.longitude||null==a.southwest.latitude||null==a.southwest.longitude?void g.error("Invalid map bounds for new value: "+JSON.stringify(a)):(d=new google.maps.LatLng(a.northeast.latitude,a.northeast.longitude),e=new google.maps.LatLng(a.southwest.latitude,a.southwest.longitude),c=new google.maps.LatLngBounds(e,d),m.fitBounds(c))}),a.$watch("options",function(a,b){return _.isEqual(a,b)||(j.options=a,null==m)?void 0:m.setOptions(j)},!0),a.$watch("styles",function(a,b){return _.isEqual(a,b)||(j.styles=a,null==m)?void 0:m.setOptions(j)},!0)},d}(f)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api").factory("Marker",["IMarker","MarkerParentModel","MarkerManager",function(b,d,e){var f;return f=function(b){function f(b){this.link=a(this.link,this);var c;f.__super__.constructor.call(this,b),c=this,this.template='',this.$log.info(this)}return c(f,b),f.prototype.controller=["$scope","$element",function(a){return{getMarkerScope:function(){return a}}}],f.prototype.link=function(a,b,c,f){var g;return a.fit&&(g=!0),this.gMarkerManager||(this.gMarkerManager=new e(f.getMap())),new d(a,b,c,f,this.$timeout,this.gMarkerManager,g)},f}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api").factory("Markers",["IMarker","MarkersParentModel",function(b,d){var e;return e=function(b){function e(b){this.link=a(this.link,this);var c;e.__super__.constructor.call(this,b),this.template='',this.scope.idKey="=idkey",this.scope.doRebuildAll="=dorebuildall",this.scope.models="=models",this.scope.doCluster="=docluster",this.scope.clusterOptions="=clusteroptions",this.scope.clusterEvents="=clusterevents",this.scope.labelContent="=labelcontent",this.scope.labelAnchor="@labelanchor",this.scope.labelClass="@labelclass",this.$timeout=b,c=this,this.$log.info(this)}return c(e,b),e.prototype.controller=["$scope","$element",function(a){return{getMarkersScope:function(){return a}}}],e.prototype.link=function(a,b,c,e){return new d(a,b,c,e,this.$timeout)},e}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api").factory("Polyline",["IPolyline","$timeout","array-sync","PolylineChildModel",function(b,d,e,f){var g,h;return g=function(b){function e(){return this.link=a(this.link,this),h=e.__super__.constructor.apply(this,arguments)}return c(e,b),e.prototype.link=function(a,b,c,e){var g=this;return angular.isUndefined(a.path)||null===a.path||!this.validatePath(a.path)?void this.$log.error("polyline: no valid path attribute found"):d(function(){return new f(a,c,e.getMap(),g.DEFAULTS)})},e}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api").factory("Polylines",["IPolyline","$timeout","array-sync","PolylinesParentModel",function(b,d,e,f){var g;return g=function(b){function e(){this.link=a(this.link,this),e.__super__.constructor.call(this),this.scope.idKey="=idkey",this.scope.models="=models",this.$log.info(this)}return c(e,b),e.prototype.link=function(a,b,c,e){var g=this;return angular.isUndefined(a.path)||null===a.path?void this.$log.error("polylines: no valid path attribute found"):a.models?d(function(){return new f(a,b,c,e.getMap(),g.DEFAULTS)}):void this.$log.error("polylines: no models found to create from")},e}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api").factory("Window",["IWindow","GmapUtil","WindowChildModel",function(b,d,e){var f;return f=function(b){function f(b,c,d,e){this.link=a(this.link,this);var g;f.__super__.constructor.call(this,b,c,d,e),g=this,this.require=["^googleMap","^?marker"],this.template='',this.$log.info(g)}return c(f,b),f.include(d),f.prototype.link=function(a,b,c,f){var g=this;return this.$timeout(function(){var d,h,i,j,k,l,m,n;return i=!0,angular.isDefined(c.isiconvisibleonclick)&&(i=a.isIconVisibleOnClick),j=f[0].getMap(),k=f.length>1&&null!=f[1]?f[1].getMarkerScope().gMarker:void 0,d=null!=a.options?a.options:{},h=null!=a&&g.validateCoords(a.coords),m=h?g.createWindowOptions(k,a,b.html(),d):d,null!=j&&(n=new e({},a,m,i,j,k,b)),a.$on("$destroy",function(){return n.destroy()}),null!=f[1]&&(l=f[1].getMarkerScope(),l.$watch("coords",function(a,b){return g.validateCoords(a)?angular.equals(a,b)?void 0:n.getLatestPosition():n.hideWindow()},!0)),null!=g.onChildCreation&&null!=n?g.onChildCreation(n):void 0},d.defaultDelay+25)},f}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api").factory("Windows",["IWindow","WindowsParentModel",function(b,d){var e;return e=function(b){function e(b,c,d,f,g){this.link=a(this.link,this);var h;e.__super__.constructor.call(this,b,c,d,f),h=this,this.$interpolate=g,this.require=["^googleMap","^?markers"],this.template='',this.scope.idKey="=idkey",this.scope.doRebuildAll="=dorebuildall",this.scope.models="=models",this.$log.info(this)}return c(e,b),e.prototype.link=function(a,b,c,e){return new d(a,b,c,e,this.$timeout,this.$compile,this.$http,this.$templateCache,this.$interpolate)},e}(b)}])}.call(this),function(){angular.module("google-maps").directive("googleMap",["Map",function(a){return new a}])}.call(this),function(){angular.module("google-maps").directive("marker",["$timeout","Marker",function(a,b){return new b(a)}])}.call(this),function(){angular.module("google-maps").directive("markers",["$timeout","Markers",function(a,b){return new b(a)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps").directive("markerLabel",["$timeout","ILabel","MarkerLabelChildModel","GmapUtil",function(b,d,e,f){var g;return new(g=function(b){function d(b){this.link=a(this.link,this);var c;d.__super__.constructor.call(this,b),c=this,this.require="^marker",this.template='',this.$log.info(this)}return c(d,b),d.prototype.link=function(a,b,c,d){return this.$timeout(function(){var b,c;return c=d.getMarkerScope().gMarker,null!=c&&(b=new e(c,a)),a.$on("$destroy",function(){return b.destroy()})},f.defaultDelay+25)},d}(d))(b)}])}.call(this),function(){angular.module("google-maps").directive("polygon",["$log","$timeout","array-sync","GmapUtil",function(a,b,c,d){var e,f;return f=function(a){return angular.isDefined(a)&&null!==a&&a===!0||"1"===a||"y"===a||"true"===a},e={},{restrict:"ECA",replace:!0,require:"^googleMap",scope:{path:"=path",stroke:"=stroke",clickable:"=",draggable:"=",editable:"=",geodesic:"=",fill:"=",icons:"=icons",visible:"=","static":"=",events:"=",zIndex:"=zindex"},link:function(g,h,i,j){return angular.isUndefined(g.path)||null===g.path||!d.validatePath(g.path)?void a.error("polygon: no valid path attribute found"):b(function(){var a,b,h,k,l,m;if(b=function(a){var b;return b=angular.extend({},e,{map:l,path:a,strokeColor:g.stroke&&g.stroke.color,strokeOpacity:g.stroke&&g.stroke.opacity,strokeWeight:g.stroke&&g.stroke.weight,fillColor:g.fill&&g.fill.color,fillOpacity:g.fill&&g.fill.opacity}),angular.forEach({clickable:!0,draggable:!1,editable:!1,geodesic:!1,visible:!0,"static":!1,zIndex:0},function(a,c){return b[c]=angular.isUndefined(g[c])||null===g[c]?a:g[c]}),b["static"]&&(b.editable=!1),b},l=j.getMap(),m=new google.maps.Polygon(b(d.convertPathPoints(g.path))),f(i.fit)&&d.extendMapBounds(l,pathPoints),!g["static"]&&angular.isDefined(g.editable)&&g.$watch("editable",function(a,b){return a!==b?m.setEditable(a):void 0}),angular.isDefined(g.draggable)&&g.$watch("draggable",function(a,b){return a!==b?m.setDraggable(a):void 0}),angular.isDefined(g.visible)&&g.$watch("visible",function(a,b){return a!==b?m.setVisible(a):void 0}),angular.isDefined(g.geodesic)&&g.$watch("geodesic",function(a,c){return a!==c?m.setOptions(b(m.getPath())):void 0}),angular.isDefined(g.stroke)&&angular.isDefined(g.stroke.opacity)&&g.$watch("stroke.opacity",function(){return m.setOptions(b(m.getPath()))}),angular.isDefined(g.stroke)&&angular.isDefined(g.stroke.weight)&&g.$watch("stroke.weight",function(a,c){return a!==c?m.setOptions(b(m.getPath())):void 0}),angular.isDefined(g.stroke)&&angular.isDefined(g.stroke.color)&&g.$watch("stroke.color",function(a,c){return a!==c?m.setOptions(b(m.getPath())):void 0}),angular.isDefined(g.fill)&&angular.isDefined(g.fill.color)&&g.$watch("fill.color",function(a,c){return a!==c?m.setOptions(b(m.getPath())):void 0}),angular.isDefined(g.fill)&&angular.isDefined(g.fill.opacity)&&g.$watch("fill.opacity",function(a,c){return a!==c?m.setOptions(b(m.getPath())):void 0}),angular.isDefined(g.zIndex)&&g.$watch("zIndex",function(a,c){return a!==c?m.setOptions(b(m.getPath())):void 0}),angular.isDefined(g.events)&&null!==g.events&&angular.isObject(g.events)){k=function(a){return function(){return g.events[a].apply(g,[m,a,arguments])}};for(h in g.events)g.events.hasOwnProperty(h)&&angular.isFunction(g.events[h])&&m.addListener(h,k(h))}return a=c(m.getPath(),g,"path"),g.$on("$destroy",function(){return m.setMap(null),a?(a(),a=null):void 0})})}}}])}.call(this),function(){angular.module("google-maps").directive("circle",["$log","$timeout","GmapUtil",function(a,b,c){"use strict";var d;return d={},{restrict:"ECA",replace:!0,require:"^googleMap",scope:{center:"=center",radius:"=radius",stroke:"=stroke",fill:"=fill",clickable:"=",draggable:"=",editable:"=",geodesic:"=",icons:"=icons",visible:"="},link:function(e,f,g,h){return b(function(){var f,g,i;return f=function(){var b;return c.validateCoords(e.center)?(b=angular.extend({},d,{map:i,center:c.getCoords(e.center),radius:e.radius,strokeColor:e.stroke&&e.stroke.color,strokeOpacity:e.stroke&&e.stroke.opacity,strokeWeight:e.stroke&&e.stroke.weight,fillColor:e.fill&&e.fill.color,fillOpacity:e.fill&&e.fill.opacity}),angular.forEach({clickable:!0,draggable:!1,editable:!1,geodesic:!1,visible:!0},function(a,c){return b[c]=angular.isUndefined(e[c])||null===e[c]?a:e[c]}),b):void a.error("circle: no valid center attribute found")},i=h.getMap(),g=new google.maps.Circle(f()),e.$watchCollection("center",function(a,b){return a!==b?g.setOptions(f()):void 0}),e.$watchCollection("stroke",function(a,b){return a!==b?g.setOptions(f()):void 0}),e.$watchCollection("fill",function(a,b){return a!==b?g.setOptions(f()):void 0}),e.$watch("radius",function(a,b){return a!==b?g.setOptions(f()):void 0}),e.$watch("clickable",function(a,b){return a!==b?g.setOptions(f()):void 0}),e.$watch("editable",function(a,b){return a!==b?g.setOptions(f()):void 0}),e.$watch("draggable",function(a,b){return a!==b?g.setOptions(f()):void 0}),e.$watch("visible",function(a,b){return a!==b?g.setOptions(f()):void 0}),e.$watch("geodesic",function(a,b){return a!==b?g.setOptions(f()):void 0}),google.maps.event.addListener(g,"radius_changed",function(){return e.radius=g.getRadius(),b(function(){return e.$apply()})}),google.maps.event.addListener(g,"center_changed",function(){return angular.isDefined(e.center.type)?(e.center.coordinates[1]=g.getCenter().lat(),e.center.coordinates[0]=g.getCenter().lng()):(e.center.latitude=g.getCenter().lat(),e.center.longitude=g.getCenter().lng()),b(function(){return e.$apply()})}),e.$on("$destroy",function(){return g.setMap(null)})})}}}])}.call(this),function(){angular.module("google-maps").directive("polyline",["Polyline",function(a){return new a}])}.call(this),function(){angular.module("google-maps").directive("polylines",["Polylines",function(a){return new a}])}.call(this),function(){angular.module("google-maps").directive("rectangle",["$log","$timeout",function(a,b){var c,d,e,f,g;return g=function(a){return angular.isUndefined(a.sw.latitude)||angular.isUndefined(a.sw.longitude)||angular.isUndefined(a.ne.latitude)||angular.isUndefined(a.ne.longitude)?!1:!0},d=function(a){var b;return b=new google.maps.LatLngBounds(new google.maps.LatLng(a.sw.latitude,a.sw.longitude),new google.maps.LatLng(a.ne.latitude,a.ne.longitude))},e=function(a,b){return a.fitBounds(b)},f=function(a){return angular.isDefined(a)&&null!==a&&a===!0||"1"===a||"y"===a||"true"===a},c={},{restrict:"ECA",require:"^googleMap",replace:!0,scope:{bounds:"=",stroke:"=",clickable:"=",draggable:"=",editable:"=",fill:"=",visible:"="},link:function(h,i,j,k){return angular.isUndefined(h.bounds)||null===h.bounds||angular.isUndefined(h.bounds.sw)||null===h.bounds.sw||angular.isUndefined(h.bounds.ne)||null===h.bounds.ne||!g(h.bounds)?void a.error("rectangle: no valid bound attribute found"):b(function(){var b,g,i,l,m;return b=function(a){var b;return b=angular.extend({},c,{map:i,bounds:a,strokeColor:h.stroke&&h.stroke.color,strokeOpacity:h.stroke&&h.stroke.opacity,strokeWeight:h.stroke&&h.stroke.weight,fillColor:h.fill&&h.fill.color,fillOpacity:h.fill&&h.fill.opacity}),angular.forEach({clickable:!0,draggable:!1,editable:!1,visible:!0},function(a,c){return b[c]=angular.isUndefined(h[c])||null===h[c]?a:h[c]}),b},i=k.getMap(),l=new google.maps.Rectangle(b(d(h.bounds))),f(j.fit)&&e(i,bounds),g=!1,google.maps.event.addListener(l,"mousedown",function(){google.maps.event.addListener(l,"mousemove",function(){return g=!0,_.defer(function(){return h.$apply(function(a){return null!=a.dragging?a.dragging=g:void 0})})}),google.maps.event.addListener(l,"mouseup",function(){return google.maps.event.clearListeners(l,"mousemove"),google.maps.event.clearListeners(l,"mouseup"),g=!1,_.defer(function(){return h.$apply(function(a){return null!=a.dragging?a.dragging=g:void 0})})})}),m=!1,google.maps.event.addListener(l,"bounds_changed",function(){var a,b,c;return a=l.getBounds(),b=a.getNorthEast(),c=a.getSouthWest(),m?void 0:_.defer(function(){return h.$apply(function(a){l.dragging||null!==a.bounds&&void 0!==a.bounds&&void 0!==a.bounds&&(a.bounds.ne={latitude:b.lat(),longitude:b.lng()},a.bounds.sw={latitude:c.lat(),longitude:c.lng()})})})}),h.$watch("bounds",function(b,c){var d;if(!_.isEqual(b,c))return m=!0,g||((null==b.sw.latitude||null==b.sw.longitude||null==b.ne.latitude||null==b.ne.longitude)&&a.error("Invalid bounds for newValue: "+JSON.stringify(b)),d=new google.maps.LatLngBounds(new google.maps.LatLng(b.sw.latitude,b.sw.longitude),new google.maps.LatLng(b.ne.latitude,b.ne.longitude)),l.setBounds(d)),m=!1},!0),angular.isDefined(h.editable)&&h.$watch("editable",function(a){return l.setEditable(a)}),angular.isDefined(h.draggable)&&h.$watch("draggable",function(a){return l.setDraggable(a)}),angular.isDefined(h.visible)&&h.$watch("visible",function(a){return l.setVisible(a)}),angular.isDefined(h.stroke)&&(angular.isDefined(h.stroke.color)&&h.$watch("stroke.color",function(){return l.setOptions(b(l.getBounds()))}),angular.isDefined(h.stroke.weight)&&h.$watch("stroke.weight",function(){return l.setOptions(b(l.getBounds()))}),angular.isDefined(h.stroke.opacity)&&h.$watch("stroke.opacity",function(){return l.setOptions(b(l.getBounds()))})),angular.isDefined(h.fill)&&(angular.isDefined(h.fill.color)&&h.$watch("fill.color",function(){return l.setOptions(b(l.getBounds()))}),angular.isDefined(h.fill.opacity)&&h.$watch("fill.opacity",function(){return l.setOptions(b(l.getBounds()))})),h.$on("$destroy",function(){return l.setMap(null)})})}}}])}.call(this),function(){angular.module("google-maps").directive("window",["$timeout","$compile","$http","$templateCache","Window",function(a,b,c,d,e){return new e(a,b,c,d)}])}.call(this),function(){angular.module("google-maps").directive("windows",["$timeout","$compile","$http","$templateCache","$interpolate","Windows",function(a,b,c,d,e,f){return new f(a,b,c,d,e)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}};angular.module("google-maps").directive("layer",["$timeout","Logger","LayerParentModel",function(b,c,d){var e;return new(e=function(){function b(b){this.$timeout=b,this.link=a(this.link,this),this.$log=c,this.restrict="ECMA",this.require="^googleMap",this.priority=-1,this.transclude=!0,this.template='',this.replace=!0,this.scope={show:"=show",type:"=type",namespace:"=namespace",options:"=options",onCreated:"&oncreated"}}return b.prototype.link=function(a,b,c,e){return null!=c.oncreated?new d(a,b,c,e,this.$timeout,a.onCreated):new d(a,b,c,e,this.$timeout)},b}())(b)}])}.call(this),InfoBox.prototype=new google.maps.OverlayView,InfoBox.prototype.createInfoBoxDiv_=function(){var a,b,c,d=this,e=function(a){a.cancelBubble=!0,a.stopPropagation&&a.stopPropagation()},f=function(a){a.returnValue=!1,a.preventDefault&&a.preventDefault(),d.enableEventPropagation_||e(a)};if(!this.div_){if(this.div_=document.createElement("div"),this.setBoxStyle_(),"undefined"==typeof this.content_.nodeType?this.div_.innerHTML=this.getCloseBoxImg_()+this.content_:(this.div_.innerHTML=this.getCloseBoxImg_(),this.div_.appendChild(this.content_)),this.getPanes()[this.pane_].appendChild(this.div_),this.addClickHandler_(),this.div_.style.width?this.fixedWidthSet_=!0:0!==this.maxWidth_&&this.div_.offsetWidth>this.maxWidth_?(this.div_.style.width=this.maxWidth_,this.div_.style.overflow="auto",this.fixedWidthSet_=!0):(c=this.getBoxWidths_(),this.div_.style.width=this.div_.offsetWidth-c.left-c.right+"px",this.fixedWidthSet_=!1),this.panBox_(this.disableAutoPan_),!this.enableEventPropagation_){for(this.eventListeners_=[],b=["mousedown","mouseover","mouseout","mouseup","click","dblclick","touchstart","touchend","touchmove"],a=0;ag&&(d=o.x+k+i+m-g),this.alignBottom_?o.y<-j+n+l?e=o.y+j-n-l:o.y+j+n>h&&(e=o.y+j+n-h):o.y<-j+n?e=o.y+j-n:o.y+l+j+n>h&&(e=o.y+l+j+n-h),0!==d||0!==e){{b.getCenter()}b.panBy(d,e)}}},InfoBox.prototype.setBoxStyle_=function(){var a,b;if(this.div_){this.div_.className=this.boxClass_,this.div_.style.cssText="",b=this.boxStyle_;for(a in b)b.hasOwnProperty(a)&&(this.div_.style[a]=b[a]);"undefined"!=typeof this.div_.style.opacity&&""!==this.div_.style.opacity&&(this.div_.style.filter="alpha(opacity="+100*this.div_.style.opacity+")"),this.div_.style.position="absolute",this.div_.style.visibility="hidden",null!==this.zIndex_&&(this.div_.style.zIndex=this.zIndex_)}},InfoBox.prototype.getBoxWidths_=function(){var a,b={top:0,bottom:0,left:0,right:0},c=this.div_;return document.defaultView&&document.defaultView.getComputedStyle?(a=c.ownerDocument.defaultView.getComputedStyle(c,""),a&&(b.top=parseInt(a.borderTopWidth,10)||0,b.bottom=parseInt(a.borderBottomWidth,10)||0,b.left=parseInt(a.borderLeftWidth,10)||0,b.right=parseInt(a.borderRightWidth,10)||0)):document.documentElement.currentStyle&&c.currentStyle&&(b.top=parseInt(c.currentStyle.borderTopWidth,10)||0,b.bottom=parseInt(c.currentStyle.borderBottomWidth,10)||0,b.left=parseInt(c.currentStyle.borderLeftWidth,10)||0,b.right=parseInt(c.currentStyle.borderRightWidth,10)||0),b},InfoBox.prototype.onRemove=function(){this.div_&&(this.div_.parentNode.removeChild(this.div_),this.div_=null)},InfoBox.prototype.draw=function(){this.createInfoBoxDiv_();var a=this.getProjection().fromLatLngToDivPixel(this.position_);this.div_.style.left=a.x+this.pixelOffset_.width+"px",this.alignBottom_?this.div_.style.bottom=-(a.y+this.pixelOffset_.height)+"px":this.div_.style.top=a.y+this.pixelOffset_.height+"px",this.div_.style.visibility=this.isHidden_?"hidden":"visible"},InfoBox.prototype.setOptions=function(a){"undefined"!=typeof a.boxClass&&(this.boxClass_=a.boxClass,this.setBoxStyle_()),"undefined"!=typeof a.boxStyle&&(this.boxStyle_=a.boxStyle,this.setBoxStyle_()),"undefined"!=typeof a.content&&this.setContent(a.content),"undefined"!=typeof a.disableAutoPan&&(this.disableAutoPan_=a.disableAutoPan),"undefined"!=typeof a.maxWidth&&(this.maxWidth_=a.maxWidth),"undefined"!=typeof a.pixelOffset&&(this.pixelOffset_=a.pixelOffset),"undefined"!=typeof a.alignBottom&&(this.alignBottom_=a.alignBottom),"undefined"!=typeof a.position&&this.setPosition(a.position),"undefined"!=typeof a.zIndex&&this.setZIndex(a.zIndex),"undefined"!=typeof a.closeBoxMargin&&(this.closeBoxMargin_=a.closeBoxMargin),"undefined"!=typeof a.closeBoxURL&&(this.closeBoxURL_=a.closeBoxURL),"undefined"!=typeof a.infoBoxClearance&&(this.infoBoxClearance_=a.infoBoxClearance),"undefined"!=typeof a.isHidden&&(this.isHidden_=a.isHidden),"undefined"!=typeof a.visible&&(this.isHidden_=!a.visible),"undefined"!=typeof a.enableEventPropagation&&(this.enableEventPropagation_=a.enableEventPropagation),this.div_&&this.draw()},InfoBox.prototype.setContent=function(a){this.content_=a,this.div_&&(this.closeListener_&&(google.maps.event.removeListener(this.closeListener_),this.closeListener_=null),this.fixedWidthSet_||(this.div_.style.width=""),"undefined"==typeof a.nodeType?this.div_.innerHTML=this.getCloseBoxImg_()+a:(this.div_.innerHTML=this.getCloseBoxImg_(),this.div_.appendChild(a)),this.fixedWidthSet_||(this.div_.style.width=this.div_.offsetWidth+"px","undefined"==typeof a.nodeType?this.div_.innerHTML=this.getCloseBoxImg_()+a:(this.div_.innerHTML=this.getCloseBoxImg_(),this.div_.appendChild(a))),this.addClickHandler_()),google.maps.event.trigger(this,"content_changed")},InfoBox.prototype.setPosition=function(a){this.position_=a,this.div_&&this.draw(),google.maps.event.trigger(this,"position_changed")},InfoBox.prototype.setZIndex=function(a){this.zIndex_=a,this.div_&&(this.div_.style.zIndex=a),google.maps.event.trigger(this,"zindex_changed")},InfoBox.prototype.setVisible=function(a){this.isHidden_=!a,this.div_&&(this.div_.style.visibility=this.isHidden_?"hidden":"visible")},InfoBox.prototype.getContent=function(){return this.content_},InfoBox.prototype.getPosition=function(){return this.position_},InfoBox.prototype.getZIndex=function(){return this.zIndex_},InfoBox.prototype.getVisible=function(){var a;return a="undefined"==typeof this.getMap()||null===this.getMap()?!1:!this.isHidden_},InfoBox.prototype.show=function(){this.isHidden_=!1,this.div_&&(this.div_.style.visibility="visible")},InfoBox.prototype.hide=function(){this.isHidden_=!0,this.div_&&(this.div_.style.visibility="hidden")},InfoBox.prototype.open=function(a,b){var c=this;b&&(this.position_=b.getPosition(),this.moveListener_=google.maps.event.addListener(b,"position_changed",function(){c.setPosition(this.getPosition())})),this.setMap(a),this.div_&&this.panBox_()},InfoBox.prototype.close=function(){var a;if(this.closeListener_&&(google.maps.event.removeListener(this.closeListener_),this.closeListener_=null),this.eventListeners_){for(a=0;af&&g.getMap().setZoom(f+1)},100)),d.cancelBubble=!0,d.stopPropagation&&d.stopPropagation()}}),google.maps.event.addDomListener(this.div_,"mouseover",function(){var a=c.cluster_.getMarkerClusterer();google.maps.event.trigger(a,"mouseover",c.cluster_)}),google.maps.event.addDomListener(this.div_,"mouseout",function(){var a=c.cluster_.getMarkerClusterer();google.maps.event.trigger(a,"mouseout",c.cluster_)})},ClusterIcon.prototype.onRemove=function(){this.div_&&this.div_.parentNode&&(this.hide(),google.maps.event.removeListener(this.boundsChangedListener_),google.maps.event.clearInstanceListeners(this.div_),this.div_.parentNode.removeChild(this.div_),this.div_=null)},ClusterIcon.prototype.draw=function(){if(this.visible_){var a=this.getPosFromLatLng_(this.center_);this.div_.style.top=a.y+"px",this.div_.style.left=a.x+"px"}},ClusterIcon.prototype.hide=function(){this.div_&&(this.div_.style.display="none"),this.visible_=!1},ClusterIcon.prototype.show=function(){if(this.div_){var a="",b=this.backgroundPosition_.split(" "),c=parseInt(b[0].trim(),10),d=parseInt(b[1].trim(),10),e=this.getPosFromLatLng_(this.center_);this.div_.style.cssText=this.createCss(e),a="",this.div_.innerHTML=a+"
"+this.sums_.text+"
",this.div_.title="undefined"==typeof this.sums_.title||""===this.sums_.title?this.cluster_.getMarkerClusterer().getTitle():this.sums_.title,this.div_.style.display="" -}this.visible_=!0},ClusterIcon.prototype.useStyle=function(a){this.sums_=a;var b=Math.max(0,a.index-1);b=Math.min(this.styles_.length-1,b);var c=this.styles_[b];this.url_=c.url,this.height_=c.height,this.width_=c.width,this.anchorText_=c.anchorText||[0,0],this.anchorIcon_=c.anchorIcon||[parseInt(this.height_/2,10),parseInt(this.width_/2,10)],this.textColor_=c.textColor||"black",this.textSize_=c.textSize||11,this.textDecoration_=c.textDecoration||"none",this.fontWeight_=c.fontWeight||"bold",this.fontStyle_=c.fontStyle||"normal",this.fontFamily_=c.fontFamily||"Arial,sans-serif",this.backgroundPosition_=c.backgroundPosition||"0 0"},ClusterIcon.prototype.setCenter=function(a){this.center_=a},ClusterIcon.prototype.createCss=function(a){var b=[];return b.push("cursor: pointer;"),b.push("position: absolute; top: "+a.y+"px; left: "+a.x+"px;"),b.push("width: "+this.width_+"px; height: "+this.height_+"px;"),b.join("")},ClusterIcon.prototype.getPosFromLatLng_=function(a){var b=this.getProjection().fromLatLngToDivPixel(a);return b.x-=this.anchorIcon_[1],b.y-=this.anchorIcon_[0],b.x=parseInt(b.x,10),b.y=parseInt(b.y,10),b},Cluster.prototype.getSize=function(){return this.markers_.length},Cluster.prototype.getMarkers=function(){return this.markers_},Cluster.prototype.getCenter=function(){return this.center_},Cluster.prototype.getMap=function(){return this.map_},Cluster.prototype.getMarkerClusterer=function(){return this.markerClusterer_},Cluster.prototype.getBounds=function(){var a,b=new google.maps.LatLngBounds(this.center_,this.center_),c=this.getMarkers();for(a=0;ad)a.getMap()!==this.map_&&a.setMap(this.map_);else if(cb;b++)this.markers_[b].setMap(null);else a.setMap(null);return this.updateIcon_(),!0},Cluster.prototype.isMarkerInClusterBounds=function(a){return this.bounds_.contains(a.getPosition())},Cluster.prototype.calculateBounds_=function(){var a=new google.maps.LatLngBounds(this.center_,this.center_);this.bounds_=this.markerClusterer_.getExtendedBounds(a)},Cluster.prototype.updateIcon_=function(){var a=this.markers_.length,b=this.markerClusterer_.getMaxZoom();if(null!==b&&this.map_.getZoom()>b)return void this.clusterIcon_.hide();if(a0))for(a=0;ac&&(f=c,g=d));g&&g.isMarkerInClusterBounds(a)?g.addMarker(a):(d=new Cluster(this),d.addMarker(a),this.clusters_.push(d))},MarkerClusterer.prototype.createClusters_=function(a){var b,c,d,e=this;if(this.ready_){0===a&&(google.maps.event.trigger(this,"clusteringbegin",this),"undefined"!=typeof this.timerRefStatic&&(clearTimeout(this.timerRefStatic),delete this.timerRefStatic)),d=this.getMap().getZoom()>3?new google.maps.LatLngBounds(this.getMap().getBounds().getSouthWest(),this.getMap().getBounds().getNorthEast()):new google.maps.LatLngBounds(new google.maps.LatLng(85.02070771743472,-178.48388434375),new google.maps.LatLng(-85.08136444384544,178.00048865625));var f=this.getExtendedBounds(d),g=Math.min(a+this.batchSize_,this.markers_.length);for(b=a;g>b;b++)c=this.markers_[b],!c.isAdded&&this.isMarkerInBounds_(c,f)&&(!this.ignoreHidden_||this.ignoreHidden_&&c.getVisible())&&this.addToClosestCluster_(c);gd?Math.max(0,f+d):d}for(;f>e;){if(null!=c){if(c(a[e],b))return e}else if(_.isEqual(a[e],b))return e;e++}return-1},_["extends"]=function(a){return _.reduce(a,function(a,b){return _.extend(a,b)},{})},_.isNullOrUndefined=function(a){return _.isNull(a||_.isUndefined(a))}}.call(this),function(){angular.module("google-maps.providers".ns(),[]),angular.module("google-maps.wrapped".ns(),[]),angular.module("google-maps.extensions".ns(),["google-maps.wrapped".ns(),"google-maps.providers".ns()]),angular.module("google-maps.directives.api.utils".ns(),["google-maps.extensions".ns()]),angular.module("google-maps.directives.api.managers".ns(),[]),angular.module("google-maps.directives.api.options".ns(),["google-maps.directives.api.utils".ns()]),angular.module("google-maps.directives.api.options.builders".ns(),[]),angular.module("google-maps.directives.api.models.child".ns(),["google-maps.directives.api.utils".ns(),"google-maps.directives.api.options".ns(),"google-maps.directives.api.options.builders".ns()]),angular.module("google-maps.directives.api.models.parent".ns(),["google-maps.directives.api.managers".ns(),"google-maps.directives.api.models.child".ns(),"google-maps.providers".ns()]),angular.module("google-maps.directives.api".ns(),["google-maps.directives.api.models.parent".ns()]),angular.module("google-maps".ns(),["google-maps.directives.api".ns(),"google-maps.providers".ns()]).factory("debounce".ns(),["$timeout",function(a){return function(b){var c;return c=0,function(){var d,e,f;return f=this,d=arguments,c++,e=function(a){return function(){return a===c?b.apply(f,d):void 0}}(c),a(e,0,!0)}}}])}.call(this),function(){angular.module("google-maps.providers".ns()).factory("MapScriptLoader".ns(),["$q",function(a){return{load:function(b){var c,d,e,f;return c=a.defer(),angular.isDefined(window.google)&&angular.isDefined(window.google.maps)?(c.resolve(window.google.maps),c.promise):(e=b.callback="onGoogleMapsReady"+Math.round(1e3*Math.random()),window[e]=function(){window[e]=null,c.resolve(window.google.maps)},d=_.map(b,function(a,b){return b+"="+a}),d=d.join("&"),f=document.createElement("script"),f.type="text/javascript",f.src="https://maps.googleapis.com/maps/api/js?"+d,document.body.appendChild(f),c.promise)}}}]).provider("GoogleMapApi".ns(),function(){return this.options={v:"3.16",libraries:"places",language:"en",sensor:"false"},this.configure=function(a){angular.extend(this.options,a)},this.$get=["MapScriptLoader".ns(),function(a){return function(b){return a.promise=b.load(a.options),a.promise}}(this)],this})}.call(this),function(){angular.module("google-maps.extensions".ns()).service("ExtendGWin".ns(),function(){return{init:_.once(function(){return google||("undefined"!=typeof google&&null!==google?google.maps:void 0)||null!=google.maps.InfoWindow?(google.maps.InfoWindow.prototype._open=google.maps.InfoWindow.prototype.open,google.maps.InfoWindow.prototype._close=google.maps.InfoWindow.prototype.close,google.maps.InfoWindow.prototype._isOpen=!1,google.maps.InfoWindow.prototype.open=function(a,b,c){null==c&&(this._isOpen=!0,this._open(a,b,!0))},google.maps.InfoWindow.prototype.close=function(a){null==a&&(this._isOpen=!1,this._close(!0))},google.maps.InfoWindow.prototype.isOpen=function(a){return null==a&&(a=void 0),null==a?this._isOpen:this._isOpen=a},window.InfoBox&&(window.InfoBox.prototype._open=window.InfoBox.prototype.open,window.InfoBox.prototype._close=window.InfoBox.prototype.close,window.InfoBox.prototype._isOpen=!1,window.InfoBox.prototype.open=function(a,b){this._isOpen=!0,this._open(a,b)},window.InfoBox.prototype.close=function(){this._isOpen=!1,this._close()},window.InfoBox.prototype.isOpen=function(a){return null==a&&(a=void 0),null==a?this._isOpen:this._isOpen=a}),window.MarkerLabel_?(window.MarkerLabel_.prototype.setContent=function(){var a;a=this.marker_.get("labelContent"),a&&!_.isEqual(this.oldContent,a)&&("undefined"==typeof(null!=a?a.nodeType:void 0)?(this.labelDiv_.innerHTML=a,this.eventDiv_.innerHTML=this.labelDiv_.innerHTML,this.oldContent=a):(this.labelDiv_.innerHTML="",this.labelDiv_.appendChild(a),a=a.cloneNode(!0),this.eventDiv_.appendChild(a),this.oldContent=a))},window.MarkerLabel_.prototype.onRemove=function(){null!=this.labelDiv_.parentNode&&this.labelDiv_.parentNode.removeChild(this.labelDiv_),null!=this.eventDiv_.parentNode&&this.eventDiv_.parentNode.removeChild(this.eventDiv_),this.listeners_&&this.listeners_.length&&this.listeners_.forEach(function(a){return google.maps.event.removeListener(a)})}):void 0):void 0})}})}.call(this),function(){angular.module("google-maps.directives.api.utils".ns()).service("_sync".ns(),[function(){return{fakePromise:function(){var a;return a=void 0,{then:function(b){return a=b},resolve:function(){return a.apply(void 0,arguments)}}}}}]).factory("_async".ns(),[function(){var a,b,c,d,e;return a=20,e=function(a,b){return a.existingPieces=a.existingPieces?a.existingPieces.then(function(){return b()}):b()},b=function(a,c,d,e,f,g,h){var i,j,k;try{for(i=c&&c0?c(a,function(a){return h.push(b(a))},d,e,f,g).then(function(){return h}):Promise.resolve(h)},{each:c,map:d,waitOrGo:e,defaultChunkSize:a}}])}.call(this),function(){var a=[].indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(b in this&&this[b]===a)return b;return-1};angular.module("google-maps.directives.api.utils".ns()).factory("BaseObject".ns(),function(){var b,c;return c=["extended","included"],b=function(){function b(){}return b.extend=function(b){var d,e,f;for(d in b)e=b[d],a.call(c,d)<0&&(this[d]=e);return null!=(f=b.extended)&&f.apply(this),this},b.include=function(b){var d,e,f;for(d in b)e=b[d],a.call(c,d)<0&&(this.prototype[d]=e);return null!=(f=b.included)&&f.apply(this),this},b}()})}.call(this),function(){angular.module("google-maps.directives.api.utils".ns()).factory("ChildEvents".ns(),function(){return{onChildCreation:function(){}}})}.call(this),function(){angular.module("google-maps.directives.api.utils".ns()).service("CtrlHandle".ns(),["$q",function(a){var b;return b={handle:function(b){return b.deferred=a.defer(),{getScope:function(){return b}}},mapPromise:function(a,b){var c;return c=b.getScope(),c.deferred.promise.then(function(b){return a.map=b}),c.deferred.promise}}}])}.call(this),function(){angular.module("google-maps.directives.api.utils".ns()).service("EventsHelper".ns(),["Logger".ns(),function(a){return{setEvents:function(b,c,d,e){return angular.isDefined(c.events)&&null!=c.events&&angular.isObject(c.events)?_.compact(_.map(c.events,function(f,g){var h;return e&&(h=_(e).contains(g)),c.events.hasOwnProperty(g)&&angular.isFunction(c.events[g])&&!h?google.maps.event.addListener(b,g,function(){return c.$apply(f.apply(c,[b,g,d,arguments]))}):a.info("EventHelper: invalid event listener "+g)})):void 0},removeEvents:function(a){return null!=a?a.forEach(function(a){return google.maps.event.removeListener(a)}):void 0}}}])}.call(this),function(){var a={}.hasOwnProperty,b=function(b,c){function d(){this.constructor=b}for(var e in c)a.call(c,e)&&(b[e]=c[e]);return d.prototype=c.prototype,b.prototype=new d,b.__super__=c.prototype,b};angular.module("google-maps.directives.api.utils".ns()).factory("FitHelper".ns(),["BaseObject".ns(),"Logger".ns(),"_async".ns(),function(a,c,d){var e;return e=function(a){function c(){return c.__super__.constructor.apply(this,arguments)}return b(c,a),c.prototype.fit=function(a,b){var c,e;return b&&a&&a.length>0?(c=new google.maps.LatLngBounds,e=!1,d.each(a,function(){return function(a){return a?(e||(e=!0),c.extend(a.getPosition())):void 0}}(this)).then(function(){return e?b.fitBounds(c):void 0})):void 0},c}(a)}])}.call(this),function(){angular.module("google-maps.directives.api.utils".ns()).service("GmapUtil".ns(),["Logger".ns(),"$compile",function(a,b){var c,d,e,f;return d=function(a){return Array.isArray(a)&&2===a.length?a[1]:angular.isDefined(a.type)&&"Point"===a.type?a.coordinates[1]:a.latitude},e=function(a){return Array.isArray(a)&&2===a.length?a[0]:angular.isDefined(a.type)&&"Point"===a.type?a.coordinates[0]:a.longitude},c=function(a){return a?Array.isArray(a)&&2===a.length?new google.maps.LatLng(a[1],a[0]):angular.isDefined(a.type)&&"Point"===a.type?new google.maps.LatLng(a.coordinates[1],a.coordinates[0]):new google.maps.LatLng(a.latitude,a.longitude):void 0},f=function(a){if(angular.isUndefined(a))return!1;if(_.isArray(a)){if(2===a.length)return!0}else if(null!=a&&(null!=a?a.type:void 0)&&"Point"===a.type&&_.isArray(a.coordinates)&&2===a.coordinates.length)return!0;return a&&angular.isDefined((null!=a?a.latitude:void 0)&&angular.isDefined(null!=a?a.longitude:void 0))?!0:!1},{setCoordsFromEvent:function(a,b){return a?(Array.isArray(a)&&2===a.length?(a[1]=b.lat(),a[0]=b.lng()):angular.isDefined(a.type)&&"Point"===a.type?(a.coordinates[1]=b.lat(),a.coordinates[0]=b.lng()):(a.latitude=b.lat(),a.longitude=b.lng()),a):void 0},getLabelPositionPoint:function(a){var b,c;return void 0===a?void 0:(a=/^([-\d\.]+)\s([-\d\.]+)$/.exec(a),b=parseFloat(a[1]),c=parseFloat(a[2]),null!=b&&null!=c?new google.maps.Point(b,c):void 0)},createWindowOptions:function(d,e,f,g){var h;return null!=f&&null!=g&&null!=b?(h=angular.extend({},g,{content:this.buildContent(e,g,f),position:null!=g.position?g.position:angular.isObject(d)?d.getPosition():c(e.coords)}),null!=d&&null==(null!=h?h.pixelOffset:void 0)&&(h.pixelOffset=null==h.boxClass?{height:-40,width:0}:{height:0,width:-2}),h):g?g:(a.error("infoWindow defaults not defined"),f?void 0:a.error("infoWindow content not defined"))},buildContent:function(a,c,d){var e,f;return null!=c.content?f=c.content:null!=b?(e=b(d)(a),e.length>0&&(f=e[0])):f=d,f},defaultDelay:50,isTrue:function(a){return angular.isDefined(a)&&null!==a&&a===!0||"1"===a||"y"===a||"true"===a},isFalse:function(a){return-1!==["false","FALSE",0,"n","N","no","NO"].indexOf(a)},getCoords:c,validateCoords:f,equalCoords:function(a,b){return d(a)===d(b)&&e(a)===e(b)},validatePath:function(a){var b,c,d,e;if(c=0,angular.isUndefined(a.type)){if(!Array.isArray(a)||a.length<2)return!1;for(;cthis.max?(this.max=a[0].length,this.index=b):void 0},e),d=a.coordinates[e.index],b=d[0],b.length<4)return!1}else{if("LineString"!==a.type)return!1;if(a.coordinates.length<2)return!1;b=a.coordinates}for(;cthis.max?(this.max=a[0].length,this.index=b):void 0},f),b=a.coordinates[f.index][0]):"LineString"===a.type&&(b=a.coordinates);cd;)l=j.getAt(d),h=a[d],"function"==typeof h.equals?h.equals(l)||(j.setAt(d,h),c=!0):(l.lat()!==h.latitude||l.lng()!==h.longitude)&&(j.setAt(d,new google.maps.LatLng(h.latitude,h.longitude)),c=!0),d++;for(;g>d;)h=a[d],j.push("function"==typeof h.lat&&"function"==typeof h.lng?h:new google.maps.LatLng(h.latitude,h.longitude)),c=!0,d++;for(;k>d;)j.pop(),c=!0,d++}return i=!1,c?e(j):void 0},h=function(a){var c,d,f,g,h,j,k,l,n;if(i=!0,k=b,d=!1,a){for("Polygon"===m.type?c=a.coordinates[0]:"LineString"===m.type&&(c=a.coordinates),f=0,l=k.getLength(),h=c.length,g=Math.min(l,h),j=void 0;g>f;)n=k.getAt(f),j=c[f],(n.lat()!==j[1]||n.lng()!==j[0])&&(k.setAt(f,new google.maps.LatLng(j[1],j[0])),d=!0),f++;for(;h>f;)j=c[f],k.push(new google.maps.LatLng(j[1],j[0])),d=!0,f++;for(;l>f;)k.pop(),d=!0,f++}return i=!1,d?e(k):void 0},c["static"]||(n=angular.isUndefined(m.type)?c.$watchCollection(d,k):c.$watch(d,h,!0)),function(){return l&&(l(),l=null),n?(n(),n=null):void 0}}}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.options.builders".ns()).service("CommonOptionsBuilder".ns(),["BaseObject".ns(),"Logger".ns(),function(b,d){var e;return e=function(b){function e(){return this.watchProps=a(this.watchProps,this),this.buildOpts=a(this.buildOpts,this),e.__super__.constructor.apply(this,arguments)}return c(e,b),e.prototype.props=["clickable","draggable","editable","visible",{prop:"stroke",isColl:!0}],e.prototype.buildOpts=function(a,b){var c,e,f,g,h,i;return null==a&&(a={}),null==b&&(b={}),this.scope?this.map?(c=_(this.scope).chain().keys().contains("model").value(),e=c?this.scope.model:this.scope,f=angular.extend(a,this.DEFAULTS,{map:this.map,strokeColor:null!=(g=e.stroke)?g.color:void 0,strokeOpacity:null!=(h=e.stroke)?h.opacity:void 0,strokeWeight:null!=(i=e.stroke)?i.weight:void 0}),angular.forEach(angular.extend(b,{clickable:!0,draggable:!1,editable:!1,"static":!1,fit:!1,visible:!0,zIndex:0}),function(){return function(a,b){return f[b]=angular.isUndefined(e[b]||null===e[b])?a:e[b]}}(this)),f["static"]&&(f.editable=!1),f):void d.error("this.map not defined in CommonOptionsBuilder can not buildOpts"):void d.error("this.scope not defined in CommonOptionsBuilder can not buildOpts")},e.prototype.watchProps=function(a){return null==a&&(a=this.props),a.forEach(function(a){return function(b){return null!=a.attrs[b]||null!=a.attrs[null!=b?b.prop:void 0]?(null!=b?b.isColl:void 0)?a.scope.$watchCollection(b.prop,a.setMyOptions):a.scope.$watch(b,a.setMyOptions):void 0}}(this))},e}(b)}])}.call(this),function(){var a={}.hasOwnProperty,b=function(b,c){function d(){this.constructor=b}for(var e in c)a.call(c,e)&&(b[e]=c[e]);return d.prototype=c.prototype,b.prototype=new d,b.__super__=c.prototype,b};angular.module("google-maps.directives.api.options.builders".ns()).factory("PolylineOptionsBuilder".ns(),["CommonOptionsBuilder".ns(),function(a){var c;return c=function(a){function c(){return c.__super__.constructor.apply(this,arguments)}return b(c,a),c.prototype.buildOpts=function(a){return c.__super__.buildOpts.call(this,{path:a},{geodesic:!1})},c}(a)}]).factory("ShapeOptionsBuilder".ns(),["CommonOptionsBuilder".ns(),function(a){var c;return c=function(a){function c(){return c.__super__.constructor.apply(this,arguments)}return b(c,a),c.prototype.buildOpts=function(a,b){var d,e;return a=angular.extend(a,{fillColor:null!=(d=this.scope.fill)?d.color:void 0,fillOpacity:null!=(e=this.scope.fill)?e.opacity:void 0}),c.__super__.buildOpts.call(this,a,b)},c}(a)}]).factory("PolygonOptionsBuilder".ns(),["ShapeOptionsBuilder".ns(),function(a){var c;return c=function(a){function c(){return c.__super__.constructor.apply(this,arguments)}return b(c,a),c.prototype.buildOpts=function(a){return c.__super__.buildOpts.call(this,{path:a},{geodesic:!1})},c}(a)}]).factory("RectangleOptionsBuilder".ns(),["ShapeOptionsBuilder".ns(),function(a){var c;return c=function(a){function c(){return c.__super__.constructor.apply(this,arguments)}return b(c,a),c.prototype.buildOpts=function(a){return c.__super__.buildOpts.call(this,{bounds:a})},c}(a)}]).factory("CircleOptionsBuilder".ns(),["ShapeOptionsBuilder".ns(),function(a){var c;return c=function(a){function c(){return c.__super__.constructor.apply(this,arguments)}return b(c,a),c.prototype.buildOpts=function(a,b){return c.__super__.buildOpts.call(this,{center:a,radius:b})},c}(a)}])}.call(this),function(){angular.module("google-maps.directives.api.options".ns()).service("MarkerOptions".ns(),["Logger".ns(),"GmapUtil".ns(),function(a,b){return _.extend(b,{createOptions:function(a,c,d,e){var f;return null==e&&(e=void 0),null==d&&(d={}),f=angular.extend({},d,{position:null!=d.position?d.position:b.getCoords(a),visible:null!=d.visible?d.visible:b.validateCoords(a)}),(null!=d.icon||null!=c)&&(f=angular.extend(f,{icon:null!=d.icon?d.icon:c})),null!=e&&(f.map=e),f},isLabel:function(a){return null!=a.labelContent||null!=a.labelAnchor||null!=a.labelClass||null!=a.labelStyle||null!=a.labelVisible?!0:!1}})}])}.call(this),function(){angular.module("google-maps.directives.api.models.child".ns()).factory("DrawFreeHandChildModel".ns(),["Logger".ns(),"$q",function(a,b){var c,d;return c=function(a,b,c){var d,e;return this.polys=b,e=new google.maps.Polyline({map:a,clickable:!1}),d=google.maps.event.addListener(a,"mousemove",function(a){return e.getPath().push(a.latLng)}),void google.maps.event.addListenerOnce(a,"mouseup",function(){var f;return google.maps.event.removeListener(d),f=e.getPath(),e.setMap(null),b.push(new google.maps.Polygon({map:a,path:f})),e=null,google.maps.event.clearListeners(a.getDiv(),"mousedown"),c() +})},d=function(d){var e,f;return this.map=d,f=function(a){return function(){var b;return null!=(b=a.deferred)&&b.resolve(),a.map.setOptions(a.oldOptions)}}(this),e=function(b){return function(){return a.info("disabling map move"),b.oldOptions=d.getOptions(),b.map.setOptions({draggable:!1,zoomControl:!1,scrollwheel:!1,disableDoubleClickZoom:!1})}}(this),this.engage=function(d){return function(g){return d.polys=g,d.deferred=b.defer(),e(),a.info("DrawFreeHandChildModel is engaged (drawing)."),google.maps.event.addDomListener(d.map.getDiv(),"mousedown",function(){return c(d.map,d.polys,f)}),d.deferred.promise}}(this),this}}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.child".ns()).factory("MarkerChildModel".ns(),["ModelKey".ns(),"GmapUtil".ns(),"Logger".ns(),"EventsHelper".ns(),"MarkerOptions".ns(),function(b,d,e,f,g){var h,i;return i=["coords","icon","options","fit"],h=function(b){function h(b,c,d,f,g,j,k,l,m){this.model=c,this.keys=d,this.gMap=f,this.defaults=g,this.doClick=j,this.gMarkerManager=k,this.doDrawSelf=null!=l?l:!0,this.trackModel=null!=m?m:!0,this.internalEvents=a(this.internalEvents,this),this.setLabelOptions=a(this.setLabelOptions,this),this.setOptions=a(this.setOptions,this),this.setIcon=a(this.setIcon,this),this.setCoords=a(this.setCoords,this),this.maybeSetScopeValue=a(this.maybeSetScopeValue,this),this.createMarker=a(this.createMarker,this),this.setMyScope=a(this.setMyScope,this),this.destroy=a(this.destroy,this),_.each(this.keys,function(a){return function(b,c){return a[c+"Key"]=_.isFunction(a.keys[c])?a.keys[c]():a.keys[c]}}(this)),this.idKey=this.idKeyKey||"id",null!=this.model[this.idKey]&&(this.id=this.model[this.idKey]),this.needRedraw=!1,this.deferred=Promise.defer(),h.__super__.constructor.call(this,b),this.setMyScope(this.model,void 0,!0),this.createMarker(this.model),this.trackModel?(this.scope.model=this.model,this.scope.$watch("model",function(a){return function(b,c){return b!==c?(a.setMyScope(b,c),a.needRedraw=!0):void 0}}(this),!0)):_.each(this.keys,function(a){return function(b,c){return a.scope.$watch(c,function(){return a.setMyScope(a.scope)})}}(this)),this.scope.$on("$destroy",function(a){return function(){return i(a)}}(this)),e.info(this)}var i;return c(h,b),h.include(d),h.include(f),h.include(g),i=function(a){return null!=(null!=a?a.gMarker:void 0)&&(a.removeEvents(a.externalListeners),a.removeEvents(a.internalListeners),null!=a?a.gMarker:void 0)?(null!=a&&a.gMarkerManager.remove(null!=a?a.gMarker:void 0,!0),delete a.gMarker):void 0},h.prototype.destroy=function(){return this.scope.$destroy()},h.prototype.setMyScope=function(a,b,c){return null==b&&(b=void 0),null==c&&(c=!1),this.maybeSetScopeValue("icon",a,b,this.iconKey,this.evalModelHandle,c,this.setIcon),this.maybeSetScopeValue("coords",a,b,this.coordsKey,this.evalModelHandle,c,this.setCoords),_.isFunction(this.clickKey)?this.scope.click=function(a){return function(){return a.clickKey(a.gMarker,"click",a.model,void 0)}}(this):(this.maybeSetScopeValue("click",a,b,this.clickKey,this.evalModelHandle,c),this.createMarker(a,b,c))},h.prototype.createMarker=function(a,b,c){return null==b&&(b=void 0),null==c&&(c=!1),this.maybeSetScopeValue("options",a,b,this.optionsKey,this.evalModelHandle,c,this.setOptions)},h.prototype.maybeSetScopeValue=function(a,b,c,d,e,f,g){var h,i;return null==g&&(g=void 0),void 0===c?(this.scope[a]=e(b,d),void(f||null!=g&&g(this.scope))):(i=e(c,d),h=e(b,d),h!==i&&(this.scope[a]=h,!f&&(null!=g&&g(this.scope),this.doDrawSelf))?this.gMarkerManager.draw():void 0)},h.prototype.setCoords=function(a){return a.$id===this.scope.$id&&void 0!==this.gMarker?null!=a.coords?this.validateCoords(this.scope.coords)?(this.gMarker.setPosition(this.getCoords(a.coords)),this.gMarker.setVisible(this.validateCoords(a.coords)),this.gMarkerManager.add(this.gMarker)):void e.debug("MarkerChild does not have coords yet. They may be defined later."):this.gMarkerManager.remove(this.gMarker):void 0},h.prototype.setIcon=function(a){return a.$id===this.scope.$id&&void 0!==this.gMarker?(this.gMarkerManager.remove(this.gMarker),this.gMarker.setIcon(a.icon),this.gMarkerManager.add(this.gMarker),this.gMarker.setPosition(this.getCoords(a.coords)),this.gMarker.setVisible(this.validateCoords(a.coords))):void 0},h.prototype.setOptions=function(a){var b;if(a.$id===this.scope.$id&&(null!=this.gMarker&&(this.gMarkerManager.remove(this.gMarker),delete this.gMarker),null!=(b=a.coords)?b:"function"==typeof a.icon?a.icon(null!=a.options):void 0))return this.opts=this.createOptions(a.coords,a.icon,a.options),delete this.gMarker,this.gMarker=this.isLabel(this.opts)?new MarkerWithLabel(this.setLabelOptions(this.opts)):new google.maps.Marker(this.opts),this.gMarker?this.deferred.resolve(this.gMarker):this.deferred.reject("gMarker is null"),this.model.fitKey&&this.gMarkerManager.fit(),this.externalListeners&&this.removeEvents(this.externalListeners),this.internalListeners&&this.removeEvents(this.internalListeners),this.externalListeners=this.setEvents(this.gMarker,this.scope,this.model,["dragend"]),this.internalListeners=this.setEvents(this.gMarker,{events:this.internalEvents(),$apply:function(){}},this.model),null!=this.id&&(this.gMarker.key=this.id),this.gMarkerManager.add(this.gMarker)},h.prototype.setLabelOptions=function(a){return a.labelAnchor=this.getLabelPositionPoint(a.labelAnchor),a},h.prototype.internalEvents=function(){return{dragend:function(a){return function(b,c,d,e){var f,g;return f=a.setCoordsFromEvent(a.modelOrKey(a.scope.model,a.coordsKey),a.gMarker.getPosition()),a.scope.model=a.setVal(d,a.coordsKey,f),null!=(null!=(g=a.scope.events)?g.dragend:void 0)&&a.scope.events.dragend(b,c,a.scope.model,e),a.scope.$apply()}}(this),click:function(a){return function(b,c,d,e){return a.doClick&&null!=a.scope.click?a.scope.$apply(a.scope.click(b,c,a.model,e)):void 0}}(this)}},h}(b)}])}.call(this),function(){var a={}.hasOwnProperty,b=function(b,c){function d(){this.constructor=b}for(var e in c)a.call(c,e)&&(b[e]=c[e]);return d.prototype=c.prototype,b.prototype=new d,b.__super__=c.prototype,b};angular.module("google-maps.directives.api".ns()).factory("PolygonChildModel".ns(),["PolygonOptionsBuilder".ns(),"Logger".ns(),"$timeout","array-sync".ns(),"GmapUtil".ns(),"EventsHelper".ns(),function(a,c,d,e,f,g){var h;return h=function(a){function d(a,b,d,f,h){var i,j,k;return this.scope=a,this.attrs=b,this.map=d,this.defaults=f,this.model=h,this.listeners=void 0,angular.isUndefined(a.path)||null===a.path||!this.validatePath(a.path)?void c.error("polygon: no valid path attribute found"):(j=this.convertPathPoints(a.path),k=new google.maps.Polygon(this.buildOpts(j)),a.fit&&this.extendMapBounds(this.map,j),!a["static"]&&angular.isDefined(a.editable)&&a.$watch("editable",function(a,b){return a!==b?k.setEditable(a):void 0}),angular.isDefined(a.draggable)&&a.$watch("draggable",function(a,b){return a!==b?k.setDraggable(a):void 0}),angular.isDefined(a.visible)&&a.$watch("visible",function(a,b){return a!==b?k.setVisible(a):void 0}),angular.isDefined(a.geodesic)&&a.$watch("geodesic",function(a){return function(b,c){return b!==c?k.setOptions(a.buildOpts(k.getPath())):void 0}}(this)),angular.isDefined(a.stroke)&&angular.isDefined(a.stroke.opacity)&&a.$watch("stroke.opacity",function(a){return function(){return k.setOptions(a.buildOpts(k.getPath()))}}(this)),angular.isDefined(a.stroke)&&angular.isDefined(a.stroke.weight)&&a.$watch("stroke.weight",function(a){return function(b,c){return b!==c?k.setOptions(a.buildOpts(k.getPath())):void 0}}(this)),angular.isDefined(a.stroke)&&angular.isDefined(a.stroke.color)&&a.$watch("stroke.color",function(a){return function(b,c){return b!==c?k.setOptions(a.buildOpts(k.getPath())):void 0}}(this)),angular.isDefined(a.fill)&&angular.isDefined(a.fill.color)&&a.$watch("fill.color",function(a){return function(b,c){return b!==c?k.setOptions(a.buildOpts(k.getPath())):void 0}}(this)),angular.isDefined(a.fill)&&angular.isDefined(a.fill.opacity)&&a.$watch("fill.opacity",function(a){return function(b,c){return b!==c?k.setOptions(a.buildOpts(k.getPath())):void 0}}(this)),angular.isDefined(a.zIndex)&&a.$watch("zIndex",function(a){return function(b,c){return b!==c?k.setOptions(a.buildOpts(k.getPath())):void 0}}(this)),angular.isDefined(a.events)&&null!==a.events&&angular.isObject(a.events)&&(this.listeners=g.setEvents(k,a,a)),i=e(k.getPath(),a,"path",function(b){return function(c){return a.fit?b.extendMapBounds(b.map,c):void 0}}(this)),void a.$on("$destroy",function(a){return function(){return k.setMap(null),a.removeEvents(a.listeners),i?(i(),i=null):void 0}}(this)))}return b(d,a),d.include(f),d.include(g),d}(a)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api".ns()).factory("PolylineChildModel".ns(),["PolylineOptionsBuilder".ns(),"Logger".ns(),"$timeout","array-sync".ns(),"GmapUtil".ns(),"EventsHelper".ns(),function(b,d,e,f,g,h){var i;return i=function(b){function e(b,c,e,g,h){var i;this.scope=b,this.attrs=c,this.map=e,this.defaults=g,this.model=h,this.clean=a(this.clean,this),i=function(a){return function(){var b;return b=a.convertPathPoints(a.scope.path),null!=a.polyline&&a.clean(),b.length>0&&(a.polyline=new google.maps.Polyline(a.buildOpts(b))),a.polyline?(a.scope.fit&&a.extendMapBounds(e,b),f(a.polyline.getPath(),a.scope,"path",function(b){return a.scope.fit?a.extendMapBounds(e,b):void 0}),a.listeners=a.model?a.setEvents(a.polyline,a.scope,a.model):a.setEvents(a.polyline,a.scope,a.scope)):void 0}}(this),i(),b.$watch("path",function(a){return function(b,c){return _.isEqual(b,c)&&a.polyline?void 0:i()}}(this)),!b["static"]&&angular.isDefined(b.editable)&&b.$watch("editable",function(a){return function(b,c){var d;return b!==c&&null!=(d=a.polyline)?d.setEditable(b):void 0}}(this)),angular.isDefined(b.draggable)&&b.$watch("draggable",function(a){return function(b,c){var d;return b!==c&&null!=(d=a.polyline)?d.setDraggable(b):void 0}}(this)),angular.isDefined(b.visible)&&b.$watch("visible",function(a){return function(b,c){var d;return b!==c&&null!=(d=a.polyline)?d.setVisible(b):void 0}}(this)),angular.isDefined(b.geodesic)&&b.$watch("geodesic",function(a){return function(b,c){var d;return b!==c&&null!=(d=a.polyline)?d.setOptions(a.buildOpts(a.polyline.getPath())):void 0}}(this)),angular.isDefined(b.stroke)&&angular.isDefined(b.stroke.weight)&&b.$watch("stroke.weight",function(a){return function(b,c){var d;return b!==c&&null!=(d=a.polyline)?d.setOptions(a.buildOpts(a.polyline.getPath())):void 0}}(this)),angular.isDefined(b.stroke)&&angular.isDefined(b.stroke.color)&&b.$watch("stroke.color",function(a){return function(b,c){var d;return b!==c&&null!=(d=a.polyline)?d.setOptions(a.buildOpts(a.polyline.getPath())):void 0}}(this)),angular.isDefined(b.stroke)&&angular.isDefined(b.stroke.opacity)&&b.$watch("stroke.opacity",function(a){return function(b,c){var d;return b!==c&&null!=(d=a.polyline)?d.setOptions(a.buildOpts(a.polyline.getPath())):void 0}}(this)),angular.isDefined(b.icons)&&b.$watch("icons",function(a){return function(b,c){var d;return b!==c&&null!=(d=a.polyline)?d.setOptions(a.buildOpts(a.polyline.getPath())):void 0}}(this)),b.$on("$destroy",function(a){return function(){return a.clean(),a.scope=null}}(this)),d.info(this)}return c(e,b),e.include(g),e.include(h),e.prototype.clean=function(){var a,b;return this.removeEvents(this.listeners),null!=(b=this.polyline)&&b.setMap(null),this.polyline=null,a?(a(),a=null):void 0},e.prototype.destroy=function(){return this.scope.$destroy()},e}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.child".ns()).factory("WindowChildModel".ns(),["BaseObject".ns(),"GmapUtil".ns(),"Logger".ns(),"$compile","$http","$templateCache",function(b,d,e,f,g,h){var i;return i=function(b){function i(b,c,d,f,g,h,i,j,k){this.model=b,this.scope=c,this.opts=d,this.isIconVisibleOnClick=f,this.mapCtrl=g,this.markerCtrl=h,this.element=i,this.needToManualDestroy=null!=j?j:!1,this.markerIsVisibleAfterWindowClose=null!=k?k:!0,this.getGWin=a(this.getGWin,this),this.destroy=a(this.destroy,this),this.remove=a(this.remove,this),this.getLatestPosition=a(this.getLatestPosition,this),this.hideWindow=a(this.hideWindow,this),this.showWindow=a(this.showWindow,this),this.handleClick=a(this.handleClick,this),this.watchOptions=a(this.watchOptions,this),this.watchCoords=a(this.watchCoords,this),this.createGWin=a(this.createGWin,this),this.watchElement=a(this.watchElement,this),this.watchAndDoShow=a(this.watchAndDoShow,this),this.doShow=a(this.doShow,this),this.googleMapsHandles=[],this.$log=e,this.createGWin(),null!=this.markerCtrl&&this.markerCtrl.setClickable(!0),this.watchElement(),this.watchOptions(),this.watchCoords(),this.watchAndDoShow(),this.scope.$on("$destroy",function(a){return function(){return a.destroy()}}(this)),this.$log.info(this)}return c(i,b),i.include(d),i.prototype.doShow=function(){return this.scope.show?this.showWindow():void 0},i.prototype.watchAndDoShow=function(){return null!=this.model.show&&(this.scope.show=this.model.show),this.scope.$watch("show",this.doShow,!0),this.doShow()},i.prototype.watchElement=function(){return this.scope.$watch(function(a){return function(){var b;if(a.element&&a.html)return a.html!==a.element.html()&&a.gWin?(null!=(b=a.opts)&&(b.content=void 0),a.remove(),a.createGWin()):void 0}}(this))},i.prototype.createGWin=function(){var a,b;return null==this.gWin&&(a={},null!=this.opts&&(this.scope.coords&&(this.opts.position=this.getCoords(this.scope.coords)),a=this.opts),this.element&&(this.html=_.isObject(this.element)?this.element.html():this.element),b=this.scope.options?this.scope.options:a,this.opts=this.createWindowOptions(this.markerCtrl,this.scope,this.html,b)),null==this.opts||this.gWin?void 0:(this.gWin=this.opts.boxClass&&window.InfoBox&&"function"==typeof window.InfoBox?new window.InfoBox(this.opts):new google.maps.InfoWindow(this.opts),this.handleClick(),this.doShow(),this.googleMapsHandles.push(google.maps.event.addListener(this.gWin,"closeclick",function(a){return function(){return a.markerCtrl&&(a.markerCtrl.setAnimation(a.oldMarkerAnimation),a.markerIsVisibleAfterWindowClose&&_.delay(function(){return a.markerCtrl.setVisible(!1),a.markerCtrl.setVisible(a.markerIsVisibleAfterWindowClose)},250)),a.gWin.isOpen(!1),null!=a.scope.closeClick?a.scope.$apply(a.scope.closeClick()):void 0}}(this))))},i.prototype.watchCoords=function(){var a;return a=null!=this.markerCtrl?this.scope.$parent:this.scope,a.$watch("coords",function(a){return function(b,c){var d;if(b!==c){if(null==b)return a.hideWindow();if(!a.validateCoords(b))return void a.$log.error("WindowChildMarker cannot render marker as scope.coords as no position on marker: "+JSON.stringify(a.model));if(d=a.getCoords(b),a.gWin.setPosition(d),a.opts)return a.opts.position=d}}}(this),!0)},i.prototype.watchOptions=function(){var a;return a=null!=this.markerCtrl?this.scope.$parent:this.scope,this.scope.$watch("options",function(a){return function(b,c){if(b!==c&&(a.opts=b,null!=a.gWin)){if(a.gWin.setOptions(a.opts),null!=a.opts.visible&&a.opts.visible)return a.showWindow();if(null!=a.opts.visible)return a.hideWindow()}}}(this),!0)},i.prototype.handleClick=function(a){var b;if(null!=this.gWin)return b=function(a){return function(){var b;return null==a.gWin&&a.createGWin(),b=a.markerCtrl.getPosition(),null!=a.gWin&&(a.gWin.setPosition(b),a.opts&&(a.opts.position=b),a.showWindow()),a.initialMarkerVisibility=a.markerCtrl.getVisible(),a.oldMarkerAnimation=a.markerCtrl.getAnimation(),a.markerCtrl.setVisible(a.isIconVisibleOnClick)}}(this),null!=this.markerCtrl?(a&&b(),this.googleMapsHandles.push(google.maps.event.addListener(this.markerCtrl,"click",b))):void 0},i.prototype.showWindow=function(){var a,b,c;return b=function(a){return function(){return null==a.gWin||a.gWin.isOpen()?void 0:a.gWin.open(a.mapCtrl)}}(this),this.scope.templateUrl?null!=this.gWin&&g.get(this.scope.templateUrl,{cache:h}).then(function(a){return function(b){var c,d;return d=a.scope.$new(),angular.isDefined(a.scope.templateParameter)&&(d.parameter=a.scope.templateParameter),c=f(b.data)(d),a.gWin.setContent(c[0])}}(this)):this.scope.template&&null!=this.gWin&&(c=this.scope.$new(),angular.isDefined(this.scope.templateParameter)&&(c.parameter=this.scope.templateParameter),a=f(this.scope.template)(c),this.gWin.setContent(a[0])),b()},i.prototype.hideWindow=function(){return null!=this.gWin&&this.gWin.isOpen()?this.gWin.close():void 0},i.prototype.getLatestPosition=function(a){return null==this.gWin||null==this.markerCtrl||a?a?this.gWin.setPosition(a):void 0:this.gWin.setPosition(this.markerCtrl.getPosition())},i.prototype.remove=function(){return this.hideWindow(),_.each(this.googleMapsHandles,function(a){return google.maps.event.removeListener(a)}),this.googleMapsHandles.length=0,delete this.gWin,delete this.opts},i.prototype.destroy=function(a){var b,c;return null==a&&(a=!1),this.remove(),null!=this.scope&&(null!=(c=this.scope)?c.$$destroyed:void 0)&&(this.needToManualDestroy||a)&&this.scope.$destroy(),b=void 0},i.prototype.getGWin=function(){return this.gWin},i}(b)}])}.call(this),function(){var a={}.hasOwnProperty,b=function(b,c){function d(){this.constructor=b}for(var e in c)a.call(c,e)&&(b[e]=c[e]);return d.prototype=c.prototype,b.prototype=new d,b.__super__=c.prototype,b};angular.module("google-maps.directives.api.models.parent".ns()).factory("CircleParentModel".ns(),["Logger".ns(),"$timeout","GmapUtil".ns(),"EventsHelper".ns(),"CircleOptionsBuilder".ns(),function(a,c,d,e,f){var g;return g=function(f){function g(b,e,f,g,h){var i,j;this.scope=b,this.attrs=f,this.map=g,this.DEFAULTS=h,i=new google.maps.Circle(this.buildOpts(d.getCoords(b.center),b.radius)),this.setMyOptions=function(a){return function(c,e){return _.isEqual(c,e)?void 0:i.setOptions(a.buildOpts(d.getCoords(b.center),b.radius))}}(this),this.props=this.props.concat([{prop:"center",isColl:!0},{prop:"fill",isColl:!0},"radius"]),this.watchProps(),j=this.setEvents(i,b,b),google.maps.event.addListener(i,"radius_changed",function(){return b.radius=i.getRadius(),c(function(){return b.$apply()})}),google.maps.event.addListener(i,"center_changed",function(){return angular.isDefined(b.center.type)?(b.center.coordinates[1]=i.getCenter().lat(),b.center.coordinates[0]=i.getCenter().lng()):(b.center.latitude=i.getCenter().lat(),b.center.longitude=i.getCenter().lng()),c(function(){return b.$apply()})}),b.$on("$destroy",function(a){return function(){return a.removeEvents(j),i.setMap(null)}}(this)),a.info(this)}return b(g,f),g.include(d),g.include(e),g}(f)}])}.call(this),function(){angular.module("google-maps.directives.api.models.parent".ns()).factory("DrawingManagerParentModel".ns(),["Logger".ns(),"$timeout",function(){var a;return a=function(){function a(a,b,c,d){var e;this.scope=a,this.attrs=c,this.map=d,e=new google.maps.drawing.DrawingManager(this.scope.options),e.setMap(this.map),null!=this.scope.control&&(this.scope.control.getDrawingManager=function(){return function(){return e}}(this)),!this.scope["static"]&&this.scope.options&&this.scope.$watch("options",function(){return function(a){return null!=e?e.setOptions(a):void 0}}(this),!0),a.$on("$destroy",function(){return function(){return e.setMap(null),e=null}}(this))}return a}()}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.parent".ns()).factory("IMarkerParentModel".ns(),["ModelKey".ns(),"Logger".ns(),function(b,d){var e;return e=function(b){function e(b,c,f,g){if(this.scope=b,this.element=c,this.attrs=f,this.map=g,this.onDestroy=a(this.onDestroy,this),this.onWatch=a(this.onWatch,this),this.watch=a(this.watch,this),this.validateScope=a(this.validateScope,this),e.__super__.constructor.call(this,this.scope),this.$log=d,!this.validateScope(b))throw new String("Unable to construct IMarkerParentModel due to invalid scope");this.doClick=angular.isDefined(f.click),null!=b.options&&(this.DEFAULTS=b.options),this.watch("coords",this.scope),this.watch("icon",this.scope),this.watch("options",this.scope),b.$on("$destroy",function(a){return function(){return a.onDestroy(b)}}(this))}return c(e,b),e.prototype.DEFAULTS={},e.prototype.validateScope=function(a){var b;return null==a?(this.$log.error(this.constructor.name+": invalid scope used"),!1):(b=null!=a.coords,b?b:(this.$log.error(this.constructor.name+": no valid coords attribute found"),!1))},e.prototype.watch=function(a,b){return b.$watch(a,function(c){return function(d,e){return _.isEqual(d,e)?void 0:c.onWatch(a,b,d,e)}}(this),!0)},e.prototype.onWatch=function(){},e.prototype.onDestroy=function(){throw new String("OnDestroy Not Implemented!!")},e}(b)}])}.call(this),function(){var a={}.hasOwnProperty,b=function(b,c){function d(){this.constructor=b}for(var e in c)a.call(c,e)&&(b[e]=c[e]);return d.prototype=c.prototype,b.prototype=new d,b.__super__=c.prototype,b};angular.module("google-maps.directives.api.models.parent".ns()).factory("IWindowParentModel".ns(),["ModelKey".ns(),"GmapUtil".ns(),"Logger".ns(),function(a,c,d){var e;return e=function(a){function e(a,b,c,f,g,h,i,j){e.__super__.constructor.call(this,a),this.$log=d,this.$timeout=g,this.$compile=h,this.$http=i,this.$templateCache=j,this.DEFAULTS={},null!=a.options&&(this.DEFAULTS=a.options)}return b(e,a),e.include(c),e}(a)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.parent".ns()).factory("LayerParentModel".ns(),["BaseObject".ns(),"Logger".ns(),"$timeout",function(b,d){var e;return e=function(b){function e(b,c,e,f,g,h){return this.scope=b,this.element=c,this.attrs=e,this.gMap=f,this.onLayerCreated=null!=g?g:void 0,this.$log=null!=h?h:d,this.createGoogleLayer=a(this.createGoogleLayer,this),null==this.attrs.type?void this.$log.info("type attribute for the layer directive is mandatory. Layer creation aborted!!"):(this.createGoogleLayer(),this.doShow=!0,angular.isDefined(this.attrs.show)&&(this.doShow=this.scope.show),this.doShow&&null!=this.gMap&&this.layer.setMap(this.gMap),this.scope.$watch("show",function(a){return function(b,c){return b!==c?(a.doShow=b,a.layer.setMap(b?a.gMap:null)):void 0}}(this),!0),this.scope.$watch("options",function(a){return function(b,c){return b!==c?(a.layer.setMap(null),a.layer=null,a.createGoogleLayer()):void 0}}(this),!0),void this.scope.$on("$destroy",function(a){return function(){return a.layer.setMap(null)}}(this)))}return c(e,b),e.prototype.createGoogleLayer=function(){var a;return this.layer=null==this.attrs.options?void 0===this.attrs.namespace?new google.maps[this.attrs.type]:new google.maps[this.attrs.namespace][this.attrs.type]:void 0===this.attrs.namespace?new google.maps[this.attrs.type](this.scope.options):new google.maps[this.attrs.namespace][this.attrs.type](this.scope.options),null!=this.layer&&null!=this.onLayerCreated&&(a=this.onLayerCreated(this.scope,this.layer))?a(this.layer):void 0},e}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.parent".ns()).factory("MapTypeParentModel".ns(),["BaseObject".ns(),"Logger".ns(),"$timeout",function(b,d){var e;return e=function(b){function e(b,c,e,f,g){return this.scope=b,this.element=c,this.attrs=e,this.gMap=f,this.$log=null!=g?g:d,this.hideOverlay=a(this.hideOverlay,this),this.showOverlay=a(this.showOverlay,this),this.refreshMapType=a(this.refreshMapType,this),this.createMapType=a(this.createMapType,this),null==this.attrs.options?void this.$log.info("options attribute for the map-type directive is mandatory. Map type creation aborted!!"):(this.id=this.gMap.overlayMapTypesCount=this.gMap.overlayMapTypesCount+1||0,this.doShow=!0,this.createMapType(),angular.isDefined(this.attrs.show)&&(this.doShow=this.scope.show),this.doShow&&null!=this.gMap&&this.showOverlay(),this.scope.$watch("show",function(a){return function(b,c){return b!==c?(a.doShow=b,b?a.showOverlay():a.hideOverlay()):void 0}}(this),!0),this.scope.$watch("options",function(a){return function(b,c){return _.isEqual(b,c)?void 0:a.refreshMapType()}}(this),!0),angular.isDefined(this.attrs.refresh)&&this.scope.$watch("refresh",function(a){return function(b,c){return _.isEqual(b,c)?void 0:a.refreshMapType()}}(this),!0),void this.scope.$on("$destroy",function(a){return function(){return a.hideOverlay(),a.mapType=null}}(this)))}return c(e,b),e.prototype.createMapType=function(){if(null!=this.scope.options.getTile)this.mapType=this.scope.options;else{if(null==this.scope.options.getTileUrl)return void this.$log.info("options should provide either getTile or getTileUrl methods. Map type creation aborted!!");this.mapType=new google.maps.ImageMapType(this.scope.options)}return this.attrs.id&&this.scope.id&&(this.gMap.mapTypes.set(this.scope.id,this.mapType),angular.isDefined(this.attrs.show)||(this.doShow=!1)),this.mapType.layerId=this.id},e.prototype.refreshMapType=function(){return this.hideOverlay(),this.mapType=null,this.createMapType(),this.doShow&&null!=this.gMap?this.showOverlay():void 0},e.prototype.showOverlay=function(){return this.gMap.overlayMapTypes.push(this.mapType)},e.prototype.hideOverlay=function(){var a;return a=!1,this.gMap.overlayMapTypes.forEach(function(b){return function(c,d){a||c.layerId!==b.id||(a=!0,b.gMap.overlayMapTypes.removeAt(d))}}(this))},e}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.parent".ns()).factory("MarkersParentModel".ns(),["IMarkerParentModel".ns(),"ModelsWatcher".ns(),"PropMap".ns(),"MarkerChildModel".ns(),"_async".ns(),"ClustererMarkerManager".ns(),"MarkerManager".ns(),"$timeout","IMarker".ns(),function(b,d,e,f,g,h,i,j,k){var l;return l=function(b){function j(b,c,d,f){this.onDestroy=a(this.onDestroy,this),this.newChildMarker=a(this.newChildMarker,this),this.updateChild=a(this.updateChild,this),this.pieceMeal=a(this.pieceMeal,this),this.reBuildMarkers=a(this.reBuildMarkers,this),this.createMarkersFromScratch=a(this.createMarkersFromScratch,this),this.validateScope=a(this.validateScope,this),this.onWatch=a(this.onWatch,this);var g;j.__super__.constructor.call(this,b,c,d,f),g=this,this.scope.markerModels=new e,this.$log.info(this),this.doRebuildAll=null!=this.scope.doRebuildAll?this.scope.doRebuildAll:!1,this.setIdKey(b),this.scope.$watch("doRebuildAll",function(a){return function(b,c){return b!==c?a.doRebuildAll=b:void 0}}(this)),this.watch("models",b),this.watch("doCluster",b),this.watch("clusterOptions",b),this.watch("clusterEvents",b),this.watch("fit",b),this.watch("idKey",b),this.gMarkerManager=void 0,this.createMarkersFromScratch(b)}return c(j,b),j.include(d),j.prototype.onWatch=function(a,b,c,d){return"idKey"===a&&c!==d&&(this.idKey=c),this.doRebuildAll?this.reBuildMarkers(b):this.pieceMeal(b)},j.prototype.validateScope=function(a){var b;return b=angular.isUndefined(a.models)||void 0===a.models,b&&this.$log.error(this.constructor.name+": no valid models attribute found"),j.__super__.validateScope.call(this,a)||b},j.prototype.createMarkersFromScratch=function(a){return a.doCluster?(a.clusterEvents&&(this.clusterInternalOptions=_.once(function(b){return function(){var c,d,e,f;return c=b,b.origClusterEvents?void 0:(b.origClusterEvents={click:null!=(d=a.clusterEvents)?d.click:void 0,mouseout:null!=(e=a.clusterEvents)?e.mouseout:void 0,mouseover:null!=(f=a.clusterEvents)?f.mouseover:void 0},_.extend(a.clusterEvents,{click:function(a){return c.maybeExecMappedEvent(a,"click")},mouseout:function(a){return c.maybeExecMappedEvent(a,"mouseout")},mouseover:function(a){return c.maybeExecMappedEvent(a,"mouseover")}}))}}(this))()),a.clusterOptions||a.clusterEvents?void 0===this.gMarkerManager?this.gMarkerManager=new h(this.map,void 0,a.clusterOptions,this.clusterInternalOptions):this.gMarkerManager.opt_options!==a.clusterOptions&&(this.gMarkerManager=new h(this.map,void 0,a.clusterOptions,this.clusterInternalOptions)):this.gMarkerManager=new h(this.map)):this.gMarkerManager=new i(this.map),g.waitOrGo(this,function(b){return function(){return g.each(a.models,function(c){return b.newChildMarker(c,a)},!1).then(function(){return b.gMarkerManager.draw(),a.fit?b.gMarkerManager.fit():void 0})}}(this)).then(function(a){return function(){return a.existingPieces=void 0}}(this))},j.prototype.reBuildMarkers=function(a){var b;if(a.doRebuild||void 0===a.doRebuild)return(null!=(b=this.scope.markerModels)?b.length:void 0)&&this.onDestroy(a),this.createMarkersFromScratch(a)},j.prototype.pieceMeal=function(a){var b;return b=null!=this.existingPieces?!1:g.defaultChunkSize,null!=this.scope.models&&this.scope.models.length>0&&this.scope.markerModels.length>0?this.figureOutState(this.idKey,a,this.scope.markerModels,this.modelKeyComparison,function(c){return function(d){var e;return e=d,g.waitOrGo(c,function(){return g.each(e.removals,function(a){return null!=a?(null!=a.destroy&&a.destroy(),c.scope.markerModels.remove(a.id)):void 0},b).then(function(){return g.each(e.adds,function(b){return c.newChildMarker(b,a)},b)}).then(function(){return g.each(e.updates,function(a){return c.updateChild(a.child,a.model)},b)}).then(function(){return(e.adds.length>0||e.removals.length>0||e.updates.length>0)&&(c.gMarkerManager.draw(),a.markerModels=c.scope.markerModels,a.fit)?c.gMarkerManager.fit():void 0})}).then(function(){return c.existingPieces=void 0})}}(this)):this.reBuildMarkers(a)},j.prototype.updateChild=function(a,b){return null==b[this.idKey]?void this.$log.error("Marker model has no id to assign a child to. This is required for performance. Please assign id, or redirect id to a different key."):a.setMyScope(b,a.model,!1)},j.prototype.newChildMarker=function(a,b){var c,d,e,g;return null==a[this.idKey]?void this.$log.error("Marker model has no id to assign a child to. This is required for performance. Please assign id, or redirect id to a different key."):(this.$log.info("child",c,"markers",this.scope.markerModels),d=b.$new(!1),d.events=b.events,g={},_.each(k.keys,function(a,c){return g[c]=b[c]}),c=new f(d,a,g,this.map,this.DEFAULTS,this.doClick,this.gMarkerManager,e=!1),this.scope.markerModels.put(a[this.idKey],c),c)},j.prototype.onDestroy=function(){return g.waitOrGo(this,function(a){return function(){return null!=a.gMarkerManager&&a.gMarkerManager.clear(),_.each(a.scope.markerModels.values(),function(a){return null!=a?a.destroy():void 0}),delete a.scope.markerModels,a.scope.markerModels=new e,Promise.resolve() +}}(this))},j.prototype.maybeExecMappedEvent=function(a,b){var c,d;return _.isFunction(null!=(d=this.scope.clusterEvents)?d[b]:void 0)&&(c=this.mapClusterToMarkerModels(a),this.origClusterEvents[b])?this.origClusterEvents[b](c.cluster,c.mapped):void 0},j.prototype.mapClusterToMarkerModels=function(a){var b,c;return b=a.getMarkers().values(),c=b.map(function(a){return function(b){return a.scope.markerModels[b.key].model}}(this)),{cluster:a,mapped:c}},j}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.parent".ns()).factory("PolylinesParentModel".ns(),["$timeout","Logger".ns(),"ModelKey".ns(),"ModelsWatcher".ns(),"PropMap".ns(),"PolylineChildModel".ns(),"_async".ns(),function(b,d,e,f,g,h,i){var j;return j=function(b){function e(b,c,f,h,i){var j;this.scope=b,this.element=c,this.attrs=f,this.gMap=h,this.defaults=i,this.modelKeyComparison=a(this.modelKeyComparison,this),this.setChildScope=a(this.setChildScope,this),this.createChild=a(this.createChild,this),this.pieceMeal=a(this.pieceMeal,this),this.createAllNew=a(this.createAllNew,this),this.watchIdKey=a(this.watchIdKey,this),this.createChildScopes=a(this.createChildScopes,this),this.watchOurScope=a(this.watchOurScope,this),this.watchDestroy=a(this.watchDestroy,this),this.rebuildAll=a(this.rebuildAll,this),this.doINeedToWipe=a(this.doINeedToWipe,this),this.watchModels=a(this.watchModels,this),this.watch=a(this.watch,this),e.__super__.constructor.call(this,b),j=this,this.$log=d,this.plurals=new g,this.scopePropNames=["path","stroke","clickable","draggable","editable","geodesic","icons","visible"],_.each(this.scopePropNames,function(a){return function(b){return a[b+"Key"]=void 0}}(this)),this.models=void 0,this.firstTime=!0,this.$log.info(this),this.watchOurScope(b),this.createChildScopes()}return c(e,b),e.include(f),e.prototype.watch=function(a,b,c){return a.$watch(b,function(a){return function(d,e){return d!==e?(a[c]="function"==typeof d?d():d,i.waitOrGo(a,function(){return i.each(_.values(a.plurals),function(d){return d.scope[b]="self"===a[c]?d:d[a[c]]})})):void 0}}(this))},e.prototype.watchModels=function(a){return a.$watch("models",function(b){return function(c,d){return _.isEqual(c,d)?void 0:b.doINeedToWipe(c)?b.rebuildAll(a,!0,!0):b.createChildScopes(!1)}}(this),!0)},e.prototype.doINeedToWipe=function(a){var b;return b=null!=a?0===a.length:!0,this.plurals.length>0&&b},e.prototype.rebuildAll=function(a,b,c){return i.waitOrGo(this,function(a){return function(){return i.each(a.plurals.values(),function(a){return a.destroy()}).then(function(){return c&&delete a.plurals,a.plurals=new g,b?a.createChildScopes():void 0})}}(this))},e.prototype.watchDestroy=function(a){return a.$on("$destroy",function(b){return function(){return b.rebuildAll(a,!1,!0)}}(this))},e.prototype.watchOurScope=function(a){return _.each(this.scopePropNames,function(b){return function(c){var d;return d=c+"Key",b[d]="function"==typeof a[c]?a[c]():a[c],b.watch(a,c,d)}}(this))},e.prototype.createChildScopes=function(a){return null==a&&(a=!0),angular.isUndefined(this.scope.models)?void this.$log.error("No models to create polylines from! I Need direct models!"):null!=this.gMap&&null!=this.scope.models?(this.watchIdKey(this.scope),a?this.createAllNew(this.scope,!1):this.pieceMeal(this.scope,!1)):void 0},e.prototype.watchIdKey=function(a){return this.setIdKey(a),a.$watch("idKey",function(b){return function(c,d){return c!==d&&null==c?(b.idKey=c,b.rebuildAll(a,!0,!0)):void 0}}(this))},e.prototype.createAllNew=function(a,b){return null==b&&(b=!1),this.models=a.models,this.firstTime&&(this.watchModels(a),this.watchDestroy(a)),i.waitOrGo(this,function(b){return function(){return i.each(a.models,function(a){return b.createChild(a,b.gMap)})}}(this)).then(function(a){return function(){return a.firstTime=!1,a.existingPieces=void 0}}(this))},e.prototype.pieceMeal=function(a,b){var c;return null==b&&(b=!0),c=null!=this.existingPieces?!1:i.defaultChunkSize,this.models=a.models,null!=a&&null!=a.models&&a.models.length>0&&this.plurals.length>0?this.figureOutState(this.idKey,a,this.plurals,this.modelKeyComparison,function(a){return function(b){var c;return c=b,i.waitOrGo(a,function(){return i.each(c.removals,function(b){var c;return c=a.plurals[b],null!=c?(c.destroy(),a.plurals.remove(b)):void 0}).then(function(){return i.each(c.adds,function(b){return a.createChild(b,a.gMap)})}).then(function(){return a.existingPieces=void 0})})}}(this)):this.rebuildAll(this.scope,!0,!0)},e.prototype.createChild=function(a,b){var c,d;return d=this.scope.$new(!1),this.setChildScope(d,a),d.$watch("model",function(a){return function(b,c){return b!==c?a.setChildScope(d,b):void 0}}(this),!0),d["static"]=this.scope["static"],c=new h(d,this.attrs,b,this.defaults,a),null==a[this.idKey]?void this.$log.error("Polyline model has no id to assign a child to. This is required for performance. Please assign id, or redirect id to a different key."):(this.plurals.put(a[this.idKey],c),c)},e.prototype.setChildScope=function(a,b){return _.each(this.scopePropNames,function(c){return function(d){var e,f;return e=d+"Key",f="self"===c[e]?b:b[c[e]],f!==a[d]?a[d]=f:void 0}}(this)),a.model=b},e.prototype.modelKeyComparison=function(a,b){return _.isEqual(this.evalModelHandle(a,this.scope.path),this.evalModelHandle(b,this.scope.path))},e}(e)}])}.call(this),function(){var a={}.hasOwnProperty,b=function(b,c){function d(){this.constructor=b}for(var e in c)a.call(c,e)&&(b[e]=c[e]);return d.prototype=c.prototype,b.prototype=new d,b.__super__=c.prototype,b};angular.module("google-maps.directives.api.models.parent".ns()).factory("RectangleParentModel".ns(),["Logger".ns(),"GmapUtil".ns(),"EventsHelper".ns(),"RectangleOptionsBuilder".ns(),function(a,c,d,e){var f;return f=function(e){function f(b,c,d,e,f){var g,h,i,j,k,l,m,n,o,p,q;this.scope=b,this.attrs=d,this.map=e,this.DEFAULTS=f,g=void 0,j=!1,n=[],m=void 0,k=function(a){return function(){return a.isTrue(d.fit)?a.fitMapBounds(a.map,g):void 0}}(this),i=function(c){return function(){var d,e;return null!=b.bounds&&null!=(null!=(d=b.bounds)?d.sw:void 0)&&null!=(null!=(e=b.bounds)?e.ne:void 0)&&c.validateBoundPoints(b.bounds)?(g=c.convertBoundPoints(b.bounds),a.info("new new bounds created: "+o)):null!=b.bounds.getNorthEast&&null!=b.bounds.getSouthWest?g=b.bounds:"undefined"!=typeof bound&&null!==bound?a.error("Invalid bounds for newValue: "+JSON.stringify(b.bounds)):void 0}}(this),i(),o=new google.maps.Rectangle(this.buildOpts(g)),a.info("rectangle created: "+o),p=!1,q=function(){return function(){var a,c,d;return a=o.getBounds(),c=a.getNorthEast(),d=a.getSouthWest(),p?void 0:_.defer(function(){return b.$apply(function(b){return null!=b.bounds&&null!=b.bounds.sw&&null!=b.bounds.ne&&(b.bounds.ne={latitude:c.lat(),longitude:c.lng()},b.bounds.sw={latitude:d.lat(),longitude:d.lng()}),null!=b.bounds.getNorthEast&&null!=b.bounds.getSouthWest?b.bounds=a:void 0})})}}(this),l=function(a){return function(){return k(),a.removeEvents(n),n.push(google.maps.event.addListener(o,"dragstart",function(){return j=!0})),n.push(google.maps.event.addListener(o,"dragend",function(){return j=!1,q()})),n.push(google.maps.event.addListener(o,"bounds_changed",function(){return j?void 0:q()}))}}(this),h=function(a){return function(){return a.removeEvents(n),null!=m&&a.removeEvents(m),o.setMap(null)}}(this),null!=g&&l(),b.$watch("bounds",function(a,b){var c;if(!(_.isEqual(a,b)&&null!=g||j))return p=!0,null==a?void h():(null==g?c=!0:k(),i(),o.setBounds(g),p=!1,c&&null!=g?l():void 0)},!0),this.setMyOptions=function(a){return function(b,c){return _.isEqual(b,c)||null==g||null==b?void 0:o.setOptions(a.buildOpts(g))}}(this),this.props.push("bounds"),this.watchProps(this.props),null!=d.events&&(m=this.setEvents(o,b,b),b.$watch("events",function(a){return function(c,d){return _.isEqual(c,d)?void 0:(null!=m&&a.removeEvents(m),m=a.setEvents(o,b,b))}}(this))),b.$on("$destroy",function(){return function(){return h()}}(this)),a.info(this)}return b(f,e),f.include(c),f.include(d),f}(e)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.parent".ns()).factory("SearchBoxParentModel".ns(),["BaseObject".ns(),"Logger".ns(),"EventsHelper".ns(),"$timeout","$http","$templateCache",function(b,d,e){var f;return f=function(b){function f(b,c,e,f,g,h,i){var j;return this.scope=b,this.element=c,this.attrs=e,this.gMap=f,this.ctrlPosition=g,this.template=h,this.$log=null!=i?i:d,this.getBounds=a(this.getBounds,this),this.setBounds=a(this.setBounds,this),this.createSearchBox=a(this.createSearchBox,this),this.addToParentDiv=a(this.addToParentDiv,this),this.addAsMapControl=a(this.addAsMapControl,this),this.init=a(this.init,this),null==this.attrs.template?void this.$log.error("template attribute for the search-box directive is mandatory. Places Search Box creation aborted!!"):(j=angular.element("
"),j.append(this.template),this.input=j.find("input")[0],void this.init())}return c(f,b),f.include(e),f.prototype.init=function(){return this.createSearchBox(),null!=this.attrs.parentdiv?this.addToParentDiv():this.addAsMapControl(),this.listener=google.maps.event.addListener(this.searchBox,"places_changed",function(a){return function(){return a.places=a.searchBox.getPlaces()}}(this)),this.listeners=this.setEvents(this.searchBox,this.scope,this.scope),this.$log.info(this),this.scope.$watch("options",function(a){return function(b){return angular.isObject(b)&&null!=b.bounds?a.setBounds(b.bounds):void 0}}(this),!0),this.scope.$on("$destroy",function(a){return function(){return a.searchBox=null}}(this))},f.prototype.addAsMapControl=function(){return this.gMap.controls[google.maps.ControlPosition[this.ctrlPosition]].push(this.input)},f.prototype.addToParentDiv=function(){return this.parentDiv=angular.element(document.getElementById(this.scope.parentdiv)),this.parentDiv.append(this.input)},f.prototype.createSearchBox=function(){return this.searchBox=new google.maps.places.SearchBox(this.input,this.scope.options)},f.prototype.setBounds=function(a){if(angular.isUndefined(a.isEmpty))this.$log.error("Error: SearchBoxParentModel setBounds. Bounds not an instance of LatLngBounds.");else if(a.isEmpty()===!1&&null!=this.searchBox)return this.searchBox.setBounds(a)},f.prototype.getBounds=function(){return this.searchBox.getBounds()},f}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api.models.parent".ns()).factory("WindowsParentModel".ns(),["IWindowParentModel".ns(),"ModelsWatcher".ns(),"PropMap".ns(),"WindowChildModel".ns(),"Linked".ns(),"_async".ns(),"Logger".ns(),"$timeout","$compile","$http","$templateCache","$interpolate",function(b,d,e,f,g,h,i,j,k,l,m,n){var o;return o=function(b){function o(b,c,d,f,h,i){var n;this.gMap=h,this.markersScope=i,this.interpolateContent=a(this.interpolateContent,this),this.setChildScope=a(this.setChildScope,this),this.createWindow=a(this.createWindow,this),this.setContentKeys=a(this.setContentKeys,this),this.pieceMealWindows=a(this.pieceMealWindows,this),this.createAllNewWindows=a(this.createAllNewWindows,this),this.watchIdKey=a(this.watchIdKey,this),this.createChildScopesWindows=a(this.createChildScopesWindows,this),this.watchOurScope=a(this.watchOurScope,this),this.watchDestroy=a(this.watchDestroy,this),this.rebuildAll=a(this.rebuildAll,this),this.doINeedToWipe=a(this.doINeedToWipe,this),this.watchModels=a(this.watchModels,this),this.go=a(this.go,this),o.__super__.constructor.call(this,b,c,d,f,j,k,l,m),n=this,this.windows=new e,this.scopePropNames=["coords","template","templateUrl","templateParameter","isIconVisibleOnClick","closeClick","options","show"],_.each(this.scopePropNames,function(a){return function(b){return a[b+"Key"]=void 0}}(this)),this.linked=new g(b,c,d,f),this.models=void 0,this.contentKeys=void 0,this.isIconVisibleOnClick=void 0,this.firstTime=!0,this.firstWatchModels=!0,this.$log.info(n),this.parentScope=void 0,this.go(b)}return c(o,b),o.include(d),o.prototype.go=function(a){return this.watchOurScope(a),this.doRebuildAll=null!=this.scope.doRebuildAll?this.scope.doRebuildAll:!1,a.$watch("doRebuildAll",function(a){return function(b,c){return b!==c?a.doRebuildAll=b:void 0}}(this)),this.createChildScopesWindows()},o.prototype.watchModels=function(a){return a.$watch("models",function(b){return function(c,d){var e;return!_.isEqual(c,d)||b.firstWatchModels?(b.firstWatchModels=!1,b.doRebuildAll||b.doINeedToWipe(c)?b.rebuildAll(a,!0,!0):(e=0===b.windows.length,null!=b.existingPieces?b.existingPieces.then(function(){return b.createChildScopesWindows(e)}):b.createChildScopesWindows(e))):void 0}}(this))},o.prototype.doINeedToWipe=function(a){var b;return b=null!=a?0===a.length:!0,this.windows.length>0&&b},o.prototype.rebuildAll=function(a,b,c){return h.waitOrGo(this,function(a){return function(){return h.each(a.windows.values(),function(a){return a.destroy()}).then(function(){return c&&delete a.windows,a.windows=new e,b&&a.createChildScopesWindows(),Promise.resolve()})}}(this))},o.prototype.watchDestroy=function(a){return a.$on("$destroy",function(b){return function(){return b.firstWatchModels=!0,b.firstTime=!0,b.rebuildAll(a,!1,!0)}}(this))},o.prototype.watchOurScope=function(a){return _.each(this.scopePropNames,function(b){return function(c){var d;return d=c+"Key",b[d]="function"==typeof a[c]?a[c]():a[c]}}(this))},o.prototype.createChildScopesWindows=function(a){var b,c,d;return null==a&&(a=!0),this.isIconVisibleOnClick=!0,angular.isDefined(this.linked.attrs.isiconvisibleonclick)&&(this.isIconVisibleOnClick=this.linked.scope.isIconVisibleOnClick),b=angular.isUndefined(this.linked.scope.models),!b||void 0!==this.markersScope&&void 0!==(null!=(c=this.markersScope)?c.markerModels:void 0)&&void 0!==(null!=(d=this.markersScope)?d.models:void 0)?null!=this.gMap?null!=this.linked.scope.models?(this.watchIdKey(this.linked.scope),a?this.createAllNewWindows(this.linked.scope,!1):this.pieceMealWindows(this.linked.scope,!1)):(this.parentScope=this.markersScope,this.watchIdKey(this.parentScope),a?this.createAllNewWindows(this.markersScope,!0,"markerModels",!1):this.pieceMealWindows(this.markersScope,!0,"markerModels",!1)):void 0:void this.$log.error("No models to create windows from! Need direct models or models derrived from markers!")},o.prototype.watchIdKey=function(a){return this.setIdKey(a),a.$watch("idKey",function(b){return function(c,d){return c!==d&&null==c?(b.idKey=c,b.rebuildAll(a,!0,!0)):void 0}}(this))},o.prototype.createAllNewWindows=function(a,b,c,d){return null==c&&(c="models"),null==d&&(d=!1),this.models=a.models,this.firstTime&&(this.watchModels(a),this.watchDestroy(a)),this.setContentKeys(a.models),h.waitOrGo(this,function(d){return function(){return h.each(a.models,function(e){var f,g;return f=b&&null!=(g=a[c][[e[d.idKey]]])?g.gMarker:void 0,d.createWindow(e,f,d.gMap)})}}(this)).then(function(a){return function(){return a.firstTime=!1}}(this))},o.prototype.pieceMealWindows=function(a,b,c,d){var e;return null==c&&(c="models"),null==d&&(d=!0),e=null!=this.existingPieces?!1:h.defaultChunkSize,this.models=a.models,null!=a&&null!=a.models&&a.models.length>0&&this.windows.length>0?this.figureOutState(this.idKey,a,this.windows,this.modelKeyComparison,function(b){return function(d){var f;return f=d,h.waitOrGo(b,function(){return h.each(f.removals,function(a){return null!=a&&(b.windows.remove(a.id),null!=a.destroy)?a.destroy(!0):void 0},e).then(function(){return h.each(f.adds,function(d){var e,f;if(e=null!=(f=a[c][d[b.idKey]])?f.gMarker:void 0,!e)throw"Gmarker undefined";return b.createWindow(d,e,b.gMap)},e)})}).then(function(){return b.existingPieces=void 0})["catch"](function(){return i.error("Error while pieceMealing Windows!")})}}(this)):this.rebuildAll(this.scope,!0,!0)},o.prototype.setContentKeys=function(a){return a.length>0?this.contentKeys=Object.keys(a[0]):void 0},o.prototype.createWindow=function(a,b,c){var d,e,g,h;return e=this.linked.scope.$new(!1),this.setChildScope(e,a),e.$watch("model",function(a){return function(b,c){var d;return b!==c&&(a.setChildScope(e,b),a.markersScope)?a.windows[b[a.idKey]].markerCtrl=null!=(d=a.markersScope.markerModels[b[a.idKey]])?d.gMarker:void 0:void 0}}(this),!0),g={html:function(b){return function(){return b.interpolateContent(b.linked.element.html(),a)}}(this)},this.DEFAULTS=this.markersScope?a[this.optionsKey]||{}:this.DEFAULTS,h=this.createWindowOptions(b,e,g.html(),this.DEFAULTS),d=new f(a,e,h,this.isIconVisibleOnClick,c,b,g,!1,!0),null==a[this.idKey]?void this.$log.error("Window model has no id to assign a child to. This is required for performance. Please assign id, or redirect id to a different key."):(this.windows.put(a[this.idKey],d),d)},o.prototype.setChildScope=function(a,b){return _.each(this.scopePropNames,function(c){return function(d){var e,f;return e=d+"Key",f="self"===c[e]?b:b[c[e]],f!==a[d]?a[d]=f:void 0}}(this)),a.model=b},o.prototype.interpolateContent=function(a,b){var c,d,e,f,g,h;if(void 0!==this.contentKeys&&0!==this.contentKeys.length){for(c=n(a),d={},h=this.contentKeys,f=0,g=h.length;g>f;f++)e=h[f],d[e]=b[e];return c(d)}},o}(b)}])}.call(this),function(){angular.module("google-maps.directives.api".ns()).factory("Circle".ns(),["ICircle".ns(),"CircleParentModel".ns(),function(a,b){return _.extend(a,{link:function(a,c,d,e){return e.getScope().deferred.promise.then(function(){return function(e){return new b(a,c,d,e)}}(this))}})}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api".ns()).factory("Control".ns(),["IControl".ns(),"$http","$templateCache","$compile","$controller","GoogleMapApi".ns(),function(b,d,e,f,g,h){var i;return i=function(i){function j(){this.link=a(this.link,this),j.__super__.constructor.call(this)}return c(j,i),j.prototype.link=function(a,c,i,j){return h.then(function(c){return function(h){var i,k;return angular.isUndefined(a.template)?void c.$log.error("mapControl: could not find a valid template property"):(i=angular.isDefined(a.index&&!isNaN(parseInt(a.index)))?parseInt(a.index):void 0,k=angular.isDefined(a.position)?a.position.toUpperCase().replace(/-/g,"_"):"TOP_CENTER",h.ControlPosition[k]?b.mapPromise(a,j).then(function(b){var h,j;return h=void 0,j=angular.element("
"),d.get(a.template,{cache:e}).success(function(b){var c,d;return d=a.$new(),j.append(b),i&&(j[0].index=i),angular.isDefined(a.controller)&&(c=g(a.controller,{$scope:d}),j.children().data("$ngControllerController",c)),h=f(j.contents())(d)}).error(function(){return c.$log.error("mapControl: template could not be found")}).then(function(){return b.controls[google.maps.ControlPosition[k]].push(h[0])})}):void c.$log.error("mapControl: invalid position property"))}}(this))},j}(b)}])}.call(this),function(){angular.module("google-maps.directives.api".ns()).factory("DrawingManager".ns(),["IDrawingManager".ns(),"DrawingManagerParentModel".ns(),function(a,b){return _.extend(a,{link:function(a,c,d,e){return e.getScope().deferred.promise.then(function(){return function(e){return new b(a,c,d,e)}}(this))}})}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api".ns()).factory("ApiFreeDrawPolygons".ns(),["Logger".ns(),"BaseObject".ns(),"CtrlHandle".ns(),"DrawFreeHandChildModel".ns(),function(b,d,e,f){var g;return g=function(d){function g(){return this.link=a(this.link,this),g.__super__.constructor.apply(this,arguments)}return c(g,d),g.include(e),g.prototype.restrict="EMA",g.prototype.replace=!0,g.prototype.require="^"+"GoogleMap".ns(),g.prototype.scope={polygons:"=",draw:"="},g.prototype.link=function(a,c,d,e){return this.mapPromise(a,e).then(function(){return function(c){var d,e;return a.polygons?_.isArray(a.polygons)?(d=new f(c,a.originalMapOpts),e=void 0,a.draw=function(){return"function"==typeof e&&e(),d.engage(a.polygons).then(function(){var b;return b=!0,e=a.$watch("polygons",function(a,c){var d;return b?void(b=!1):(d=_.differenceObjects(c,a),d.forEach(function(a){return a.setMap(null)}))})})}):b.error("Free Draw Polygons must be of type Array!"):b.error("No polygons to bind to!")}}(this))},g}(d)}])}.call(this),function(){angular.module("google-maps.directives.api".ns()).service("ICircle".ns(),[function(){var a;return a={},{restrict:"EA",replace:!0,require:"^"+"GoogleMap".ns(),scope:{center:"=center",radius:"=radius",stroke:"=stroke",fill:"=fill",clickable:"=",draggable:"=",editable:"=",geodesic:"=",icons:"=icons",visible:"=",events:"="}}}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api".ns()).factory("IControl".ns(),["BaseObject".ns(),"Logger".ns(),"CtrlHandle".ns(),function(b,d,e){var f;return f=function(b){function f(){this.link=a(this.link,this),this.restrict="EA",this.replace=!0,this.require="^"+"GoogleMap".ns(),this.scope={template:"@template",position:"@position",controller:"@controller",index:"@index"},this.$log=d}return c(f,b),f.extend(e),f.prototype.link=function(){throw new Exception("Not implemented!!")},f}(b)}])}.call(this),function(){angular.module("google-maps.directives.api".ns()).service("IDrawingManager".ns(),[function(){return{restrict:"EA",replace:!0,require:"^"+"GoogleMap".ns(),scope:{"static":"@",control:"=",options:"="}}}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api".ns()).factory("IMarker".ns(),["Logger".ns(),"BaseObject".ns(),"CtrlHandle".ns(),function(b,d,e){var f;return f=function(d){function f(){this.link=a(this.link,this),this.$log=b,this.restrict="EMA",this.require="^"+"GoogleMap".ns(),this.priority=-1,this.transclude=!0,this.replace=!0,this.scope=f.keys}return c(f,d),f.keys={coords:"=coords",icon:"=icon",click:"&click",options:"=options",events:"=events",fit:"=fit",idKey:"=idkey",control:"=control"},f.extend(e),f.prototype.link=function(a,b,c,d){if(!d)throw new Error("No Map Control! Marker Directive Must be inside the map!")},f}(d)}])}.call(this),function(){var a={}.hasOwnProperty,b=function(b,c){function d(){this.constructor=b}for(var e in c)a.call(c,e)&&(b[e]=c[e]);return d.prototype=c.prototype,b.prototype=new d,b.__super__=c.prototype,b};angular.module("google-maps.directives.api".ns()).factory("IPolygon".ns(),["GmapUtil".ns(),"BaseObject".ns(),"Logger".ns(),"CtrlHandle".ns(),function(a,c,d,e){var f;return f=function(c){function f(){}return b(f,c),f.include(a),f.extend(e),f.prototype.restrict="EMA",f.prototype.replace=!0,f.prototype.require="^"+"GoogleMap".ns(),f.prototype.scope={path:"=path",stroke:"=stroke",clickable:"=",draggable:"=",editable:"=",geodesic:"=",fill:"=",icons:"=icons",visible:"=","static":"=",events:"=",zIndex:"=zindex",fit:"=",control:"=control"},f.prototype.DEFAULTS={},f.prototype.$log=d,f}(c)}])}.call(this),function(){var a={}.hasOwnProperty,b=function(b,c){function d(){this.constructor=b}for(var e in c)a.call(c,e)&&(b[e]=c[e]);return d.prototype=c.prototype,b.prototype=new d,b.__super__=c.prototype,b};angular.module("google-maps.directives.api".ns()).factory("IPolyline".ns(),["GmapUtil".ns(),"BaseObject".ns(),"Logger".ns(),"CtrlHandle".ns(),function(a,c,d,e){var f;return f=function(c){function f(){}return b(f,c),f.include(a),f.extend(e),f.prototype.restrict="EMA",f.prototype.replace=!0,f.prototype.require="^"+"GoogleMap".ns(),f.prototype.scope={path:"=",stroke:"=",clickable:"=",draggable:"=",editable:"=",geodesic:"=",icons:"=",visible:"=","static":"=",fit:"=",events:"="},f.prototype.DEFAULTS={},f.prototype.$log=d,f}(c)}])}.call(this),function(){angular.module("google-maps.directives.api".ns()).service("IRectangle".ns(),[function(){"use strict";var a;return a={},{restrict:"EMA",require:"^"+"GoogleMap".ns(),replace:!0,scope:{bounds:"=",stroke:"=",clickable:"=",draggable:"=",editable:"=",fill:"=",visible:"=",events:"="}}}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api".ns()).factory("IWindow".ns(),["BaseObject".ns(),"ChildEvents".ns(),"Logger".ns(),function(b,d,e){var f;return f=function(b){function f(){this.link=a(this.link,this),this.restrict="EMA",this.template=void 0,this.transclude=!0,this.priority=-100,this.require="^"+"GoogleMap".ns(),this.replace=!0,this.scope={coords:"=coords",template:"=template",templateUrl:"=templateurl",templateParameter:"=templateparameter",isIconVisibleOnClick:"=isiconvisibleonclick",closeClick:"&closeclick",options:"=options",control:"=control",show:"=show"},this.$log=e}return c(f,b),f.include(d),f.prototype.link=function(){throw new Exception("Not Implemented!!")},f}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api".ns()).factory("Map".ns(),["$timeout","$q","Logger".ns(),"GmapUtil".ns(),"BaseObject".ns(),"CtrlHandle".ns(),"IsReady".ns(),"uuid".ns(),"ExtendGWin".ns(),"ExtendMarkerClusterer".ns(),"GoogleMapsUtilV3".ns(),"GoogleMapApi".ns(),function(b,d,e,f,g,h,i,j,k,l,m,n){"use strict";var o,p,q;return o=void 0,q=[m,k,l],p=function(b){function d(){this.link=a(this.link,this);var b,c;b=function(a){var b;return b=h.handle(a),a.ctrlType="Map",a.deferred.promise.then(function(){return q.forEach(function(a){return a.init()})}),b.getMap=function(){return a.map},_.extend(this,b)},this.controller=["$scope",b],c=this}return c(d,b),d.include(f),d.prototype.restrict="EMA",d.prototype.transclude=!0,d.prototype.replace=!1,d.prototype.template='
',d.prototype.scope={center:"=",zoom:"=",dragging:"=",control:"=",options:"=",events:"=",styles:"=",bounds:"="},d.prototype.link=function(a,b,c){return n.then(function(d){return function(f){var g,h,k,l,m,n,p,q,r,s,t;if(o={mapTypeId:f.MapTypeId.ROADMAP},r=i.spawn(),p=function(){return r.deferred.resolve({instance:r.instance,map:t})},!d.validateCoords(a.center))return void e.error("angular-google-maps: could not find a valid center property");if(!angular.isDefined(a.zoom))return void e.error("angular-google-maps: map zoom property not set");if(h=angular.element(b),h.addClass("angular-google-map"),n={options:{}},c.options&&(n.options=a.options),c.styles&&(n.styles=a.styles),c.type&&(s=c.type.toUpperCase(),google.maps.MapTypeId.hasOwnProperty(s)?n.mapTypeId=google.maps.MapTypeId[c.type.toUpperCase()]:e.error("angular-google-maps: invalid map type '"+c.type+"'")),m=angular.extend({},o,n,{center:d.getCoords(a.center),zoom:a.zoom,bounds:a.bounds}),t=new google.maps.Map(h.find("div")[1],m),t["_id".ns()]=j.generate(),g=!1,t?(a.deferred.resolve(t),p()):google.maps.event.addListener(t,"tilesloaded ",function(b){return a.deferred.resolve(b),p()}),google.maps.event.addListener(t,"dragstart",function(){return g=!0,_.defer(function(){return a.$apply(function(a){return null!=a.dragging?a.dragging=g:void 0})})}),google.maps.event.addListener(t,"dragend",function(){return g=!1,_.defer(function(){return a.$apply(function(a){return null!=a.dragging?a.dragging=g:void 0})})}),google.maps.event.addListener(t,"drag",function(){var b;return b=t.center,_.defer(function(){return a.$apply(function(a){return angular.isDefined(a.center.type)?(a.center.coordinates[1]=b.lat(),a.center.coordinates[0]=b.lng()):(a.center.latitude=b.lat(),a.center.longitude=b.lng())})})}),google.maps.event.addListener(t,"zoom_changed",function(){return a.zoom!==t.zoom?_.defer(function(){return a.$apply(function(a){return a.zoom=t.zoom})}):void 0}),q=!1,google.maps.event.addListener(t,"center_changed",function(){var b;return b=t.center,q?void 0:_.defer(function(){return a.$apply(function(a){if(!t.dragging)if(angular.isDefined(a.center.type)){if(a.center.coordinates[1]!==b.lat()&&(a.center.coordinates[1]=b.lat()),a.center.coordinates[0]!==b.lng())return a.center.coordinates[0]=b.lng()}else if(a.center.latitude!==b.lat()&&(a.center.latitude=b.lat()),a.center.longitude!==b.lng())return a.center.longitude=b.lng()})})}),google.maps.event.addListener(t,"idle",function(){var b,c,d;return b=t.getBounds(),c=b.getNorthEast(),d=b.getSouthWest(),_.defer(function(){return a.$apply(function(a){return null!==a.bounds&&void 0!==a.bounds&&void 0!==a.bounds?(a.bounds.northeast={latitude:c.lat(),longitude:c.lng()},a.bounds.southwest={latitude:d.lat(),longitude:d.lng()}):void 0})})}),angular.isDefined(a.events)&&null!==a.events&&angular.isObject(a.events)){l=function(b){return function(){return a.events[b].apply(a,[t,b,arguments])}};for(k in a.events)a.events.hasOwnProperty(k)&&angular.isFunction(a.events[k])&&google.maps.event.addListener(t,k,l(k))}return t.getOptions=function(){return m},a.map=t,null!=c.control&&null!=a.control&&(a.control.refresh=function(a){var b;if(null!=t)return google.maps.event.trigger(t,"resize"),null!=(null!=a?a.latitude:void 0)&&null!=(null!=a?a.latitude:void 0)?(b=d.getCoords(a),d.isTrue(c.pan)?t.panTo(b):t.setCenter(b)):void 0},a.control.getGMap=function(){return t},a.control.getMapOptions=function(){return m}),a.$watch("center",function(b){var f;return f=d.getCoords(b),f.lat()!==t.center.lat()||f.lng()!==t.center.lng()?(q=!0,g||(d.validateCoords(b)||e.error("Invalid center for newValue: "+JSON.stringify(b)),d.isTrue(c.pan)&&a.zoom===t.zoom?t.panTo(f):t.setCenter(f)),q=!1):void 0},!0),a.$watch("zoom",function(a){return a!==t.zoom?_.defer(function(){return t.setZoom(a)}):void 0}),a.$watch("bounds",function(a,b){var c,d,f;if(a!==b)return null==a.northeast.latitude||null==a.northeast.longitude||null==a.southwest.latitude||null==a.southwest.longitude?void e.error("Invalid map bounds for new value: "+JSON.stringify(a)):(d=new google.maps.LatLng(a.northeast.latitude,a.northeast.longitude),f=new google.maps.LatLng(a.southwest.latitude,a.southwest.longitude),c=new google.maps.LatLngBounds(f,d),t.fitBounds(c))}),a.$watch("options",function(a,b){return _.isEqual(a,b)||(n.options=a,null==t)?void 0:t.setOptions(n)},!0),a.$watch("styles",function(a,b){return _.isEqual(a,b)||(n.styles=a,null==t)?void 0:t.setOptions(n)},!0)}}(this))},d}(g)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments) +}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api".ns()).factory("Marker".ns(),["IMarker".ns(),"MarkerChildModel".ns(),"MarkerManager".ns(),function(b,d,e){var f;return f=function(f){function g(){this.link=a(this.link,this),g.__super__.constructor.call(this),this.template='',this.$log.info(this)}return c(g,f),g.prototype.controller=["$scope","$element",function(a,c){return a.ctrlType="Marker",_.extend(g,b.handle(a,c))}],g.prototype.link=function(a,c,f,g){var h;return a.fit&&(h=!0),b.mapPromise(a,g).then(function(c){return function(f){var g,h,i,j;return c.gMarkerManager||(c.gMarkerManager=new e(f)),i=_.keys(b.keys),i=_.object(i,i),c.promise=new d(a,a,i,f,{},g=!0,c.gMarkerManager,h=!1,j=!1).deferred.promise.then(function(b){return a.deferred.resolve(b)}),null!=a.control?a.control.getGMarkers=c.gMarkerManager.getGMarkers:void 0}}(this))},g}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api".ns()).factory("Markers".ns(),["IMarker".ns(),"MarkersParentModel".ns(),"_sync".ns(),function(b,d){var e;return e=function(e){function f(b){this.link=a(this.link,this),f.__super__.constructor.call(this,b),this.template='',this.scope=_.extend(this.scope||{},{idKey:"=idkey",doRebuildAll:"=dorebuildall",models:"=models",doCluster:"=docluster",clusterOptions:"=clusteroptions",clusterEvents:"=clusterevents"}),this.$log.info(this)}return c(f,e),f.prototype.controller=["$scope","$element",function(a,c){return a.ctrlType="Markers",_.extend(this,b.handle(a,c))}],f.prototype.link=function(a,c,e,f){var g,h;return g=void 0,h=function(){return function(){return null!=a.control&&(a.control.getGMarkers=function(){var a;return null!=(a=g.gMarkerManager)?a.getGMarkers():void 0},a.control.getChildMarkers=function(){return g.markerModels}),a.deferred.resolve()}}(this),b.mapPromise(a,f).then(function(){return function(b){return g=new d(a,c,e,b),g.existingPieces.then(function(){return h()})}}(this))},f}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api".ns()).factory("Polygon".ns(),["IPolygon".ns(),"$timeout","array-sync".ns(),"PolygonChildModel".ns(),function(b,d,e,f){var g;return g=function(d){function e(){return this.link=a(this.link,this),e.__super__.constructor.apply(this,arguments)}return c(e,d),e.prototype.link=function(a,c,d,e){var g,h;return g=[],h=b.mapPromise(a,e),null!=a.control&&(a.control.getInstance=this,a.control.polygons=g,a.control.promise=h),h.then(function(b){return function(c){return g.push(new f(a,d,c,b.DEFAULTS))}}(this))},e}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api".ns()).factory("Polyline".ns(),["IPolyline".ns(),"$timeout","array-sync".ns(),"PolylineChildModel".ns(),function(b,d,e,f){var g;return g=function(d){function e(){return this.link=a(this.link,this),e.__super__.constructor.apply(this,arguments)}return c(e,d),e.prototype.link=function(a,c,d,e){return angular.isUndefined(a.path)||null===a.path||!this.validatePath(a.path)?void this.$log.error("polyline: no valid path attribute found"):b.mapPromise(a,e).then(function(b){return function(c){return new f(a,d,c,b.DEFAULTS)}}(this))},e}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api".ns()).factory("Polylines".ns(),["IPolyline".ns(),"$timeout","array-sync".ns(),"PolylinesParentModel".ns(),function(b,d,e,f){var g;return g=function(b){function d(){this.link=a(this.link,this),d.__super__.constructor.call(this),this.scope.idKey="=idkey",this.scope.models="=models",this.$log.info(this)}return c(d,b),d.prototype.link=function(a,b,c,d){return angular.isUndefined(a.path)||null===a.path?void this.$log.error("polylines: no valid path attribute found"):a.models?d.getScope().deferred.promise.then(function(d){return function(e){return new f(a,b,c,e,d.DEFAULTS)}}(this)):void this.$log.error("polylines: no models found to create from")},d}(b)}])}.call(this),function(){angular.module("google-maps.directives.api".ns()).factory("Rectangle".ns(),["Logger".ns(),"GmapUtil".ns(),"IRectangle".ns(),"RectangleParentModel".ns(),function(a,b,c,d){return _.extend(c,{link:function(a,b,c,e){return e.getScope().deferred.promise.then(function(){return function(e){return new d(a,b,c,e)}}(this))}})}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api".ns()).factory("Window".ns(),["IWindow".ns(),"GmapUtil".ns(),"WindowChildModel".ns(),function(b,d,e){var f;return f=function(b){function f(){this.link=a(this.link,this),f.__super__.constructor.call(this),this.require=["^"+"GoogleMap".ns(),"^?"+"Marker".ns()],this.template='',this.$log.info(this),this.childWindows=[]}return c(f,b),f.include(d),f.prototype.link=function(a,b,c,d){var e,f,g;return e=d[0].getScope(),f=d.length>1&&null!=d[1]?d[1]:void 0,g=null!=f?f.getScope():void 0,e.deferred.promise.then(function(d){return function(e){var h;return h=!0,angular.isDefined(c.isiconvisibleonclick)&&(h=a.isIconVisibleOnClick),f?g.deferred.promise.then(function(c){return d.init(a,b,h,e,g,c)}):void d.init(a,b,h,e)}}(this))},f.prototype.init=function(a,b,c,d,f,g){var h,i,j,k;return i=null!=a.options?a.options:{},j=null!=a&&this.validateCoords(a.coords),null!=f&&f.$watch("coords",function(a){return function(b,c){return null==g||h.markerCtrl||(h.markerCtrl=g,h.handleClick(!0)),a.validateCoords(b)?angular.equals(b,c)?void 0:h.getLatestPosition(a.getCoords(b)):h.hideWindow()}}(this),!0),k=j?this.createWindowOptions(g,a,b.html(),i):i,null!=d&&(h=new e({},a,k,c,d,g,b),this.childWindows.push(h),a.$on("$destroy",function(a){return function(){return a.childWindows=_.withoutObjects(a.childWindows,[h],function(a,b){return a.scope.$id===b.scope.$id})}}(this))),null!=a.control&&(a.control.getGWindows=function(a){return function(){return a.childWindows.map(function(a){return a.gWin})}}(this),a.control.getChildWindows=function(a){return function(){return a.childWindows}}(this),a.control.showWindow=function(a){return function(){return a.childWindows.map(function(a){return a.showWindow()})}}(this),a.control.hideWindow=function(a){return function(){return a.childWindows.map(function(a){return a.hideWindow()})}}(this)),null!=this.onChildCreation&&null!=h?this.onChildCreation(h):void 0},f}(b)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}},b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};angular.module("google-maps.directives.api".ns()).factory("Windows".ns(),["IWindow".ns(),"WindowsParentModel".ns(),function(b,d){var e;return e=function(b){function e(){this.init=a(this.init,this),this.link=a(this.link,this),e.__super__.constructor.call(this),this.require=["^"+"GoogleMap".ns(),"^?"+"Markers".ns()],this.template='',this.scope.idKey="=idkey",this.scope.doRebuildAll="=dorebuildall",this.scope.models="=models",this.$log.debug(this)}return c(e,b),e.prototype.link=function(a,b,c,d){var e,f,g;return e=d[0].getScope(),f=d.length>1&&null!=d[1]?d[1]:void 0,g=null!=f?f.getScope():void 0,e.deferred.promise.then(function(e){return function(f){var h,i;return h=(null!=g&&null!=(i=g.deferred)?i.promise:void 0)||Promise.resolve(),h.then(function(){var h,i;return h=null!=(i=e.parentModel)?i.existingPieces:void 0,h?h.then(function(){return e.init(a,b,c,d,f,g)}):e.init(a,b,c,d,f,g)})}}(this))},e.prototype.init=function(a,b,c,e,f,g){var h;return h=new d(a,b,c,e,f,g),null!=a.control?(a.control.getGWindows=function(){return function(){return h.windows.map(function(a){return a.gWin})}}(this),a.control.getChildWindows=function(){return function(){return h.windows}}(this)):void 0},e}(b)}])}.call(this),function(){angular.module("google-maps".ns()).directive("GoogleMap".ns(),["Map".ns(),function(a){return new a}])}.call(this),function(){angular.module("google-maps".ns()).directive("Marker".ns(),["$timeout","Marker".ns(),function(a,b){return new b(a)}])}.call(this),function(){angular.module("google-maps".ns()).directive("Markers".ns(),["$timeout","Markers".ns(),function(a,b){return new b(a)}])}.call(this),function(){angular.module("google-maps".ns()).directive("Polygon".ns(),["Polygon".ns(),function(a){return new a}])}.call(this),function(){angular.module("google-maps".ns()).directive("Circle".ns(),["Circle".ns(),function(a){return a}])}.call(this),function(){angular.module("google-maps".ns()).directive("Polyline".ns(),["Polyline".ns(),function(a){return new a}])}.call(this),function(){angular.module("google-maps".ns()).directive("Polylines".ns(),["Polylines".ns(),function(a){return new a}])}.call(this),function(){angular.module("google-maps".ns()).directive("Rectangle".ns(),["Logger".ns(),"Rectangle".ns(),function(a,b){return b}])}.call(this),function(){angular.module("google-maps".ns()).directive("Window".ns(),["$timeout","$compile","$http","$templateCache","Window".ns(),function(a,b,c,d,e){return new e(a,b,c,d)}])}.call(this),function(){angular.module("google-maps".ns()).directive("Windows".ns(),["$timeout","$compile","$http","$templateCache","$interpolate","Windows".ns(),function(a,b,c,d,e,f){return new f(a,b,c,d,e)}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}};angular.module("google-maps".ns()).directive("Layer".ns(),["$timeout","Logger".ns(),"LayerParentModel".ns(),function(b,c,d){var e;return new(e=function(){function b(){this.link=a(this.link,this),this.$log=c,this.restrict="EMA",this.require="^"+"GoogleMap".ns(),this.priority=-1,this.transclude=!0,this.template='',this.replace=!0,this.scope={show:"=show",type:"=type",namespace:"=namespace",options:"=options",onCreated:"&oncreated"}}return b.prototype.link=function(a,b,c,e){return e.getScope().deferred.promise.then(function(){return function(e){return null!=a.onCreated?new d(a,b,c,e,a.onCreated):new d(a,b,c,e)}}(this))},b}())}])}.call(this),function(){angular.module("google-maps".ns()).directive("MapControl".ns(),["Control".ns(),function(a){return new a}])}.call(this),function(){angular.module("google-maps".ns()).directive("DrawingManager".ns(),["DrawingManager".ns(),function(a){return a}])}.call(this),function(){angular.module("google-maps".ns()).directive("FreeDrawPolygons".ns(),["ApiFreeDrawPolygons".ns(),function(a){return new a}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}};angular.module("google-maps".ns()).directive("MapType".ns(),["$timeout","Logger".ns(),"MapTypeParentModel".ns(),function(b,c,d){var e;return new(e=function(){function b(){this.link=a(this.link,this),this.$log=c,this.restrict="EMA",this.require="^"+"GoogleMap".ns(),this.priority=-1,this.transclude=!0,this.template='',this.replace=!0,this.scope={show:"=show",options:"=options",refresh:"=refresh",id:"@"}}return b.prototype.link=function(a,b,c,e){return e.getScope().deferred.promise.then(function(){return function(e){return new d(a,b,c,e)}}(this))},b}())}])}.call(this),function(){var a=function(a,b){return function(){return a.apply(b,arguments)}};angular.module("google-maps".ns()).directive("SearchBox".ns(),["GoogleMapApi".ns(),"Logger".ns(),"SearchBoxParentModel".ns(),"$http","$templateCache",function(b,c,d,e,f){var g;return new(g=function(){function g(){this.link=a(this.link,this),this.$log=c,this.restrict="EMA",this.require="^"+"GoogleMap".ns(),this.priority=-1,this.transclude=!0,this.template='',this.replace=!0,this.scope={template:"=template",position:"=position",options:"=options",events:"=events",parentdiv:"=parentdiv"}}return g.prototype.link=function(a,c,g,h){return b.then(function(b){return function(i){return e.get(a.template,{cache:f}).success(function(e){return h.getScope().deferred.promise.then(function(f){var h;return h=angular.isDefined(a.position)?a.position.toUpperCase().replace(/-/g,"_"):"TOP_LEFT",i.ControlPosition[h]?new d(a,c,g,f,h,e):void b.$log.error("searchBox: invalid position property")})})}}(this))},g}())}])}.call(this),angular.module("google-maps.wrapped".ns()).service("uuid".ns(),function(){function a(){}return a.generate=function(){var b=a._gri,c=a._ha;return c(b(32),8)+"-"+c(b(16),4)+"-"+c(16384|b(12),4)+"-"+c(32768|b(14),4)+"-"+c(b(48),12)},a._gri=function(a){return 0>a?0/0:30>=a?0|Math.random()*(1<=a?(0|1073741824*Math.random())+1073741824*(0|Math.random()*(1<0;d>>>=1,e+=e)1&d&&(c=e+c);return c},a}),angular.module("google-maps.wrapped".ns()).service("GoogleMapsUtilV3".ns(),function(){return{init:_.once(function(){function a(a){a=a||{},google.maps.OverlayView.apply(this,arguments),this.content_=a.content||"",this.disableAutoPan_=a.disableAutoPan||!1,this.maxWidth_=a.maxWidth||0,this.pixelOffset_=a.pixelOffset||new google.maps.Size(0,0),this.position_=a.position||new google.maps.LatLng(0,0),this.zIndex_=a.zIndex||null,this.boxClass_=a.boxClass||"infoBox",this.boxStyle_=a.boxStyle||{},this.closeBoxMargin_=a.closeBoxMargin||"2px",this.closeBoxURL_=a.closeBoxURL||"http://www.google.com/intl/en_us/mapfiles/close.gif",""===a.closeBoxURL&&(this.closeBoxURL_=""),this.infoBoxClearance_=a.infoBoxClearance||new google.maps.Size(1,1),"undefined"==typeof a.visible&&(a.visible="undefined"==typeof a.isHidden?!0:!a.isHidden),this.isHidden_=!a.visible,this.alignBottom_=a.alignBottom||!1,this.pane_=a.pane||"floatPane",this.enableEventPropagation_=a.enableEventPropagation||!1,this.div_=null,this.closeListener_=null,this.moveListener_=null,this.contextListener_=null,this.eventListeners_=null,this.fixedWidthSet_=null}function b(a,c){a.getMarkerClusterer().extend(b,google.maps.OverlayView),this.cluster_=a,this.className_=a.getMarkerClusterer().getClusterClass(),this.styles_=c,this.center_=null,this.div_=null,this.sums_=null,this.visible_=!1,this.setMap(a.getMap())}function c(a){this.markerClusterer_=a,this.map_=a.getMap(),this.gridSize_=a.getGridSize(),this.minClusterSize_=a.getMinimumClusterSize(),this.averageCenter_=a.getAverageCenter(),this.markers_=[],this.center_=null,this.bounds_=null,this.clusterIcon_=new b(this,a.getStyles())}function d(a,b,c){this.extend(d,google.maps.OverlayView),b=b||[],c=c||{},this.markers_=[],this.clusters_=[],this.listeners_=[],this.activeMap_=null,this.ready_=!1,this.gridSize_=c.gridSize||60,this.minClusterSize_=c.minimumClusterSize||2,this.maxZoom_=c.maxZoom||null,this.styles_=c.styles||[],this.title_=c.title||"",this.zoomOnClick_=!0,void 0!==c.zoomOnClick&&(this.zoomOnClick_=c.zoomOnClick),this.averageCenter_=!1,void 0!==c.averageCenter&&(this.averageCenter_=c.averageCenter),this.ignoreHidden_=!1,void 0!==c.ignoreHidden&&(this.ignoreHidden_=c.ignoreHidden),this.enableRetinaIcons_=!1,void 0!==c.enableRetinaIcons&&(this.enableRetinaIcons_=c.enableRetinaIcons),this.imagePath_=c.imagePath||d.IMAGE_PATH,this.imageExtension_=c.imageExtension||d.IMAGE_EXTENSION,this.imageSizes_=c.imageSizes||d.IMAGE_SIZES,this.calculator_=c.calculator||d.CALCULATOR,this.batchSize_=c.batchSize||d.BATCH_SIZE,this.batchSizeIE_=c.batchSizeIE||d.BATCH_SIZE_IE,this.clusterClass_=c.clusterClass||"cluster",-1!==navigator.userAgent.toLowerCase().indexOf("msie")&&(this.batchSize_=this.batchSizeIE_),this.setupStyles_(),this.addMarkers(b,!0),this.setMap(a)}function e(a,b){function c(){}c.prototype=b.prototype,a.superClass_=b.prototype,a.prototype=new c,a.prototype.constructor=a}function f(a,b){this.marker_=a,this.handCursorURL_=a.handCursorURL,this.labelDiv_=document.createElement("div"),this.labelDiv_.style.cssText="position: absolute; overflow: hidden;",this.eventDiv_=document.createElement("div"),this.eventDiv_.style.cssText=this.labelDiv_.style.cssText,this.eventDiv_.setAttribute("onselectstart","return false;"),this.eventDiv_.setAttribute("ondragstart","return false;"),this.crossDiv_=f.getSharedCross(b)}function g(a){a=a||{},a.labelContent=a.labelContent||"",a.labelAnchor=a.labelAnchor||new google.maps.Point(0,0),a.labelClass=a.labelClass||"markerLabels",a.labelStyle=a.labelStyle||{},a.labelInBackground=a.labelInBackground||!1,"undefined"==typeof a.labelVisible&&(a.labelVisible=!0),"undefined"==typeof a.raiseOnDrag&&(a.raiseOnDrag=!0),"undefined"==typeof a.clickable&&(a.clickable=!0),"undefined"==typeof a.draggable&&(a.draggable=!1),"undefined"==typeof a.optimized&&(a.optimized=!1),a.crossImage=a.crossImage||"http"+("https:"===document.location.protocol?"s":"")+"://maps.gstatic.com/intl/en_us/mapfiles/drag_cross_67_16.png",a.handCursor=a.handCursor||"http"+("https:"===document.location.protocol?"s":"")+"://maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur",a.optimized=!1,this.label=new f(this,a.crossImage,a.handCursor),google.maps.Marker.apply(this,arguments)}a.prototype=new google.maps.OverlayView,a.prototype.createInfoBoxDiv_=function(){var a,b,c,d=this,e=function(a){a.cancelBubble=!0,a.stopPropagation&&a.stopPropagation()},f=function(a){a.returnValue=!1,a.preventDefault&&a.preventDefault(),d.enableEventPropagation_||e(a)};if(!this.div_){if(this.div_=document.createElement("div"),this.setBoxStyle_(),"undefined"==typeof this.content_.nodeType?this.div_.innerHTML=this.getCloseBoxImg_()+this.content_:(this.div_.innerHTML=this.getCloseBoxImg_(),this.div_.appendChild(this.content_)),this.getPanes()[this.pane_].appendChild(this.div_),this.addClickHandler_(),this.div_.style.width?this.fixedWidthSet_=!0:0!==this.maxWidth_&&this.div_.offsetWidth>this.maxWidth_?(this.div_.style.width=this.maxWidth_,this.div_.style.overflow="auto",this.fixedWidthSet_=!0):(c=this.getBoxWidths_(),this.div_.style.width=this.div_.offsetWidth-c.left-c.right+"px",this.fixedWidthSet_=!1),this.panBox_(this.disableAutoPan_),!this.enableEventPropagation_){for(this.eventListeners_=[],b=["mousedown","mouseover","mouseout","mouseup","click","dblclick","touchstart","touchend","touchmove"],a=0;ag&&(d=o.x+k+i+m-g),this.alignBottom_?o.y<-j+n+l?e=o.y+j-n-l:o.y+j+n>h&&(e=o.y+j+n-h):o.y<-j+n?e=o.y+j-n:o.y+l+j+n>h&&(e=o.y+l+j+n-h),0!==d||0!==e){{b.getCenter()}b.panBy(d,e)}}},a.prototype.setBoxStyle_=function(){var a,b;if(this.div_){this.div_.className=this.boxClass_,this.div_.style.cssText="",b=this.boxStyle_;for(a in b)b.hasOwnProperty(a)&&(this.div_.style[a]=b[a]);"undefined"!=typeof this.div_.style.opacity&&""!==this.div_.style.opacity&&(this.div_.style.filter="alpha(opacity="+100*this.div_.style.opacity+")"),this.div_.style.position="absolute",this.div_.style.visibility="hidden",null!==this.zIndex_&&(this.div_.style.zIndex=this.zIndex_)}},a.prototype.getBoxWidths_=function(){var a,b={top:0,bottom:0,left:0,right:0},c=this.div_;return document.defaultView&&document.defaultView.getComputedStyle?(a=c.ownerDocument.defaultView.getComputedStyle(c,""),a&&(b.top=parseInt(a.borderTopWidth,10)||0,b.bottom=parseInt(a.borderBottomWidth,10)||0,b.left=parseInt(a.borderLeftWidth,10)||0,b.right=parseInt(a.borderRightWidth,10)||0)):document.documentElement.currentStyle&&c.currentStyle&&(b.top=parseInt(c.currentStyle.borderTopWidth,10)||0,b.bottom=parseInt(c.currentStyle.borderBottomWidth,10)||0,b.left=parseInt(c.currentStyle.borderLeftWidth,10)||0,b.right=parseInt(c.currentStyle.borderRightWidth,10)||0),b},a.prototype.onRemove=function(){this.div_&&(this.div_.parentNode.removeChild(this.div_),this.div_=null)},a.prototype.draw=function(){this.createInfoBoxDiv_();var a=this.getProjection().fromLatLngToDivPixel(this.position_);this.div_.style.left=a.x+this.pixelOffset_.width+"px",this.alignBottom_?this.div_.style.bottom=-(a.y+this.pixelOffset_.height)+"px":this.div_.style.top=a.y+this.pixelOffset_.height+"px",this.div_.style.visibility=this.isHidden_?"hidden":"visible"},a.prototype.setOptions=function(a){"undefined"!=typeof a.boxClass&&(this.boxClass_=a.boxClass,this.setBoxStyle_()),"undefined"!=typeof a.boxStyle&&(this.boxStyle_=a.boxStyle,this.setBoxStyle_()),"undefined"!=typeof a.content&&this.setContent(a.content),"undefined"!=typeof a.disableAutoPan&&(this.disableAutoPan_=a.disableAutoPan),"undefined"!=typeof a.maxWidth&&(this.maxWidth_=a.maxWidth),"undefined"!=typeof a.pixelOffset&&(this.pixelOffset_=a.pixelOffset),"undefined"!=typeof a.alignBottom&&(this.alignBottom_=a.alignBottom),"undefined"!=typeof a.position&&this.setPosition(a.position),"undefined"!=typeof a.zIndex&&this.setZIndex(a.zIndex),"undefined"!=typeof a.closeBoxMargin&&(this.closeBoxMargin_=a.closeBoxMargin),"undefined"!=typeof a.closeBoxURL&&(this.closeBoxURL_=a.closeBoxURL),"undefined"!=typeof a.infoBoxClearance&&(this.infoBoxClearance_=a.infoBoxClearance),"undefined"!=typeof a.isHidden&&(this.isHidden_=a.isHidden),"undefined"!=typeof a.visible&&(this.isHidden_=!a.visible),"undefined"!=typeof a.enableEventPropagation&&(this.enableEventPropagation_=a.enableEventPropagation),this.div_&&this.draw()},a.prototype.setContent=function(a){this.content_=a,this.div_&&(this.closeListener_&&(google.maps.event.removeListener(this.closeListener_),this.closeListener_=null),this.fixedWidthSet_||(this.div_.style.width=""),"undefined"==typeof a.nodeType?this.div_.innerHTML=this.getCloseBoxImg_()+a:(this.div_.innerHTML=this.getCloseBoxImg_(),this.div_.appendChild(a)),this.fixedWidthSet_||(this.div_.style.width=this.div_.offsetWidth+"px","undefined"==typeof a.nodeType?this.div_.innerHTML=this.getCloseBoxImg_()+a:(this.div_.innerHTML=this.getCloseBoxImg_(),this.div_.appendChild(a))),this.addClickHandler_()),google.maps.event.trigger(this,"content_changed")},a.prototype.setPosition=function(a){this.position_=a,this.div_&&this.draw(),google.maps.event.trigger(this,"position_changed")},a.prototype.setZIndex=function(a){this.zIndex_=a,this.div_&&(this.div_.style.zIndex=a),google.maps.event.trigger(this,"zindex_changed")},a.prototype.setVisible=function(a){this.isHidden_=!a,this.div_&&(this.div_.style.visibility=this.isHidden_?"hidden":"visible")},a.prototype.getContent=function(){return this.content_},a.prototype.getPosition=function(){return this.position_},a.prototype.getZIndex=function(){return this.zIndex_},a.prototype.getVisible=function(){var a;return a="undefined"==typeof this.getMap()||null===this.getMap()?!1:!this.isHidden_},a.prototype.show=function(){this.isHidden_=!1,this.div_&&(this.div_.style.visibility="visible")},a.prototype.hide=function(){this.isHidden_=!0,this.div_&&(this.div_.style.visibility="hidden")},a.prototype.open=function(a,b){var c=this;b&&(this.position_=b.getPosition(),this.moveListener_=google.maps.event.addListener(b,"position_changed",function(){c.setPosition(this.getPosition())})),this.setMap(a),this.div_&&this.panBox_()},a.prototype.close=function(){var a;if(this.closeListener_&&(google.maps.event.removeListener(this.closeListener_),this.closeListener_=null),this.eventListeners_){for(a=0;af&&g.getMap().setZoom(f+1)},100)),d.cancelBubble=!0,d.stopPropagation&&d.stopPropagation()}}),google.maps.event.addDomListener(this.div_,"mouseover",function(){var a=c.cluster_.getMarkerClusterer();google.maps.event.trigger(a,"mouseover",c.cluster_)}),google.maps.event.addDomListener(this.div_,"mouseout",function(){var a=c.cluster_.getMarkerClusterer();google.maps.event.trigger(a,"mouseout",c.cluster_)})},b.prototype.onRemove=function(){this.div_&&this.div_.parentNode&&(this.hide(),google.maps.event.removeListener(this.boundsChangedListener_),google.maps.event.clearInstanceListeners(this.div_),this.div_.parentNode.removeChild(this.div_),this.div_=null)},b.prototype.draw=function(){if(this.visible_){var a=this.getPosFromLatLng_(this.center_);this.div_.style.top=a.y+"px",this.div_.style.left=a.x+"px"}},b.prototype.hide=function(){this.div_&&(this.div_.style.display="none"),this.visible_=!1},b.prototype.show=function(){if(this.div_){var a="",b=this.backgroundPosition_.split(" "),c=parseInt(b[0].trim(),10),d=parseInt(b[1].trim(),10),e=this.getPosFromLatLng_(this.center_);this.div_.style.cssText=this.createCss(e),a="",this.div_.innerHTML=a+"
"+this.sums_.text+"
",this.div_.title="undefined"==typeof this.sums_.title||""===this.sums_.title?this.cluster_.getMarkerClusterer().getTitle():this.sums_.title,this.div_.style.display=""}this.visible_=!0},b.prototype.useStyle=function(a){this.sums_=a;var b=Math.max(0,a.index-1);b=Math.min(this.styles_.length-1,b);var c=this.styles_[b];this.url_=c.url,this.height_=c.height,this.width_=c.width,this.anchorText_=c.anchorText||[0,0],this.anchorIcon_=c.anchorIcon||[parseInt(this.height_/2,10),parseInt(this.width_/2,10)],this.textColor_=c.textColor||"black",this.textSize_=c.textSize||11,this.textDecoration_=c.textDecoration||"none",this.fontWeight_=c.fontWeight||"bold",this.fontStyle_=c.fontStyle||"normal",this.fontFamily_=c.fontFamily||"Arial,sans-serif",this.backgroundPosition_=c.backgroundPosition||"0 0"},b.prototype.setCenter=function(a){this.center_=a},b.prototype.createCss=function(a){var b=[];return b.push("cursor: pointer;"),b.push("position: absolute; top: "+a.y+"px; left: "+a.x+"px;"),b.push("width: "+this.width_+"px; height: "+this.height_+"px;"),b.join("")},b.prototype.getPosFromLatLng_=function(a){var b=this.getProjection().fromLatLngToDivPixel(a);return b.x-=this.anchorIcon_[1],b.y-=this.anchorIcon_[0],b.x=parseInt(b.x,10),b.y=parseInt(b.y,10),b},c.prototype.getSize=function(){return this.markers_.length},c.prototype.getMarkers=function(){return this.markers_},c.prototype.getCenter=function(){return this.center_},c.prototype.getMap=function(){return this.map_},c.prototype.getMarkerClusterer=function(){return this.markerClusterer_},c.prototype.getBounds=function(){var a,b=new google.maps.LatLngBounds(this.center_,this.center_),c=this.getMarkers();for(a=0;ad)a.getMap()!==this.map_&&a.setMap(this.map_);else if(cb;b++)this.markers_[b].setMap(null);else a.setMap(null);return this.updateIcon_(),!0},c.prototype.isMarkerInClusterBounds=function(a){return this.bounds_.contains(a.getPosition())},c.prototype.calculateBounds_=function(){var a=new google.maps.LatLngBounds(this.center_,this.center_);this.bounds_=this.markerClusterer_.getExtendedBounds(a)},c.prototype.updateIcon_=function(){var a=this.markers_.length,b=this.markerClusterer_.getMaxZoom();if(null!==b&&this.map_.getZoom()>b)return void this.clusterIcon_.hide();if(a0))for(a=0;ad&&(g=d,h=e));h&&h.isMarkerInClusterBounds(a)?h.addMarker(a):(e=new c(this),e.addMarker(a),this.clusters_.push(e))},d.prototype.createClusters_=function(a){var b,c,d,e=this;if(this.ready_){0===a&&(google.maps.event.trigger(this,"clusteringbegin",this),"undefined"!=typeof this.timerRefStatic&&(clearTimeout(this.timerRefStatic),delete this.timerRefStatic)),d=this.getMap().getZoom()>3?new google.maps.LatLngBounds(this.getMap().getBounds().getSouthWest(),this.getMap().getBounds().getNorthEast()):new google.maps.LatLngBounds(new google.maps.LatLng(85.02070771743472,-178.48388434375),new google.maps.LatLng(-85.08136444384544,178.00048865625));var f=this.getExtendedBounds(d),g=Math.min(a+this.batchSize_,this.markers_.length);for(b=a;g>b;b++)c=this.markers_[b],!c.isAdded&&this.isMarkerInBounds_(c,f)&&(!this.ignoreHidden_||this.ignoreHidden_&&c.getVisible())&&this.addToClosestCluster_(c);gc?a.getMap()!==this.map_&&a.setMap(this.map_):b3?new google.maps.LatLngBounds(this.getMap().getBounds().getSouthWest(),this.getMap().getBounds().getNorthEast()):new google.maps.LatLngBounds(new google.maps.LatLng(85.02070771743472,-178.48388434375),new google.maps.LatLng(-85.08136444384544,178.00048865625));var f=this.getExtendedBounds(d),g=Math.min(a+this.batchSize_,this.markers_.length);for(b=a;g>b;b++)c=this.markers_.values()[b],!c.isAdded&&this.isMarkerInBounds_(c,f)&&(!this.ignoreHidden_||this.ignoreHidden_&&c.getVisible())&&this.addToClosestCluster_(c);if(gc&&(f=c,g=d));g&&g.isMarkerInClusterBounds(a)?g.addMarker(a):(d=new NgMapCluster(this),d.addMarker(a),this.clusters_.push(d))},c.prototype.redraw_=function(){this.createClusters_(0)},c.prototype.resetViewport_=function(a){var b;for(b=0;b + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.Promise=e():"undefined"!=typeof global?global.Promise=e():"undefined"!=typeof self&&(self.Promise=e())}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(Promise) { +var SomePromiseArray = Promise._SomePromiseArray; +function Promise$_Any(promises) { + var ret = new SomePromiseArray(promises); + var promise = ret.promise(); + if (promise.isRejected()) { + return promise; + } + ret.setHowMany(1); + ret.setUnwrap(); + ret.init(); + return promise; +} + +Promise.any = function Promise$Any(promises) { + return Promise$_Any(promises); +}; + +Promise.prototype.any = function Promise$any() { + return Promise$_Any(this); +}; + +}; + +},{}],2:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +var schedule = require("./schedule.js"); +var Queue = require("./queue.js"); +var errorObj = require("./util.js").errorObj; +var tryCatch1 = require("./util.js").tryCatch1; +var _process = typeof process !== "undefined" ? process : void 0; + +function Async() { + this._isTickUsed = false; + this._schedule = schedule; + this._length = 0; + this._lateBuffer = new Queue(16); + this._functionBuffer = new Queue(65536); + var self = this; + this.consumeFunctionBuffer = function Async$consumeFunctionBuffer() { + self._consumeFunctionBuffer(); + }; +} + +Async.prototype.haveItemsQueued = function Async$haveItemsQueued() { + return this._length > 0; +}; + +Async.prototype.invokeLater = function Async$invokeLater(fn, receiver, arg) { + if (_process !== void 0 && + _process.domain != null && + !fn.domain) { + fn = _process.domain.bind(fn); + } + this._lateBuffer.push(fn, receiver, arg); + this._queueTick(); +}; + +Async.prototype.invoke = function Async$invoke(fn, receiver, arg) { + if (_process !== void 0 && + _process.domain != null && + !fn.domain) { + fn = _process.domain.bind(fn); + } + var functionBuffer = this._functionBuffer; + functionBuffer.push(fn, receiver, arg); + this._length = functionBuffer.length(); + this._queueTick(); +}; + +Async.prototype._consumeFunctionBuffer = +function Async$_consumeFunctionBuffer() { + var functionBuffer = this._functionBuffer; + while (functionBuffer.length() > 0) { + var fn = functionBuffer.shift(); + var receiver = functionBuffer.shift(); + var arg = functionBuffer.shift(); + fn.call(receiver, arg); + } + this._reset(); + this._consumeLateBuffer(); +}; + +Async.prototype._consumeLateBuffer = function Async$_consumeLateBuffer() { + var buffer = this._lateBuffer; + while(buffer.length() > 0) { + var fn = buffer.shift(); + var receiver = buffer.shift(); + var arg = buffer.shift(); + var res = tryCatch1(fn, receiver, arg); + if (res === errorObj) { + this._queueTick(); + if (fn.domain != null) { + fn.domain.emit("error", res.e); + } else { + throw res.e; + } + } + } +}; + +Async.prototype._queueTick = function Async$_queue() { + if (!this._isTickUsed) { + this._schedule(this.consumeFunctionBuffer); + this._isTickUsed = true; + } +}; + +Async.prototype._reset = function Async$_reset() { + this._isTickUsed = false; + this._length = 0; +}; + +module.exports = new Async(); + +},{"./queue.js":25,"./schedule.js":28,"./util.js":35}],3:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +var Promise = require("./promise.js")(); +module.exports = Promise; +},{"./promise.js":20}],4:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +var cr = Object.create; +if (cr) { + var callerCache = cr(null); + var getterCache = cr(null); + callerCache[" size"] = getterCache[" size"] = 0; +} + +module.exports = function(Promise) { +var util = require("./util.js"); +var canEvaluate = util.canEvaluate; +var isIdentifier = util.isIdentifier; + +function makeMethodCaller (methodName) { + return new Function("obj", " \n\ + 'use strict' \n\ + var len = this.length; \n\ + switch(len) { \n\ + case 1: return obj.methodName(this[0]); \n\ + case 2: return obj.methodName(this[0], this[1]); \n\ + case 3: return obj.methodName(this[0], this[1], this[2]); \n\ + case 0: return obj.methodName(); \n\ + default: return obj.methodName.apply(obj, this); \n\ + } \n\ + ".replace(/methodName/g, methodName)); +} + +function makeGetter (propertyName) { + return new Function("obj", " \n\ + 'use strict'; \n\ + return obj.propertyName; \n\ + ".replace("propertyName", propertyName)); +} + +function getCompiled(name, compiler, cache) { + var ret = cache[name]; + if (typeof ret !== "function") { + if (!isIdentifier(name)) { + return null; + } + ret = compiler(name); + cache[name] = ret; + cache[" size"]++; + if (cache[" size"] > 512) { + var keys = Object.keys(cache); + for (var i = 0; i < 256; ++i) delete cache[keys[i]]; + cache[" size"] = keys.length - 256; + } + } + return ret; +} + +function getMethodCaller(name) { + return getCompiled(name, makeMethodCaller, callerCache); +} + +function getGetter(name) { + return getCompiled(name, makeGetter, getterCache); +} + +function caller(obj) { + return obj[this.pop()].apply(obj, this); +} +Promise.prototype.call = function Promise$call(methodName) { + var $_len = arguments.length;var args = new Array($_len - 1); for(var $_i = 1; $_i < $_len; ++$_i) {args[$_i - 1] = arguments[$_i];} + if (canEvaluate) { + var maybeCaller = getMethodCaller(methodName); + if (maybeCaller !== null) { + return this._then(maybeCaller, void 0, void 0, args, void 0); + } + } + args.push(methodName); + return this._then(caller, void 0, void 0, args, void 0); +}; + +function namedGetter(obj) { + return obj[this]; +} +function indexedGetter(obj) { + return obj[this]; +} +Promise.prototype.get = function Promise$get(propertyName) { + var isIndex = (typeof propertyName === "number"); + var getter; + if (!isIndex) { + if (canEvaluate) { + var maybeGetter = getGetter(propertyName); + getter = maybeGetter !== null ? maybeGetter : namedGetter; + } else { + getter = namedGetter; + } + } else { + getter = indexedGetter; + } + return this._then(getter, void 0, void 0, propertyName, void 0); +}; +}; + +},{"./util.js":35}],5:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(Promise, INTERNAL) { +var errors = require("./errors.js"); +var canAttach = errors.canAttach; +var async = require("./async.js"); +var CancellationError = errors.CancellationError; + +Promise.prototype._cancel = function Promise$_cancel(reason) { + if (!this.isCancellable()) return this; + var parent; + var promiseToReject = this; + while ((parent = promiseToReject._cancellationParent) !== void 0 && + parent.isCancellable()) { + promiseToReject = parent; + } + this._unsetCancellable(); + promiseToReject._attachExtraTrace(reason); + promiseToReject._rejectUnchecked(reason); +}; + +Promise.prototype.cancel = function Promise$cancel(reason) { + if (!this.isCancellable()) return this; + reason = reason !== void 0 + ? (canAttach(reason) ? reason : new Error(reason + "")) + : new CancellationError(); + async.invokeLater(this._cancel, this, reason); + return this; +}; + +Promise.prototype.cancellable = function Promise$cancellable() { + if (this._cancellable()) return this; + this._setCancellable(); + this._cancellationParent = void 0; + return this; +}; + +Promise.prototype.uncancellable = function Promise$uncancellable() { + var ret = new Promise(INTERNAL); + ret._propagateFrom(this, 2 | 4); + ret._follow(this); + ret._unsetCancellable(); + return ret; +}; + +Promise.prototype.fork = +function Promise$fork(didFulfill, didReject, didProgress) { + var ret = this._then(didFulfill, didReject, didProgress, + void 0, void 0); + + ret._setCancellable(); + ret._cancellationParent = void 0; + return ret; +}; +}; + +},{"./async.js":2,"./errors.js":10}],6:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function() { +var inherits = require("./util.js").inherits; +var defineProperty = require("./es5.js").defineProperty; + +var rignore = new RegExp( + "\\b(?:[a-zA-Z0-9.]+\\$_\\w+|" + + "tryCatch(?:1|2|3|4|Apply)|new \\w*PromiseArray|" + + "\\w*PromiseArray\\.\\w*PromiseArray|" + + "setTimeout|CatchFilter\\$_\\w+|makeNodePromisified|processImmediate|" + + "process._tickCallback|nextTick|Async\\$\\w+)\\b" +); + +var rtraceline = null; +var formatStack = null; + +function formatNonError(obj) { + var str; + if (typeof obj === "function") { + str = "[function " + + (obj.name || "anonymous") + + "]"; + } else { + str = obj.toString(); + var ruselessToString = /\[object [a-zA-Z0-9$_]+\]/; + if (ruselessToString.test(str)) { + try { + var newStr = JSON.stringify(obj); + str = newStr; + } + catch(e) { + + } + } + if (str.length === 0) { + str = "(empty array)"; + } + } + return ("(<" + snip(str) + ">, no stack trace)"); +} + +function snip(str) { + var maxChars = 41; + if (str.length < maxChars) { + return str; + } + return str.substr(0, maxChars - 3) + "..."; +} + +function CapturedTrace(ignoreUntil, isTopLevel) { + this.captureStackTrace(CapturedTrace, isTopLevel); + +} +inherits(CapturedTrace, Error); + +CapturedTrace.prototype.captureStackTrace = +function CapturedTrace$captureStackTrace(ignoreUntil, isTopLevel) { + captureStackTrace(this, ignoreUntil, isTopLevel); +}; + +CapturedTrace.possiblyUnhandledRejection = +function CapturedTrace$PossiblyUnhandledRejection(reason) { + if (typeof console === "object") { + var message; + if (typeof reason === "object" || typeof reason === "function") { + var stack = reason.stack; + message = "Possibly unhandled " + formatStack(stack, reason); + } else { + message = "Possibly unhandled " + String(reason); + } + if (typeof console.error === "function" || + typeof console.error === "object") { + console.error(message); + } else if (typeof console.log === "function" || + typeof console.log === "object") { + console.log(message); + } + } +}; + +CapturedTrace.combine = function CapturedTrace$Combine(current, prev) { + var currentLastIndex = current.length - 1; + var currentLastLine = current[currentLastIndex]; + var commonRootMeetPoint = -1; + for (var i = prev.length - 1; i >= 0; --i) { + if (prev[i] === currentLastLine) { + commonRootMeetPoint = i; + break; + } + } + + for (var i = commonRootMeetPoint; i >= 0; --i) { + var line = prev[i]; + if (current[currentLastIndex] === line) { + current.pop(); + currentLastIndex--; + } else { + break; + } + } + + current.push("From previous event:"); + var lines = current.concat(prev); + + var ret = []; + + for (var i = 0, len = lines.length; i < len; ++i) { + + if (((rignore.test(lines[i]) && rtraceline.test(lines[i])) || + (i > 0 && !rtraceline.test(lines[i])) && + lines[i] !== "From previous event:") + ) { + continue; + } + ret.push(lines[i]); + } + return ret; +}; + +CapturedTrace.protectErrorMessageNewlines = function(stack) { + for (var i = 0; i < stack.length; ++i) { + if (rtraceline.test(stack[i])) { + break; + } + } + + if (i <= 1) return; + + var errorMessageLines = []; + for (var j = 0; j < i; ++j) { + errorMessageLines.push(stack.shift()); + } + stack.unshift(errorMessageLines.join("\u0002\u0000\u0001")); +}; + +CapturedTrace.isSupported = function CapturedTrace$IsSupported() { + return typeof captureStackTrace === "function"; +}; + +var captureStackTrace = (function stackDetection() { + if (typeof Error.stackTraceLimit === "number" && + typeof Error.captureStackTrace === "function") { + rtraceline = /^\s*at\s*/; + formatStack = function(stack, error) { + if (typeof stack === "string") return stack; + + if (error.name !== void 0 && + error.message !== void 0) { + return error.name + ". " + error.message; + } + return formatNonError(error); + + + }; + var captureStackTrace = Error.captureStackTrace; + return function CapturedTrace$_captureStackTrace( + receiver, ignoreUntil) { + captureStackTrace(receiver, ignoreUntil); + }; + } + var err = new Error(); + + if (typeof err.stack === "string" && + typeof "".startsWith === "function" && + (err.stack.startsWith("stackDetection@")) && + stackDetection.name === "stackDetection") { + + defineProperty(Error, "stackTraceLimit", { + writable: true, + enumerable: false, + configurable: false, + value: 25 + }); + rtraceline = /@/; + var rline = /[@\n]/; + + formatStack = function(stack, error) { + if (typeof stack === "string") { + return (error.name + ". " + error.message + "\n" + stack); + } + + if (error.name !== void 0 && + error.message !== void 0) { + return error.name + ". " + error.message; + } + return formatNonError(error); + }; + + return function captureStackTrace(o) { + var stack = new Error().stack; + var split = stack.split(rline); + var len = split.length; + var ret = ""; + for (var i = 0; i < len; i += 2) { + ret += split[i]; + ret += "@"; + ret += split[i + 1]; + ret += "\n"; + } + o.stack = ret; + }; + } else { + formatStack = function(stack, error) { + if (typeof stack === "string") return stack; + + if ((typeof error === "object" || + typeof error === "function") && + error.name !== void 0 && + error.message !== void 0) { + return error.name + ". " + error.message; + } + return formatNonError(error); + }; + + return null; + } +})(); + +return CapturedTrace; +}; + +},{"./es5.js":12,"./util.js":35}],7:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(NEXT_FILTER) { +var util = require("./util.js"); +var errors = require("./errors.js"); +var tryCatch1 = util.tryCatch1; +var errorObj = util.errorObj; +var keys = require("./es5.js").keys; +var TypeError = errors.TypeError; + +function CatchFilter(instances, callback, promise) { + this._instances = instances; + this._callback = callback; + this._promise = promise; +} + +function CatchFilter$_safePredicate(predicate, e) { + var safeObject = {}; + var retfilter = tryCatch1(predicate, safeObject, e); + + if (retfilter === errorObj) return retfilter; + + var safeKeys = keys(safeObject); + if (safeKeys.length) { + errorObj.e = new TypeError( + "Catch filter must inherit from Error " + + "or be a simple predicate function"); + return errorObj; + } + return retfilter; +} + +CatchFilter.prototype.doFilter = function CatchFilter$_doFilter(e) { + var cb = this._callback; + var promise = this._promise; + var boundTo = promise._boundTo; + for (var i = 0, len = this._instances.length; i < len; ++i) { + var item = this._instances[i]; + var itemIsErrorType = item === Error || + (item != null && item.prototype instanceof Error); + + if (itemIsErrorType && e instanceof item) { + var ret = tryCatch1(cb, boundTo, e); + if (ret === errorObj) { + NEXT_FILTER.e = ret.e; + return NEXT_FILTER; + } + return ret; + } else if (typeof item === "function" && !itemIsErrorType) { + var shouldHandle = CatchFilter$_safePredicate(item, e); + if (shouldHandle === errorObj) { + var trace = errors.canAttach(errorObj.e) + ? errorObj.e + : new Error(errorObj.e + ""); + this._promise._attachExtraTrace(trace); + e = errorObj.e; + break; + } else if (shouldHandle) { + var ret = tryCatch1(cb, boundTo, e); + if (ret === errorObj) { + NEXT_FILTER.e = ret.e; + return NEXT_FILTER; + } + return ret; + } + } + } + NEXT_FILTER.e = e; + return NEXT_FILTER; +}; + +return CatchFilter; +}; + +},{"./errors.js":10,"./es5.js":12,"./util.js":35}],8:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +var util = require("./util.js"); +var isPrimitive = util.isPrimitive; +var wrapsPrimitiveReceiver = util.wrapsPrimitiveReceiver; + +module.exports = function(Promise) { +var returner = function Promise$_returner() { + return this; +}; +var thrower = function Promise$_thrower() { + throw this; +}; + +var wrapper = function Promise$_wrapper(value, action) { + if (action === 1) { + return function Promise$_thrower() { + throw value; + }; + } else if (action === 2) { + return function Promise$_returner() { + return value; + }; + } +}; + + +Promise.prototype["return"] = +Promise.prototype.thenReturn = +function Promise$thenReturn(value) { + if (wrapsPrimitiveReceiver && isPrimitive(value)) { + return this._then( + wrapper(value, 2), + void 0, + void 0, + void 0, + void 0 + ); + } + return this._then(returner, void 0, void 0, value, void 0); +}; + +Promise.prototype["throw"] = +Promise.prototype.thenThrow = +function Promise$thenThrow(reason) { + if (wrapsPrimitiveReceiver && isPrimitive(reason)) { + return this._then( + wrapper(reason, 1), + void 0, + void 0, + void 0, + void 0 + ); + } + return this._then(thrower, void 0, void 0, reason, void 0); +}; +}; + +},{"./util.js":35}],9:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(Promise, INTERNAL) { +var PromiseReduce = Promise.reduce; + +Promise.prototype.each = function Promise$each(fn) { + return PromiseReduce(this, fn, null, INTERNAL); +}; + +Promise.each = function Promise$Each(promises, fn) { + return PromiseReduce(promises, fn, null, INTERNAL); +}; +}; + +},{}],10:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +var Objectfreeze = require("./es5.js").freeze; +var util = require("./util.js"); +var inherits = util.inherits; +var notEnumerableProp = util.notEnumerableProp; + +function markAsOriginatingFromRejection(e) { + try { + notEnumerableProp(e, "isOperational", true); + } + catch(ignore) {} +} + +function originatesFromRejection(e) { + if (e == null) return false; + return ((e instanceof OperationalError) || + e["isOperational"] === true); +} + +function isError(obj) { + return obj instanceof Error; +} + +function canAttach(obj) { + return isError(obj); +} + +function subError(nameProperty, defaultMessage) { + function SubError(message) { + if (!(this instanceof SubError)) return new SubError(message); + this.message = typeof message === "string" ? message : defaultMessage; + this.name = nameProperty; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + } + inherits(SubError, Error); + return SubError; +} + +var _TypeError, _RangeError; +var CancellationError = subError("CancellationError", "cancellation error"); +var TimeoutError = subError("TimeoutError", "timeout error"); +var AggregateError = subError("AggregateError", "aggregate error"); +try { + _TypeError = TypeError; + _RangeError = RangeError; +} catch(e) { + _TypeError = subError("TypeError", "type error"); + _RangeError = subError("RangeError", "range error"); +} + +var methods = ("join pop push shift unshift slice filter forEach some " + + "every map indexOf lastIndexOf reduce reduceRight sort reverse").split(" "); + +for (var i = 0; i < methods.length; ++i) { + if (typeof Array.prototype[methods[i]] === "function") { + AggregateError.prototype[methods[i]] = Array.prototype[methods[i]]; + } +} + +AggregateError.prototype.length = 0; +AggregateError.prototype["isOperational"] = true; +var level = 0; +AggregateError.prototype.toString = function() { + var indent = Array(level * 4 + 1).join(" "); + var ret = "\n" + indent + "AggregateError of:" + "\n"; + level++; + indent = Array(level * 4 + 1).join(" "); + for (var i = 0; i < this.length; ++i) { + var str = this[i] === this ? "[Circular AggregateError]" : this[i] + ""; + var lines = str.split("\n"); + for (var j = 0; j < lines.length; ++j) { + lines[j] = indent + lines[j]; + } + str = lines.join("\n"); + ret += str + "\n"; + } + level--; + return ret; +}; + +function OperationalError(message) { + this.name = "OperationalError"; + this.message = message; + this.cause = message; + this["isOperational"] = true; + + if (message instanceof Error) { + this.message = message.message; + this.stack = message.stack; + } else if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + +} +inherits(OperationalError, Error); + +var key = "__BluebirdErrorTypes__"; +var errorTypes = Error[key]; +if (!errorTypes) { + errorTypes = Objectfreeze({ + CancellationError: CancellationError, + TimeoutError: TimeoutError, + OperationalError: OperationalError, + RejectionError: OperationalError, + AggregateError: AggregateError + }); + notEnumerableProp(Error, key, errorTypes); +} + +module.exports = { + Error: Error, + TypeError: _TypeError, + RangeError: _RangeError, + CancellationError: errorTypes.CancellationError, + OperationalError: errorTypes.OperationalError, + TimeoutError: errorTypes.TimeoutError, + AggregateError: errorTypes.AggregateError, + originatesFromRejection: originatesFromRejection, + markAsOriginatingFromRejection: markAsOriginatingFromRejection, + canAttach: canAttach +}; + +},{"./es5.js":12,"./util.js":35}],11:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(Promise) { +var TypeError = require('./errors.js').TypeError; + +function apiRejection(msg) { + var error = new TypeError(msg); + var ret = Promise.rejected(error); + var parent = ret._peekContext(); + if (parent != null) { + parent._attachExtraTrace(error); + } + return ret; +} + +return apiRejection; +}; + +},{"./errors.js":10}],12:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +var isES5 = (function(){ + "use strict"; + return this === void 0; +})(); + +if (isES5) { + module.exports = { + freeze: Object.freeze, + defineProperty: Object.defineProperty, + keys: Object.keys, + getPrototypeOf: Object.getPrototypeOf, + isArray: Array.isArray, + isES5: isES5 + }; +} else { + var has = {}.hasOwnProperty; + var str = {}.toString; + var proto = {}.constructor.prototype; + + var ObjectKeys = function ObjectKeys(o) { + var ret = []; + for (var key in o) { + if (has.call(o, key)) { + ret.push(key); + } + } + return ret; + } + + var ObjectDefineProperty = function ObjectDefineProperty(o, key, desc) { + o[key] = desc.value; + return o; + } + + var ObjectFreeze = function ObjectFreeze(obj) { + return obj; + } + + var ObjectGetPrototypeOf = function ObjectGetPrototypeOf(obj) { + try { + return Object(obj).constructor.prototype; + } + catch (e) { + return proto; + } + } + + var ArrayIsArray = function ArrayIsArray(obj) { + try { + return str.call(obj) === "[object Array]"; + } + catch(e) { + return false; + } + } + + module.exports = { + isArray: ArrayIsArray, + keys: ObjectKeys, + defineProperty: ObjectDefineProperty, + freeze: ObjectFreeze, + getPrototypeOf: ObjectGetPrototypeOf, + isES5: isES5 + }; +} + +},{}],13:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(Promise, INTERNAL) { +var PromiseMap = Promise.map; + +Promise.prototype.filter = function Promise$filter(fn, options) { + return PromiseMap(this, fn, options, INTERNAL); +}; + +Promise.filter = function Promise$Filter(promises, fn, options) { + return PromiseMap(promises, fn, options, INTERNAL); +}; +}; + +},{}],14:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(Promise, NEXT_FILTER, cast) { +var util = require("./util.js"); +var wrapsPrimitiveReceiver = util.wrapsPrimitiveReceiver; +var isPrimitive = util.isPrimitive; +var thrower = util.thrower; + +function returnThis() { + return this; +} +function throwThis() { + throw this; +} +function return$(r) { + return function Promise$_returner() { + return r; + }; +} +function throw$(r) { + return function Promise$_thrower() { + throw r; + }; +} +function promisedFinally(ret, reasonOrValue, isFulfilled) { + var then; + if (wrapsPrimitiveReceiver && isPrimitive(reasonOrValue)) { + then = isFulfilled ? return$(reasonOrValue) : throw$(reasonOrValue); + } else { + then = isFulfilled ? returnThis : throwThis; + } + return ret._then(then, thrower, void 0, reasonOrValue, void 0); +} + +function finallyHandler(reasonOrValue) { + var promise = this.promise; + var handler = this.handler; + + var ret = promise._isBound() + ? handler.call(promise._boundTo) + : handler(); + + if (ret !== void 0) { + var maybePromise = cast(ret, void 0); + if (maybePromise instanceof Promise) { + return promisedFinally(maybePromise, reasonOrValue, + promise.isFulfilled()); + } + } + + if (promise.isRejected()) { + NEXT_FILTER.e = reasonOrValue; + return NEXT_FILTER; + } else { + return reasonOrValue; + } +} + +function tapHandler(value) { + var promise = this.promise; + var handler = this.handler; + + var ret = promise._isBound() + ? handler.call(promise._boundTo, value) + : handler(value); + + if (ret !== void 0) { + var maybePromise = cast(ret, void 0); + if (maybePromise instanceof Promise) { + return promisedFinally(maybePromise, value, true); + } + } + return value; +} + +Promise.prototype._passThroughHandler = +function Promise$_passThroughHandler(handler, isFinally) { + if (typeof handler !== "function") return this.then(); + + var promiseAndHandler = { + promise: this, + handler: handler + }; + + return this._then( + isFinally ? finallyHandler : tapHandler, + isFinally ? finallyHandler : void 0, void 0, + promiseAndHandler, void 0); +}; + +Promise.prototype.lastly = +Promise.prototype["finally"] = function Promise$finally(handler) { + return this._passThroughHandler(handler, true); +}; + +Promise.prototype.tap = function Promise$tap(handler) { + return this._passThroughHandler(handler, false); +}; +}; + +},{"./util.js":35}],15:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(Promise, apiRejection, INTERNAL, cast) { +var errors = require("./errors.js"); +var TypeError = errors.TypeError; +var deprecated = require("./util.js").deprecated; +var util = require("./util.js"); +var errorObj = util.errorObj; +var tryCatch1 = util.tryCatch1; +var yieldHandlers = []; + +function promiseFromYieldHandler(value, yieldHandlers) { + var _errorObj = errorObj; + var _Promise = Promise; + var len = yieldHandlers.length; + for (var i = 0; i < len; ++i) { + var result = tryCatch1(yieldHandlers[i], void 0, value); + if (result === _errorObj) { + return _Promise.reject(_errorObj.e); + } + var maybePromise = cast(result, promiseFromYieldHandler); + if (maybePromise instanceof _Promise) return maybePromise; + } + return null; +} + +function PromiseSpawn(generatorFunction, receiver, yieldHandler) { + var promise = this._promise = new Promise(INTERNAL); + promise._setTrace(void 0); + this._generatorFunction = generatorFunction; + this._receiver = receiver; + this._generator = void 0; + this._yieldHandlers = typeof yieldHandler === "function" + ? [yieldHandler].concat(yieldHandlers) + : yieldHandlers; +} + +PromiseSpawn.prototype.promise = function PromiseSpawn$promise() { + return this._promise; +}; + +PromiseSpawn.prototype._run = function PromiseSpawn$_run() { + this._generator = this._generatorFunction.call(this._receiver); + this._receiver = + this._generatorFunction = void 0; + this._next(void 0); +}; + +PromiseSpawn.prototype._continue = function PromiseSpawn$_continue(result) { + if (result === errorObj) { + this._generator = void 0; + var trace = errors.canAttach(result.e) + ? result.e : new Error(result.e + ""); + this._promise._attachExtraTrace(trace); + this._promise._reject(result.e, trace); + return; + } + + var value = result.value; + if (result.done === true) { + this._generator = void 0; + if (!this._promise._tryFollow(value)) { + this._promise._fulfill(value); + } + } else { + var maybePromise = cast(value, void 0); + if (!(maybePromise instanceof Promise)) { + maybePromise = + promiseFromYieldHandler(maybePromise, this._yieldHandlers); + if (maybePromise === null) { + this._throw(new TypeError("A value was yielded that could not be treated as a promise")); + return; + } + } + maybePromise._then( + this._next, + this._throw, + void 0, + this, + null + ); + } +}; + +PromiseSpawn.prototype._throw = function PromiseSpawn$_throw(reason) { + if (errors.canAttach(reason)) + this._promise._attachExtraTrace(reason); + this._continue( + tryCatch1(this._generator["throw"], this._generator, reason) + ); +}; + +PromiseSpawn.prototype._next = function PromiseSpawn$_next(value) { + this._continue( + tryCatch1(this._generator.next, this._generator, value) + ); +}; + +Promise.coroutine = +function Promise$Coroutine(generatorFunction, options) { + if (typeof generatorFunction !== "function") { + throw new TypeError("generatorFunction must be a function"); + } + var yieldHandler = Object(options).yieldHandler; + var PromiseSpawn$ = PromiseSpawn; + return function () { + var generator = generatorFunction.apply(this, arguments); + var spawn = new PromiseSpawn$(void 0, void 0, yieldHandler); + spawn._generator = generator; + spawn._next(void 0); + return spawn.promise(); + }; +}; + +Promise.coroutine.addYieldHandler = function(fn) { + if (typeof fn !== "function") throw new TypeError("fn must be a function"); + yieldHandlers.push(fn); +}; + +Promise.spawn = function Promise$Spawn(generatorFunction) { + deprecated("Promise.spawn is deprecated. Use Promise.coroutine instead."); + if (typeof generatorFunction !== "function") { + return apiRejection("generatorFunction must be a function"); + } + var spawn = new PromiseSpawn(generatorFunction, this); + var ret = spawn.promise(); + spawn._run(Promise.spawn); + return ret; +}; +}; + +},{"./errors.js":10,"./util.js":35}],16:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = +function(Promise, PromiseArray, cast, INTERNAL) { +var util = require("./util.js"); +var canEvaluate = util.canEvaluate; +var tryCatch1 = util.tryCatch1; +var errorObj = util.errorObj; + + +if (canEvaluate) { + var thenCallback = function(i) { + return new Function("value", "holder", " \n\ + 'use strict'; \n\ + holder.pIndex = value; \n\ + holder.checkFulfillment(this); \n\ + ".replace(/Index/g, i)); + }; + + var caller = function(count) { + var values = []; + for (var i = 1; i <= count; ++i) values.push("holder.p" + i); + return new Function("holder", " \n\ + 'use strict'; \n\ + var callback = holder.fn; \n\ + return callback(values); \n\ + ".replace(/values/g, values.join(", "))); + }; + var thenCallbacks = []; + var callers = [void 0]; + for (var i = 1; i <= 5; ++i) { + thenCallbacks.push(thenCallback(i)); + callers.push(caller(i)); + } + + var Holder = function(total, fn) { + this.p1 = this.p2 = this.p3 = this.p4 = this.p5 = null; + this.fn = fn; + this.total = total; + this.now = 0; + }; + + Holder.prototype.callers = callers; + Holder.prototype.checkFulfillment = function(promise) { + var now = this.now; + now++; + var total = this.total; + if (now >= total) { + var handler = this.callers[total]; + var ret = tryCatch1(handler, void 0, this); + if (ret === errorObj) { + promise._rejectUnchecked(ret.e); + } else if (!promise._tryFollow(ret)) { + promise._fulfillUnchecked(ret); + } + } else { + this.now = now; + } + }; +} + +function reject(reason) { + this._reject(reason); +} + +Promise.join = function Promise$Join() { + var last = arguments.length - 1; + var fn; + if (last > 0 && typeof arguments[last] === "function") { + fn = arguments[last]; + if (last < 6 && canEvaluate) { + var ret = new Promise(INTERNAL); + ret._setTrace(void 0); + var holder = new Holder(last, fn); + var callbacks = thenCallbacks; + for (var i = 0; i < last; ++i) { + var maybePromise = cast(arguments[i], void 0); + if (maybePromise instanceof Promise) { + if (maybePromise.isPending()) { + maybePromise._then(callbacks[i], reject, + void 0, ret, holder); + } else if (maybePromise.isFulfilled()) { + callbacks[i].call(ret, + maybePromise._settledValue, holder); + } else { + ret._reject(maybePromise._settledValue); + maybePromise._unsetRejectionIsUnhandled(); + } + } else { + callbacks[i].call(ret, maybePromise, holder); + } + } + return ret; + } + } + var $_len = arguments.length;var args = new Array($_len); for(var $_i = 0; $_i < $_len; ++$_i) {args[$_i] = arguments[$_i];} + var ret = new PromiseArray(args).promise(); + return fn !== void 0 ? ret.spread(fn) : ret; +}; + +}; + +},{"./util.js":35}],17:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(Promise, PromiseArray, apiRejection, cast, INTERNAL) { +var util = require("./util.js"); +var tryCatch3 = util.tryCatch3; +var errorObj = util.errorObj; +var PENDING = {}; +var EMPTY_ARRAY = []; + +function MappingPromiseArray(promises, fn, limit, _filter) { + this.constructor$(promises); + this._callback = fn; + this._preservedValues = _filter === INTERNAL + ? new Array(this.length()) + : null; + this._limit = limit; + this._inFlight = 0; + this._queue = limit >= 1 ? [] : EMPTY_ARRAY; + this._init$(void 0, -2); +} +util.inherits(MappingPromiseArray, PromiseArray); + +MappingPromiseArray.prototype._init = function MappingPromiseArray$_init() {}; + +MappingPromiseArray.prototype._promiseFulfilled = +function MappingPromiseArray$_promiseFulfilled(value, index) { + var values = this._values; + if (values === null) return; + + var length = this.length(); + var preservedValues = this._preservedValues; + var limit = this._limit; + if (values[index] === PENDING) { + values[index] = value; + if (limit >= 1) { + this._inFlight--; + this._drainQueue(); + if (this._isResolved()) return; + } + } else { + if (limit >= 1 && this._inFlight >= limit) { + values[index] = value; + this._queue.push(index); + return; + } + if (preservedValues !== null) preservedValues[index] = value; + + var callback = this._callback; + var receiver = this._promise._boundTo; + var ret = tryCatch3(callback, receiver, value, index, length); + if (ret === errorObj) return this._reject(ret.e); + + var maybePromise = cast(ret, void 0); + if (maybePromise instanceof Promise) { + if (maybePromise.isPending()) { + if (limit >= 1) this._inFlight++; + values[index] = PENDING; + return maybePromise._proxyPromiseArray(this, index); + } else if (maybePromise.isFulfilled()) { + ret = maybePromise.value(); + } else { + maybePromise._unsetRejectionIsUnhandled(); + return this._reject(maybePromise.reason()); + } + } + values[index] = ret; + } + var totalResolved = ++this._totalResolved; + if (totalResolved >= length) { + if (preservedValues !== null) { + this._filter(values, preservedValues); + } else { + this._resolve(values); + } + + } +}; + +MappingPromiseArray.prototype._drainQueue = +function MappingPromiseArray$_drainQueue() { + var queue = this._queue; + var limit = this._limit; + var values = this._values; + while (queue.length > 0 && this._inFlight < limit) { + var index = queue.pop(); + this._promiseFulfilled(values[index], index); + } +}; + +MappingPromiseArray.prototype._filter = +function MappingPromiseArray$_filter(booleans, values) { + var len = values.length; + var ret = new Array(len); + var j = 0; + for (var i = 0; i < len; ++i) { + if (booleans[i]) ret[j++] = values[i]; + } + ret.length = j; + this._resolve(ret); +}; + +MappingPromiseArray.prototype.preservedValues = +function MappingPromiseArray$preserveValues() { + return this._preservedValues; +}; + +function map(promises, fn, options, _filter) { + var limit = typeof options === "object" && options !== null + ? options.concurrency + : 0; + limit = typeof limit === "number" && + isFinite(limit) && limit >= 1 ? limit : 0; + return new MappingPromiseArray(promises, fn, limit, _filter); +} + +Promise.prototype.map = function Promise$map(fn, options) { + if (typeof fn !== "function") return apiRejection("fn must be a function"); + + return map(this, fn, options, null).promise(); +}; + +Promise.map = function Promise$Map(promises, fn, options, _filter) { + if (typeof fn !== "function") return apiRejection("fn must be a function"); + return map(promises, fn, options, _filter).promise(); +}; + + +}; + +},{"./util.js":35}],18:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(Promise) { +var util = require("./util.js"); +var async = require("./async.js"); +var tryCatch2 = util.tryCatch2; +var tryCatch1 = util.tryCatch1; +var errorObj = util.errorObj; + +function thrower(r) { + throw r; +} + +function Promise$_spreadAdapter(val, receiver) { + if (!util.isArray(val)) return Promise$_successAdapter(val, receiver); + var ret = util.tryCatchApply(this, [null].concat(val), receiver); + if (ret === errorObj) { + async.invokeLater(thrower, void 0, ret.e); + } +} + +function Promise$_successAdapter(val, receiver) { + var nodeback = this; + var ret = val === void 0 + ? tryCatch1(nodeback, receiver, null) + : tryCatch2(nodeback, receiver, null, val); + if (ret === errorObj) { + async.invokeLater(thrower, void 0, ret.e); + } +} +function Promise$_errorAdapter(reason, receiver) { + var nodeback = this; + var ret = tryCatch1(nodeback, receiver, reason); + if (ret === errorObj) { + async.invokeLater(thrower, void 0, ret.e); + } +} + +Promise.prototype.nodeify = function Promise$nodeify(nodeback, options) { + if (typeof nodeback == "function") { + var adapter = Promise$_successAdapter; + if (options !== void 0 && Object(options).spread) { + adapter = Promise$_spreadAdapter; + } + this._then( + adapter, + Promise$_errorAdapter, + void 0, + nodeback, + this._boundTo + ); + } + return this; +}; +}; + +},{"./async.js":2,"./util.js":35}],19:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(Promise, PromiseArray) { +var util = require("./util.js"); +var async = require("./async.js"); +var errors = require("./errors.js"); +var tryCatch1 = util.tryCatch1; +var errorObj = util.errorObj; + +Promise.prototype.progressed = function Promise$progressed(handler) { + return this._then(void 0, void 0, handler, void 0, void 0); +}; + +Promise.prototype._progress = function Promise$_progress(progressValue) { + if (this._isFollowingOrFulfilledOrRejected()) return; + this._progressUnchecked(progressValue); + +}; + +Promise.prototype._clearFirstHandlerData$Base = +Promise.prototype._clearFirstHandlerData; +Promise.prototype._clearFirstHandlerData = +function Promise$_clearFirstHandlerData() { + this._clearFirstHandlerData$Base(); + this._progressHandler0 = void 0; +}; + +Promise.prototype._progressHandlerAt = +function Promise$_progressHandlerAt(index) { + return index === 0 + ? this._progressHandler0 + : this[(index << 2) + index - 5 + 2]; +}; + +Promise.prototype._doProgressWith = +function Promise$_doProgressWith(progression) { + var progressValue = progression.value; + var handler = progression.handler; + var promise = progression.promise; + var receiver = progression.receiver; + + var ret = tryCatch1(handler, receiver, progressValue); + if (ret === errorObj) { + if (ret.e != null && + ret.e.name !== "StopProgressPropagation") { + var trace = errors.canAttach(ret.e) + ? ret.e : new Error(ret.e + ""); + promise._attachExtraTrace(trace); + promise._progress(ret.e); + } + } else if (ret instanceof Promise) { + ret._then(promise._progress, null, null, promise, void 0); + } else { + promise._progress(ret); + } +}; + + +Promise.prototype._progressUnchecked = +function Promise$_progressUnchecked(progressValue) { + if (!this.isPending()) return; + var len = this._length(); + var progress = this._progress; + for (var i = 0; i < len; i++) { + var handler = this._progressHandlerAt(i); + var promise = this._promiseAt(i); + if (!(promise instanceof Promise)) { + var receiver = this._receiverAt(i); + if (typeof handler === "function") { + handler.call(receiver, progressValue, promise); + } else if (receiver instanceof Promise && receiver._isProxied()) { + receiver._progressUnchecked(progressValue); + } else if (receiver instanceof PromiseArray) { + receiver._promiseProgressed(progressValue, promise); + } + continue; + } + + if (typeof handler === "function") { + async.invoke(this._doProgressWith, this, { + handler: handler, + promise: promise, + receiver: this._receiverAt(i), + value: progressValue + }); + } else { + async.invoke(progress, promise, progressValue); + } + } +}; +}; + +},{"./async.js":2,"./errors.js":10,"./util.js":35}],20:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +var old; +if (typeof Promise !== "undefined") old = Promise; +function noConflict(bluebird) { + try { if (Promise === bluebird) Promise = old; } + catch (e) {} + return bluebird; +} +module.exports = function() { +var util = require("./util.js"); +var async = require("./async.js"); +var errors = require("./errors.js"); + +var INTERNAL = function(){}; +var APPLY = {}; +var NEXT_FILTER = {e: null}; + +var cast = require("./thenables.js")(Promise, INTERNAL); +var PromiseArray = require("./promise_array.js")(Promise, INTERNAL, cast); +var CapturedTrace = require("./captured_trace.js")(); +var CatchFilter = require("./catch_filter.js")(NEXT_FILTER); +var PromiseResolver = require("./promise_resolver.js"); + +var isArray = util.isArray; + +var errorObj = util.errorObj; +var tryCatch1 = util.tryCatch1; +var tryCatch2 = util.tryCatch2; +var tryCatchApply = util.tryCatchApply; +var RangeError = errors.RangeError; +var TypeError = errors.TypeError; +var CancellationError = errors.CancellationError; +var TimeoutError = errors.TimeoutError; +var OperationalError = errors.OperationalError; +var originatesFromRejection = errors.originatesFromRejection; +var markAsOriginatingFromRejection = errors.markAsOriginatingFromRejection; +var canAttach = errors.canAttach; +var thrower = util.thrower; +var apiRejection = require("./errors_api_rejection")(Promise); + + +var makeSelfResolutionError = function Promise$_makeSelfResolutionError() { + return new TypeError("circular promise resolution chain"); +}; + +function Promise(resolver) { + if (typeof resolver !== "function") { + throw new TypeError("the promise constructor requires a resolver function"); + } + if (this.constructor !== Promise) { + throw new TypeError("the promise constructor cannot be invoked directly"); + } + this._bitField = 0; + this._fulfillmentHandler0 = void 0; + this._rejectionHandler0 = void 0; + this._promise0 = void 0; + this._receiver0 = void 0; + this._settledValue = void 0; + this._boundTo = void 0; + if (resolver !== INTERNAL) this._resolveFromResolver(resolver); +} + +function returnFirstElement(elements) { + return elements[0]; +} + +Promise.prototype.bind = function Promise$bind(thisArg) { + var maybePromise = cast(thisArg, void 0); + var ret = new Promise(INTERNAL); + if (maybePromise instanceof Promise) { + var binder = maybePromise.then(function(thisArg) { + ret._setBoundTo(thisArg); + }); + var p = Promise.all([this, binder]).then(returnFirstElement); + ret._follow(p); + } else { + ret._follow(this); + ret._setBoundTo(thisArg); + } + ret._propagateFrom(this, 2 | 1); + return ret; +}; + +Promise.prototype.toString = function Promise$toString() { + return "[object Promise]"; +}; + +Promise.prototype.caught = Promise.prototype["catch"] = +function Promise$catch(fn) { + var len = arguments.length; + if (len > 1) { + var catchInstances = new Array(len - 1), + j = 0, i; + for (i = 0; i < len - 1; ++i) { + var item = arguments[i]; + if (typeof item === "function") { + catchInstances[j++] = item; + } else { + var catchFilterTypeError = + new TypeError( + "A catch filter must be an error constructor " + + "or a filter function"); + + this._attachExtraTrace(catchFilterTypeError); + return Promise.reject(catchFilterTypeError); + } + } + catchInstances.length = j; + fn = arguments[i]; + + this._resetTrace(); + var catchFilter = new CatchFilter(catchInstances, fn, this); + return this._then(void 0, catchFilter.doFilter, void 0, + catchFilter, void 0); + } + return this._then(void 0, fn, void 0, void 0, void 0); +}; + +function reflect() { + return new Promise.PromiseInspection(this); +} + +Promise.prototype.reflect = function Promise$reflect() { + return this._then(reflect, reflect, void 0, this, void 0); +}; + +Promise.prototype.then = +function Promise$then(didFulfill, didReject, didProgress) { + return this._then(didFulfill, didReject, didProgress, + void 0, void 0); +}; + + +Promise.prototype.done = +function Promise$done(didFulfill, didReject, didProgress) { + var promise = this._then(didFulfill, didReject, didProgress, + void 0, void 0); + promise._setIsFinal(); +}; + +Promise.prototype.spread = function Promise$spread(didFulfill, didReject) { + return this._then(didFulfill, didReject, void 0, + APPLY, void 0); +}; + +Promise.prototype.isCancellable = function Promise$isCancellable() { + return !this.isResolved() && + this._cancellable(); +}; + +Promise.prototype.toJSON = function Promise$toJSON() { + var ret = { + isFulfilled: false, + isRejected: false, + fulfillmentValue: void 0, + rejectionReason: void 0 + }; + if (this.isFulfilled()) { + ret.fulfillmentValue = this._settledValue; + ret.isFulfilled = true; + } else if (this.isRejected()) { + ret.rejectionReason = this._settledValue; + ret.isRejected = true; + } + return ret; +}; + +Promise.prototype.all = function Promise$all() { + return new PromiseArray(this).promise(); +}; + + +Promise.is = function Promise$Is(val) { + return val instanceof Promise; +}; + +Promise.all = function Promise$All(promises) { + return new PromiseArray(promises).promise(); +}; + +Promise.prototype.error = function Promise$_error(fn) { + return this.caught(originatesFromRejection, fn); +}; + +Promise.prototype._resolveFromSyncValue = +function Promise$_resolveFromSyncValue(value) { + if (value === errorObj) { + this._cleanValues(); + this._setRejected(); + var reason = value.e; + this._settledValue = reason; + this._tryAttachExtraTrace(reason); + this._ensurePossibleRejectionHandled(); + } else { + var maybePromise = cast(value, void 0); + if (maybePromise instanceof Promise) { + this._follow(maybePromise); + } else { + this._cleanValues(); + this._setFulfilled(); + this._settledValue = value; + } + } +}; + +Promise.method = function Promise$_Method(fn) { + if (typeof fn !== "function") { + throw new TypeError("fn must be a function"); + } + return function Promise$_method() { + var value; + switch(arguments.length) { + case 0: value = tryCatch1(fn, this, void 0); break; + case 1: value = tryCatch1(fn, this, arguments[0]); break; + case 2: value = tryCatch2(fn, this, arguments[0], arguments[1]); break; + default: + var $_len = arguments.length;var args = new Array($_len); for(var $_i = 0; $_i < $_len; ++$_i) {args[$_i] = arguments[$_i];} + value = tryCatchApply(fn, args, this); break; + } + var ret = new Promise(INTERNAL); + ret._setTrace(void 0); + ret._resolveFromSyncValue(value); + return ret; + }; +}; + +Promise.attempt = Promise["try"] = function Promise$_Try(fn, args, ctx) { + if (typeof fn !== "function") { + return apiRejection("fn must be a function"); + } + var value = isArray(args) + ? tryCatchApply(fn, args, ctx) + : tryCatch1(fn, ctx, args); + + var ret = new Promise(INTERNAL); + ret._setTrace(void 0); + ret._resolveFromSyncValue(value); + return ret; +}; + +Promise.defer = Promise.pending = function Promise$Defer() { + var promise = new Promise(INTERNAL); + promise._setTrace(void 0); + return new PromiseResolver(promise); +}; + +Promise.bind = function Promise$Bind(thisArg) { + var maybePromise = cast(thisArg, void 0); + var ret = new Promise(INTERNAL); + ret._setTrace(void 0); + + if (maybePromise instanceof Promise) { + var p = maybePromise.then(function(thisArg) { + ret._setBoundTo(thisArg); + }); + ret._follow(p); + } else { + ret._setBoundTo(thisArg); + ret._setFulfilled(); + } + return ret; +}; + +Promise.cast = function Promise$_Cast(obj) { + var ret = cast(obj, void 0); + if (!(ret instanceof Promise)) { + var val = ret; + ret = new Promise(INTERNAL); + ret._setTrace(void 0); + ret._setFulfilled(); + ret._cleanValues(); + ret._settledValue = val; + } + return ret; +}; + +Promise.resolve = Promise.fulfilled = Promise.cast; + +Promise.reject = Promise.rejected = function Promise$Reject(reason) { + var ret = new Promise(INTERNAL); + ret._setTrace(void 0); + markAsOriginatingFromRejection(reason); + ret._cleanValues(); + ret._setRejected(); + ret._settledValue = reason; + if (!canAttach(reason)) { + var trace = new Error(reason + ""); + ret._setCarriedStackTrace(trace); + } + ret._ensurePossibleRejectionHandled(); + return ret; +}; + +Promise.onPossiblyUnhandledRejection = +function Promise$OnPossiblyUnhandledRejection(fn) { + CapturedTrace.possiblyUnhandledRejection = typeof fn === "function" + ? fn : void 0; +}; + +var unhandledRejectionHandled; +Promise.onUnhandledRejectionHandled = +function Promise$onUnhandledRejectionHandled(fn) { + unhandledRejectionHandled = typeof fn === "function" ? fn : void 0; +}; + +var debugging = false || !!( + typeof process !== "undefined" && + typeof process.execPath === "string" && + typeof process.env === "object" && + (process.env["BLUEBIRD_DEBUG"] || + process.env["NODE_ENV"] === "development") +); + + +Promise.longStackTraces = function Promise$LongStackTraces() { + if (async.haveItemsQueued() && + debugging === false + ) { + throw new Error("cannot enable long stack traces after promises have been created"); + } + debugging = CapturedTrace.isSupported(); +}; + +Promise.hasLongStackTraces = function Promise$HasLongStackTraces() { + return debugging && CapturedTrace.isSupported(); +}; + +Promise.prototype._then = +function Promise$_then( + didFulfill, + didReject, + didProgress, + receiver, + internalData +) { + var haveInternalData = internalData !== void 0; + var ret = haveInternalData ? internalData : new Promise(INTERNAL); + + if (!haveInternalData) { + if (debugging) { + var haveSameContext = this._peekContext() === this._traceParent; + ret._traceParent = haveSameContext ? this._traceParent : this; + } + ret._propagateFrom(this, 7); + } + + var callbackIndex = + this._addCallbacks(didFulfill, didReject, didProgress, ret, receiver); + + if (this.isResolved()) { + async.invoke(this._queueSettleAt, this, callbackIndex); + } + + return ret; +}; + +Promise.prototype._length = function Promise$_length() { + return this._bitField & 262143; +}; + +Promise.prototype._isFollowingOrFulfilledOrRejected = +function Promise$_isFollowingOrFulfilledOrRejected() { + return (this._bitField & 939524096) > 0; +}; + +Promise.prototype._isFollowing = function Promise$_isFollowing() { + return (this._bitField & 536870912) === 536870912; +}; + +Promise.prototype._setLength = function Promise$_setLength(len) { + this._bitField = (this._bitField & -262144) | + (len & 262143); +}; + +Promise.prototype._setFulfilled = function Promise$_setFulfilled() { + this._bitField = this._bitField | 268435456; +}; + +Promise.prototype._setRejected = function Promise$_setRejected() { + this._bitField = this._bitField | 134217728; +}; + +Promise.prototype._setFollowing = function Promise$_setFollowing() { + this._bitField = this._bitField | 536870912; +}; + +Promise.prototype._setIsFinal = function Promise$_setIsFinal() { + this._bitField = this._bitField | 33554432; +}; + +Promise.prototype._isFinal = function Promise$_isFinal() { + return (this._bitField & 33554432) > 0; +}; + +Promise.prototype._cancellable = function Promise$_cancellable() { + return (this._bitField & 67108864) > 0; +}; + +Promise.prototype._setCancellable = function Promise$_setCancellable() { + this._bitField = this._bitField | 67108864; +}; + +Promise.prototype._unsetCancellable = function Promise$_unsetCancellable() { + this._bitField = this._bitField & (~67108864); +}; + +Promise.prototype._setRejectionIsUnhandled = +function Promise$_setRejectionIsUnhandled() { + this._bitField = this._bitField | 2097152; +}; + +Promise.prototype._unsetRejectionIsUnhandled = +function Promise$_unsetRejectionIsUnhandled() { + this._bitField = this._bitField & (~2097152); + if (this._isUnhandledRejectionNotified()) { + this._unsetUnhandledRejectionIsNotified(); + this._notifyUnhandledRejectionIsHandled(); + } +}; + +Promise.prototype._isRejectionUnhandled = +function Promise$_isRejectionUnhandled() { + return (this._bitField & 2097152) > 0; +}; + +Promise.prototype._setUnhandledRejectionIsNotified = +function Promise$_setUnhandledRejectionIsNotified() { + this._bitField = this._bitField | 524288; +}; + +Promise.prototype._unsetUnhandledRejectionIsNotified = +function Promise$_unsetUnhandledRejectionIsNotified() { + this._bitField = this._bitField & (~524288); +}; + +Promise.prototype._isUnhandledRejectionNotified = +function Promise$_isUnhandledRejectionNotified() { + return (this._bitField & 524288) > 0; +}; + +Promise.prototype._setCarriedStackTrace = +function Promise$_setCarriedStackTrace(capturedTrace) { + this._bitField = this._bitField | 1048576; + this._fulfillmentHandler0 = capturedTrace; +}; + +Promise.prototype._unsetCarriedStackTrace = +function Promise$_unsetCarriedStackTrace() { + this._bitField = this._bitField & (~1048576); + this._fulfillmentHandler0 = void 0; +}; + +Promise.prototype._isCarryingStackTrace = +function Promise$_isCarryingStackTrace() { + return (this._bitField & 1048576) > 0; +}; + +Promise.prototype._getCarriedStackTrace = +function Promise$_getCarriedStackTrace() { + return this._isCarryingStackTrace() + ? this._fulfillmentHandler0 + : void 0; +}; + +Promise.prototype._receiverAt = function Promise$_receiverAt(index) { + var ret = index === 0 + ? this._receiver0 + : this[(index << 2) + index - 5 + 4]; + if (this._isBound() && ret === void 0) { + return this._boundTo; + } + return ret; +}; + +Promise.prototype._promiseAt = function Promise$_promiseAt(index) { + return index === 0 + ? this._promise0 + : this[(index << 2) + index - 5 + 3]; +}; + +Promise.prototype._fulfillmentHandlerAt = +function Promise$_fulfillmentHandlerAt(index) { + return index === 0 + ? this._fulfillmentHandler0 + : this[(index << 2) + index - 5 + 0]; +}; + +Promise.prototype._rejectionHandlerAt = +function Promise$_rejectionHandlerAt(index) { + return index === 0 + ? this._rejectionHandler0 + : this[(index << 2) + index - 5 + 1]; +}; + +Promise.prototype._addCallbacks = function Promise$_addCallbacks( + fulfill, + reject, + progress, + promise, + receiver +) { + var index = this._length(); + + if (index >= 262143 - 5) { + index = 0; + this._setLength(0); + } + + if (index === 0) { + this._promise0 = promise; + if (receiver !== void 0) this._receiver0 = receiver; + if (typeof fulfill === "function" && !this._isCarryingStackTrace()) + this._fulfillmentHandler0 = fulfill; + if (typeof reject === "function") this._rejectionHandler0 = reject; + if (typeof progress === "function") this._progressHandler0 = progress; + } else { + var base = (index << 2) + index - 5; + this[base + 3] = promise; + this[base + 4] = receiver; + this[base + 0] = typeof fulfill === "function" + ? fulfill : void 0; + this[base + 1] = typeof reject === "function" + ? reject : void 0; + this[base + 2] = typeof progress === "function" + ? progress : void 0; + } + this._setLength(index + 1); + return index; +}; + +Promise.prototype._setProxyHandlers = +function Promise$_setProxyHandlers(receiver, promiseSlotValue) { + var index = this._length(); + + if (index >= 262143 - 5) { + index = 0; + this._setLength(0); + } + if (index === 0) { + this._promise0 = promiseSlotValue; + this._receiver0 = receiver; + } else { + var base = (index << 2) + index - 5; + this[base + 3] = promiseSlotValue; + this[base + 4] = receiver; + this[base + 0] = + this[base + 1] = + this[base + 2] = void 0; + } + this._setLength(index + 1); +}; + +Promise.prototype._proxyPromiseArray = +function Promise$_proxyPromiseArray(promiseArray, index) { + this._setProxyHandlers(promiseArray, index); +}; + +Promise.prototype._proxyPromise = function Promise$_proxyPromise(promise) { + promise._setProxied(); + this._setProxyHandlers(promise, -15); +}; + +Promise.prototype._setBoundTo = function Promise$_setBoundTo(obj) { + if (obj !== void 0) { + this._bitField = this._bitField | 8388608; + this._boundTo = obj; + } else { + this._bitField = this._bitField & (~8388608); + } +}; + +Promise.prototype._isBound = function Promise$_isBound() { + return (this._bitField & 8388608) === 8388608; +}; + +Promise.prototype._resolveFromResolver = +function Promise$_resolveFromResolver(resolver) { + var promise = this; + this._setTrace(void 0); + this._pushContext(); + + function Promise$_resolver(val) { + if (promise._tryFollow(val)) { + return; + } + promise._fulfill(val); + } + function Promise$_rejecter(val) { + var trace = canAttach(val) ? val : new Error(val + ""); + promise._attachExtraTrace(trace); + markAsOriginatingFromRejection(val); + promise._reject(val, trace === val ? void 0 : trace); + } + var r = tryCatch2(resolver, void 0, Promise$_resolver, Promise$_rejecter); + this._popContext(); + + if (r !== void 0 && r === errorObj) { + var e = r.e; + var trace = canAttach(e) ? e : new Error(e + ""); + promise._reject(e, trace); + } +}; + +Promise.prototype._spreadSlowCase = +function Promise$_spreadSlowCase(targetFn, promise, values, boundTo) { + var promiseForAll = new PromiseArray(values).promise(); + var promise2 = promiseForAll._then(function() { + return targetFn.apply(boundTo, arguments); + }, void 0, void 0, APPLY, void 0); + promise._follow(promise2); +}; + +Promise.prototype._callSpread = +function Promise$_callSpread(handler, promise, value) { + var boundTo = this._boundTo; + if (isArray(value)) { + for (var i = 0, len = value.length; i < len; ++i) { + if (cast(value[i], void 0) instanceof Promise) { + this._spreadSlowCase(handler, promise, value, boundTo); + return; + } + } + } + promise._pushContext(); + return tryCatchApply(handler, value, boundTo); +}; + +Promise.prototype._callHandler = +function Promise$_callHandler( + handler, receiver, promise, value) { + var x; + if (receiver === APPLY && !this.isRejected()) { + x = this._callSpread(handler, promise, value); + } else { + promise._pushContext(); + x = tryCatch1(handler, receiver, value); + } + promise._popContext(); + return x; +}; + +Promise.prototype._settlePromiseFromHandler = +function Promise$_settlePromiseFromHandler( + handler, receiver, value, promise +) { + if (!(promise instanceof Promise)) { + handler.call(receiver, value, promise); + return; + } + if (promise.isResolved()) return; + var x = this._callHandler(handler, receiver, promise, value); + if (promise._isFollowing()) return; + + if (x === errorObj || x === promise || x === NEXT_FILTER) { + var err = x === promise + ? makeSelfResolutionError() + : x.e; + var trace = canAttach(err) ? err : new Error(err + ""); + if (x !== NEXT_FILTER) promise._attachExtraTrace(trace); + promise._rejectUnchecked(err, trace); + } else { + var castValue = cast(x, promise); + if (castValue instanceof Promise) { + if (castValue.isRejected() && + !castValue._isCarryingStackTrace() && + !canAttach(castValue._settledValue)) { + var trace = new Error(castValue._settledValue + ""); + promise._attachExtraTrace(trace); + castValue._setCarriedStackTrace(trace); + } + promise._follow(castValue); + promise._propagateFrom(castValue, 1); + } else { + promise._fulfillUnchecked(x); + } + } +}; + +Promise.prototype._follow = +function Promise$_follow(promise) { + this._setFollowing(); + + if (promise.isPending()) { + this._propagateFrom(promise, 1); + promise._proxyPromise(this); + } else if (promise.isFulfilled()) { + this._fulfillUnchecked(promise._settledValue); + } else { + this._rejectUnchecked(promise._settledValue, + promise._getCarriedStackTrace()); + } + + if (promise._isRejectionUnhandled()) promise._unsetRejectionIsUnhandled(); + + if (debugging && + promise._traceParent == null) { + promise._traceParent = this; + } +}; + +Promise.prototype._tryFollow = +function Promise$_tryFollow(value) { + if (this._isFollowingOrFulfilledOrRejected() || + value === this) { + return false; + } + var maybePromise = cast(value, void 0); + if (!(maybePromise instanceof Promise)) { + return false; + } + this._follow(maybePromise); + return true; +}; + +Promise.prototype._resetTrace = function Promise$_resetTrace() { + if (debugging) { + this._trace = new CapturedTrace(this._peekContext() === void 0); + } +}; + +Promise.prototype._setTrace = function Promise$_setTrace(parent) { + if (debugging) { + var context = this._peekContext(); + this._traceParent = context; + var isTopLevel = context === void 0; + if (parent !== void 0 && + parent._traceParent === context) { + this._trace = parent._trace; + } else { + this._trace = new CapturedTrace(isTopLevel); + } + } + return this; +}; + +Promise.prototype._tryAttachExtraTrace = +function Promise$_tryAttachExtraTrace(error) { + if (canAttach(error)) { + this._attachExtraTrace(error); + } +}; + +Promise.prototype._attachExtraTrace = +function Promise$_attachExtraTrace(error) { + if (debugging) { + var promise = this; + var stack = error.stack; + stack = typeof stack === "string" ? stack.split("\n") : []; + CapturedTrace.protectErrorMessageNewlines(stack); + var headerLineCount = 1; + var combinedTraces = 1; + while(promise != null && + promise._trace != null) { + stack = CapturedTrace.combine( + stack, + promise._trace.stack.split("\n") + ); + promise = promise._traceParent; + combinedTraces++; + } + + var stackTraceLimit = Error.stackTraceLimit || 10; + var max = (stackTraceLimit + headerLineCount) * combinedTraces; + var len = stack.length; + if (len > max) { + stack.length = max; + } + + if (len > 0) + stack[0] = stack[0].split("\u0002\u0000\u0001").join("\n"); + + if (stack.length <= headerLineCount) { + error.stack = "(No stack trace)"; + } else { + error.stack = stack.join("\n"); + } + } +}; + +Promise.prototype._cleanValues = function Promise$_cleanValues() { + if (this._cancellable()) { + this._cancellationParent = void 0; + } +}; + +Promise.prototype._propagateFrom = +function Promise$_propagateFrom(parent, flags) { + if ((flags & 1) > 0 && parent._cancellable()) { + this._setCancellable(); + this._cancellationParent = parent; + } + if ((flags & 4) > 0) { + this._setBoundTo(parent._boundTo); + } + if ((flags & 2) > 0) { + this._setTrace(parent); + } +}; + +Promise.prototype._fulfill = function Promise$_fulfill(value) { + if (this._isFollowingOrFulfilledOrRejected()) return; + this._fulfillUnchecked(value); +}; + +Promise.prototype._reject = +function Promise$_reject(reason, carriedStackTrace) { + if (this._isFollowingOrFulfilledOrRejected()) return; + this._rejectUnchecked(reason, carriedStackTrace); +}; + +Promise.prototype._settlePromiseAt = function Promise$_settlePromiseAt(index) { + var handler = this.isFulfilled() + ? this._fulfillmentHandlerAt(index) + : this._rejectionHandlerAt(index); + + var value = this._settledValue; + var receiver = this._receiverAt(index); + var promise = this._promiseAt(index); + + if (typeof handler === "function") { + this._settlePromiseFromHandler(handler, receiver, value, promise); + } else { + var done = false; + var isFulfilled = this.isFulfilled(); + if (receiver !== void 0) { + if (receiver instanceof Promise && + receiver._isProxied()) { + receiver._unsetProxied(); + + if (isFulfilled) receiver._fulfillUnchecked(value); + else receiver._rejectUnchecked(value, + this._getCarriedStackTrace()); + done = true; + } else if (receiver instanceof PromiseArray) { + if (isFulfilled) receiver._promiseFulfilled(value, promise); + else receiver._promiseRejected(value, promise); + done = true; + } + } + + if (!done) { + if (isFulfilled) promise._fulfill(value); + else promise._reject(value, this._getCarriedStackTrace()); + } + } + + if (index >= 4) { + this._queueGC(); + } +}; + +Promise.prototype._isProxied = function Promise$_isProxied() { + return (this._bitField & 4194304) === 4194304; +}; + +Promise.prototype._setProxied = function Promise$_setProxied() { + this._bitField = this._bitField | 4194304; +}; + +Promise.prototype._unsetProxied = function Promise$_unsetProxied() { + this._bitField = this._bitField & (~4194304); +}; + +Promise.prototype._isGcQueued = function Promise$_isGcQueued() { + return (this._bitField & -1073741824) === -1073741824; +}; + +Promise.prototype._setGcQueued = function Promise$_setGcQueued() { + this._bitField = this._bitField | -1073741824; +}; + +Promise.prototype._unsetGcQueued = function Promise$_unsetGcQueued() { + this._bitField = this._bitField & (~-1073741824); +}; + +Promise.prototype._queueGC = function Promise$_queueGC() { + if (this._isGcQueued()) return; + this._setGcQueued(); + async.invokeLater(this._gc, this, void 0); +}; + +Promise.prototype._gc = function Promise$gc() { + var len = this._length() * 5 - 5; + for (var i = 0; i < len; i++) { + delete this[i]; + } + this._clearFirstHandlerData(); + this._setLength(0); + this._unsetGcQueued(); +}; + +Promise.prototype._clearFirstHandlerData = +function Promise$_clearFirstHandlerData() { + this._fulfillmentHandler0 = void 0; + this._rejectionHandler0 = void 0; + this._promise0 = void 0; + this._receiver0 = void 0; +}; + +Promise.prototype._queueSettleAt = function Promise$_queueSettleAt(index) { + if (this._isRejectionUnhandled()) this._unsetRejectionIsUnhandled(); + async.invoke(this._settlePromiseAt, this, index); +}; + +Promise.prototype._fulfillUnchecked = +function Promise$_fulfillUnchecked(value) { + if (!this.isPending()) return; + if (value === this) { + var err = makeSelfResolutionError(); + this._attachExtraTrace(err); + return this._rejectUnchecked(err, void 0); + } + this._cleanValues(); + this._setFulfilled(); + this._settledValue = value; + var len = this._length(); + + if (len > 0) { + async.invoke(this._settlePromises, this, len); + } +}; + +Promise.prototype._rejectUncheckedCheckError = +function Promise$_rejectUncheckedCheckError(reason) { + var trace = canAttach(reason) ? reason : new Error(reason + ""); + this._rejectUnchecked(reason, trace === reason ? void 0 : trace); +}; + +Promise.prototype._rejectUnchecked = +function Promise$_rejectUnchecked(reason, trace) { + if (!this.isPending()) return; + if (reason === this) { + var err = makeSelfResolutionError(); + this._attachExtraTrace(err); + return this._rejectUnchecked(err); + } + this._cleanValues(); + this._setRejected(); + this._settledValue = reason; + + if (this._isFinal()) { + async.invokeLater(thrower, void 0, trace === void 0 ? reason : trace); + return; + } + var len = this._length(); + + if (trace !== void 0) this._setCarriedStackTrace(trace); + + if (len > 0) { + async.invoke(this._rejectPromises, this, null); + } else { + this._ensurePossibleRejectionHandled(); + } +}; + +Promise.prototype._rejectPromises = function Promise$_rejectPromises() { + this._settlePromises(); + this._unsetCarriedStackTrace(); +}; + +Promise.prototype._settlePromises = function Promise$_settlePromises() { + var len = this._length(); + for (var i = 0; i < len; i++) { + this._settlePromiseAt(i); + } +}; + +Promise.prototype._ensurePossibleRejectionHandled = +function Promise$_ensurePossibleRejectionHandled() { + this._setRejectionIsUnhandled(); + if (CapturedTrace.possiblyUnhandledRejection !== void 0) { + async.invokeLater(this._notifyUnhandledRejection, this, void 0); + } +}; + +Promise.prototype._notifyUnhandledRejectionIsHandled = +function Promise$_notifyUnhandledRejectionIsHandled() { + if (typeof unhandledRejectionHandled === "function") { + async.invokeLater(unhandledRejectionHandled, void 0, this); + } +}; + +Promise.prototype._notifyUnhandledRejection = +function Promise$_notifyUnhandledRejection() { + if (this._isRejectionUnhandled()) { + var reason = this._settledValue; + var trace = this._getCarriedStackTrace(); + + this._setUnhandledRejectionIsNotified(); + + if (trace !== void 0) { + this._unsetCarriedStackTrace(); + reason = trace; + } + if (typeof CapturedTrace.possiblyUnhandledRejection === "function") { + CapturedTrace.possiblyUnhandledRejection(reason, this); + } + } +}; + +var contextStack = []; +Promise.prototype._peekContext = function Promise$_peekContext() { + var lastIndex = contextStack.length - 1; + if (lastIndex >= 0) { + return contextStack[lastIndex]; + } + return void 0; + +}; + +Promise.prototype._pushContext = function Promise$_pushContext() { + if (!debugging) return; + contextStack.push(this); +}; + +Promise.prototype._popContext = function Promise$_popContext() { + if (!debugging) return; + contextStack.pop(); +}; + +Promise.noConflict = function Promise$NoConflict() { + return noConflict(Promise); +}; + +Promise.setScheduler = function(fn) { + if (typeof fn !== "function") throw new TypeError("fn must be a function"); + async._schedule = fn; +}; + +if (!CapturedTrace.isSupported()) { + Promise.longStackTraces = function(){}; + debugging = false; +} + +Promise._makeSelfResolutionError = makeSelfResolutionError; +require("./finally.js")(Promise, NEXT_FILTER, cast); +require("./direct_resolve.js")(Promise); +require("./synchronous_inspection.js")(Promise); +require("./join.js")(Promise, PromiseArray, cast, INTERNAL); +Promise.RangeError = RangeError; +Promise.CancellationError = CancellationError; +Promise.TimeoutError = TimeoutError; +Promise.TypeError = TypeError; +Promise.OperationalError = OperationalError; +Promise.RejectionError = OperationalError; +Promise.AggregateError = errors.AggregateError; + +util.toFastProperties(Promise); +util.toFastProperties(Promise.prototype); +Promise.Promise = Promise; +require('./timers.js')(Promise,INTERNAL,cast); +require('./race.js')(Promise,INTERNAL,cast); +require('./call_get.js')(Promise); +require('./generators.js')(Promise,apiRejection,INTERNAL,cast); +require('./map.js')(Promise,PromiseArray,apiRejection,cast,INTERNAL); +require('./nodeify.js')(Promise); +require('./promisify.js')(Promise,INTERNAL); +require('./props.js')(Promise,PromiseArray,cast); +require('./reduce.js')(Promise,PromiseArray,apiRejection,cast,INTERNAL); +require('./settle.js')(Promise,PromiseArray); +require('./some.js')(Promise,PromiseArray,apiRejection); +require('./progress.js')(Promise,PromiseArray); +require('./cancel.js')(Promise,INTERNAL); +require('./filter.js')(Promise,INTERNAL); +require('./any.js')(Promise,PromiseArray); +require('./each.js')(Promise,INTERNAL); +require('./using.js')(Promise,apiRejection,cast); + +Promise.prototype = Promise.prototype; +return Promise; + +}; + +},{"./any.js":1,"./async.js":2,"./call_get.js":4,"./cancel.js":5,"./captured_trace.js":6,"./catch_filter.js":7,"./direct_resolve.js":8,"./each.js":9,"./errors.js":10,"./errors_api_rejection":11,"./filter.js":13,"./finally.js":14,"./generators.js":15,"./join.js":16,"./map.js":17,"./nodeify.js":18,"./progress.js":19,"./promise_array.js":21,"./promise_resolver.js":22,"./promisify.js":23,"./props.js":24,"./race.js":26,"./reduce.js":27,"./settle.js":29,"./some.js":30,"./synchronous_inspection.js":31,"./thenables.js":32,"./timers.js":33,"./using.js":34,"./util.js":35}],21:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(Promise, INTERNAL, cast) { +var canAttach = require("./errors.js").canAttach; +var util = require("./util.js"); +var isArray = util.isArray; + +function toResolutionValue(val) { + switch(val) { + case -1: return void 0; + case -2: return []; + case -3: return {}; + } +} + +function PromiseArray(values) { + var promise = this._promise = new Promise(INTERNAL); + var parent = void 0; + if (values instanceof Promise) { + parent = values; + promise._propagateFrom(parent, 1 | 4); + } + promise._setTrace(parent); + this._values = values; + this._length = 0; + this._totalResolved = 0; + this._init(void 0, -2); +} +PromiseArray.prototype.length = function PromiseArray$length() { + return this._length; +}; + +PromiseArray.prototype.promise = function PromiseArray$promise() { + return this._promise; +}; + +PromiseArray.prototype._init = +function PromiseArray$_init(_, resolveValueIfEmpty) { + var values = cast(this._values, void 0); + if (values instanceof Promise) { + this._values = values; + values._setBoundTo(this._promise._boundTo); + if (values.isFulfilled()) { + values = values._settledValue; + if (!isArray(values)) { + var err = new Promise.TypeError("expecting an array, a promise or a thenable"); + this.__hardReject__(err); + return; + } + } else if (values.isPending()) { + values._then( + PromiseArray$_init, + this._reject, + void 0, + this, + resolveValueIfEmpty + ); + return; + } else { + values._unsetRejectionIsUnhandled(); + this._reject(values._settledValue); + return; + } + } else if (!isArray(values)) { + var err = new Promise.TypeError("expecting an array, a promise or a thenable"); + this.__hardReject__(err); + return; + } + + if (values.length === 0) { + if (resolveValueIfEmpty === -5) { + this._resolveEmptyArray(); + } + else { + this._resolve(toResolutionValue(resolveValueIfEmpty)); + } + return; + } + var len = this.getActualLength(values.length); + var newLen = len; + var newValues = this.shouldCopyValues() ? new Array(len) : this._values; + var isDirectScanNeeded = false; + for (var i = 0; i < len; ++i) { + var maybePromise = cast(values[i], void 0); + if (maybePromise instanceof Promise) { + if (maybePromise.isPending()) { + maybePromise._proxyPromiseArray(this, i); + } else { + maybePromise._unsetRejectionIsUnhandled(); + isDirectScanNeeded = true; + } + } else { + isDirectScanNeeded = true; + } + newValues[i] = maybePromise; + } + this._values = newValues; + this._length = newLen; + if (isDirectScanNeeded) { + this._scanDirectValues(len); + } +}; + +PromiseArray.prototype._settlePromiseAt = +function PromiseArray$_settlePromiseAt(index) { + var value = this._values[index]; + if (!(value instanceof Promise)) { + this._promiseFulfilled(value, index); + } else if (value.isFulfilled()) { + this._promiseFulfilled(value._settledValue, index); + } else if (value.isRejected()) { + this._promiseRejected(value._settledValue, index); + } +}; + +PromiseArray.prototype._scanDirectValues = +function PromiseArray$_scanDirectValues(len) { + for (var i = 0; i < len; ++i) { + if (this._isResolved()) { + break; + } + this._settlePromiseAt(i); + } +}; + +PromiseArray.prototype._isResolved = function PromiseArray$_isResolved() { + return this._values === null; +}; + +PromiseArray.prototype._resolve = function PromiseArray$_resolve(value) { + this._values = null; + this._promise._fulfill(value); +}; + +PromiseArray.prototype.__hardReject__ = +PromiseArray.prototype._reject = function PromiseArray$_reject(reason) { + this._values = null; + var trace = canAttach(reason) ? reason : new Error(reason + ""); + this._promise._attachExtraTrace(trace); + this._promise._reject(reason, trace); +}; + +PromiseArray.prototype._promiseProgressed = +function PromiseArray$_promiseProgressed(progressValue, index) { + if (this._isResolved()) return; + this._promise._progress({ + index: index, + value: progressValue + }); +}; + + +PromiseArray.prototype._promiseFulfilled = +function PromiseArray$_promiseFulfilled(value, index) { + if (this._isResolved()) return; + this._values[index] = value; + var totalResolved = ++this._totalResolved; + if (totalResolved >= this._length) { + this._resolve(this._values); + } +}; + +PromiseArray.prototype._promiseRejected = +function PromiseArray$_promiseRejected(reason, index) { + if (this._isResolved()) return; + this._totalResolved++; + this._reject(reason); +}; + +PromiseArray.prototype.shouldCopyValues = +function PromiseArray$_shouldCopyValues() { + return true; +}; + +PromiseArray.prototype.getActualLength = +function PromiseArray$getActualLength(len) { + return len; +}; + +return PromiseArray; +}; + +},{"./errors.js":10,"./util.js":35}],22:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +var util = require("./util.js"); +var maybeWrapAsError = util.maybeWrapAsError; +var errors = require("./errors.js"); +var TimeoutError = errors.TimeoutError; +var OperationalError = errors.OperationalError; +var async = require("./async.js"); +var haveGetters = util.haveGetters; +var es5 = require("./es5.js"); + +function isUntypedError(obj) { + return obj instanceof Error && + es5.getPrototypeOf(obj) === Error.prototype; +} + +function wrapAsOperationalError(obj) { + var ret; + if (isUntypedError(obj)) { + ret = new OperationalError(obj); + } else { + ret = obj; + } + errors.markAsOriginatingFromRejection(ret); + return ret; +} + +function nodebackForPromise(promise) { + function PromiseResolver$_callback(err, value) { + if (promise === null) return; + + if (err) { + var wrapped = wrapAsOperationalError(maybeWrapAsError(err)); + promise._attachExtraTrace(wrapped); + promise._reject(wrapped); + } else if (arguments.length > 2) { + var $_len = arguments.length;var args = new Array($_len - 1); for(var $_i = 1; $_i < $_len; ++$_i) {args[$_i - 1] = arguments[$_i];} + promise._fulfill(args); + } else { + promise._fulfill(value); + } + + promise = null; + } + return PromiseResolver$_callback; +} + + +var PromiseResolver; +if (!haveGetters) { + PromiseResolver = function PromiseResolver(promise) { + this.promise = promise; + this.asCallback = nodebackForPromise(promise); + this.callback = this.asCallback; + }; +} +else { + PromiseResolver = function PromiseResolver(promise) { + this.promise = promise; + }; +} +if (haveGetters) { + var prop = { + get: function() { + return nodebackForPromise(this.promise); + } + }; + es5.defineProperty(PromiseResolver.prototype, "asCallback", prop); + es5.defineProperty(PromiseResolver.prototype, "callback", prop); +} + +PromiseResolver._nodebackForPromise = nodebackForPromise; + +PromiseResolver.prototype.toString = function PromiseResolver$toString() { + return "[object PromiseResolver]"; +}; + +PromiseResolver.prototype.resolve = +PromiseResolver.prototype.fulfill = function PromiseResolver$resolve(value) { + if (!(this instanceof PromiseResolver)) { + throw new TypeError("Illegal invocation, resolver resolve/reject must be called within a resolver context. Consider using the promise constructor instead."); + } + + var promise = this.promise; + if (promise._tryFollow(value)) { + return; + } + async.invoke(promise._fulfill, promise, value); +}; + +PromiseResolver.prototype.reject = function PromiseResolver$reject(reason) { + if (!(this instanceof PromiseResolver)) { + throw new TypeError("Illegal invocation, resolver resolve/reject must be called within a resolver context. Consider using the promise constructor instead."); + } + + var promise = this.promise; + errors.markAsOriginatingFromRejection(reason); + var trace = errors.canAttach(reason) ? reason : new Error(reason + ""); + promise._attachExtraTrace(trace); + async.invoke(promise._reject, promise, reason); + if (trace !== reason) { + async.invoke(this._setCarriedStackTrace, this, trace); + } +}; + +PromiseResolver.prototype.progress = +function PromiseResolver$progress(value) { + if (!(this instanceof PromiseResolver)) { + throw new TypeError("Illegal invocation, resolver resolve/reject must be called within a resolver context. Consider using the promise constructor instead."); + } + async.invoke(this.promise._progress, this.promise, value); +}; + +PromiseResolver.prototype.cancel = function PromiseResolver$cancel() { + async.invoke(this.promise.cancel, this.promise, void 0); +}; + +PromiseResolver.prototype.timeout = function PromiseResolver$timeout() { + this.reject(new TimeoutError("timeout")); +}; + +PromiseResolver.prototype.isResolved = function PromiseResolver$isResolved() { + return this.promise.isResolved(); +}; + +PromiseResolver.prototype.toJSON = function PromiseResolver$toJSON() { + return this.promise.toJSON(); +}; + +PromiseResolver.prototype._setCarriedStackTrace = +function PromiseResolver$_setCarriedStackTrace(trace) { + if (this.promise.isRejected()) { + this.promise._setCarriedStackTrace(trace); + } +}; + +module.exports = PromiseResolver; + +},{"./async.js":2,"./errors.js":10,"./es5.js":12,"./util.js":35}],23:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(Promise, INTERNAL) { +var THIS = {}; +var util = require("./util.js"); +var nodebackForPromise = require("./promise_resolver.js") + ._nodebackForPromise; +var withAppended = util.withAppended; +var maybeWrapAsError = util.maybeWrapAsError; +var canEvaluate = util.canEvaluate; +var TypeError = require("./errors").TypeError; +var defaultSuffix = "Async"; +var defaultFilter = function(name, func) { + return util.isIdentifier(name) && + name.charAt(0) !== "_" && + !util.isClass(func); +}; +var defaultPromisified = {__isPromisified__: true}; + + +function escapeIdentRegex(str) { + return str.replace(/([$])/, "\\$"); +} + +function isPromisified(fn) { + try { + return fn.__isPromisified__ === true; + } + catch (e) { + return false; + } +} + +function hasPromisified(obj, key, suffix) { + var val = util.getDataPropertyOrDefault(obj, key + suffix, + defaultPromisified); + return val ? isPromisified(val) : false; +} +function checkValid(ret, suffix, suffixRegexp) { + for (var i = 0; i < ret.length; i += 2) { + var key = ret[i]; + if (suffixRegexp.test(key)) { + var keyWithoutAsyncSuffix = key.replace(suffixRegexp, ""); + for (var j = 0; j < ret.length; j += 2) { + if (ret[j] === keyWithoutAsyncSuffix) { + throw new TypeError("Cannot promisify an API " + + "that has normal methods with '"+suffix+"'-suffix"); + } + } + } + } +} + +function promisifiableMethods(obj, suffix, suffixRegexp, filter) { + var keys = util.inheritedDataKeys(obj); + var ret = []; + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + var value = obj[key]; + if (typeof value === "function" && + !isPromisified(value) && + !hasPromisified(obj, key, suffix) && + filter(key, value, obj)) { + ret.push(key, value); + } + } + checkValid(ret, suffix, suffixRegexp); + return ret; +} + +function switchCaseArgumentOrder(likelyArgumentCount) { + var ret = [likelyArgumentCount]; + var min = Math.max(0, likelyArgumentCount - 1 - 5); + for(var i = likelyArgumentCount - 1; i >= min; --i) { + if (i === likelyArgumentCount) continue; + ret.push(i); + } + for(var i = likelyArgumentCount + 1; i <= 5; ++i) { + ret.push(i); + } + return ret; +} + +function argumentSequence(argumentCount) { + return util.filledRange(argumentCount, "arguments[", "]"); +} + +function parameterDeclaration(parameterCount) { + return util.filledRange(parameterCount, "_arg", ""); +} + +function parameterCount(fn) { + if (typeof fn.length === "number") { + return Math.max(Math.min(fn.length, 1023 + 1), 0); + } + return 0; +} + +function generatePropertyAccess(key) { + if (util.isIdentifier(key)) { + return "." + key; + } + else return "['" + key.replace(/(['\\])/g, "\\$1") + "']"; +} + +function makeNodePromisifiedEval(callback, receiver, originalName, fn, suffix) { + var newParameterCount = Math.max(0, parameterCount(fn) - 1); + var argumentOrder = switchCaseArgumentOrder(newParameterCount); + var callbackName = + (typeof originalName === "string" && util.isIdentifier(originalName) + ? originalName + suffix + : "promisified"); + + function generateCallForArgumentCount(count) { + var args = argumentSequence(count).join(", "); + var comma = count > 0 ? ", " : ""; + var ret; + if (typeof callback === "string") { + ret = " \n\ + this.method({{args}}, fn); \n\ + break; \n\ + ".replace(".method", generatePropertyAccess(callback)); + } else if (receiver === THIS) { + ret = " \n\ + callback.call(this, {{args}}, fn); \n\ + break; \n\ + "; + } else if (receiver !== void 0) { + ret = " \n\ + callback.call(receiver, {{args}}, fn); \n\ + break; \n\ + "; + } else { + ret = " \n\ + callback({{args}}, fn); \n\ + break; \n\ + "; + } + return ret.replace("{{args}}", args).replace(", ", comma); + } + + function generateArgumentSwitchCase() { + var ret = ""; + for(var i = 0; i < argumentOrder.length; ++i) { + ret += "case " + argumentOrder[i] +":" + + generateCallForArgumentCount(argumentOrder[i]); + } + var codeForCall; + if (typeof callback === "string") { + codeForCall = " \n\ + this.property.apply(this, args); \n\ + " + .replace(".property", generatePropertyAccess(callback)); + } else if (receiver === THIS) { + codeForCall = " \n\ + callback.apply(this, args); \n\ + "; + } else { + codeForCall = " \n\ + callback.apply(receiver, args); \n\ + "; + } + + ret += " \n\ + default: \n\ + var args = new Array(len + 1); \n\ + var i = 0; \n\ + for (var i = 0; i < len; ++i) { \n\ + args[i] = arguments[i]; \n\ + } \n\ + args[i] = fn; \n\ + [CodeForCall] \n\ + break; \n\ + ".replace("[CodeForCall]", codeForCall); + return ret; + } + + return new Function("Promise", + "callback", + "receiver", + "withAppended", + "maybeWrapAsError", + "nodebackForPromise", + "INTERNAL"," \n\ + var ret = function FunctionName(Parameters) { \n\ + 'use strict'; \n\ + var len = arguments.length; \n\ + var promise = new Promise(INTERNAL); \n\ + promise._setTrace(void 0); \n\ + var fn = nodebackForPromise(promise); \n\ + try { \n\ + switch(len) { \n\ + [CodeForSwitchCase] \n\ + } \n\ + } catch (e) { \n\ + var wrapped = maybeWrapAsError(e); \n\ + promise._attachExtraTrace(wrapped); \n\ + promise._reject(wrapped); \n\ + } \n\ + return promise; \n\ + }; \n\ + ret.__isPromisified__ = true; \n\ + return ret; \n\ + " + .replace("FunctionName", callbackName) + .replace("Parameters", parameterDeclaration(newParameterCount)) + .replace("[CodeForSwitchCase]", generateArgumentSwitchCase()))( + Promise, + callback, + receiver, + withAppended, + maybeWrapAsError, + nodebackForPromise, + INTERNAL + ); +} + +function makeNodePromisifiedClosure(callback, receiver) { + function promisified() { + var _receiver = receiver; + if (receiver === THIS) _receiver = this; + if (typeof callback === "string") { + callback = _receiver[callback]; + } + var promise = new Promise(INTERNAL); + promise._setTrace(void 0); + var fn = nodebackForPromise(promise); + try { + callback.apply(_receiver, withAppended(arguments, fn)); + } catch(e) { + var wrapped = maybeWrapAsError(e); + promise._attachExtraTrace(wrapped); + promise._reject(wrapped); + } + return promise; + } + promisified.__isPromisified__ = true; + return promisified; +} + +var makeNodePromisified = canEvaluate + ? makeNodePromisifiedEval + : makeNodePromisifiedClosure; + +function promisifyAll(obj, suffix, filter, promisifier) { + var suffixRegexp = new RegExp(escapeIdentRegex(suffix) + "$"); + var methods = + promisifiableMethods(obj, suffix, suffixRegexp, filter); + + for (var i = 0, len = methods.length; i < len; i+= 2) { + var key = methods[i]; + var fn = methods[i+1]; + var promisifiedKey = key + suffix; + obj[promisifiedKey] = promisifier === makeNodePromisified + ? makeNodePromisified(key, THIS, key, fn, suffix) + : promisifier(fn); + } + util.toFastProperties(obj); + return obj; +} + +function promisify(callback, receiver) { + return makeNodePromisified(callback, receiver, void 0, callback); +} + +Promise.promisify = function Promise$Promisify(fn, receiver) { + if (typeof fn !== "function") { + throw new TypeError("fn must be a function"); + } + if (isPromisified(fn)) { + return fn; + } + return promisify(fn, arguments.length < 2 ? THIS : receiver); +}; + +Promise.promisifyAll = function Promise$PromisifyAll(target, options) { + if (typeof target !== "function" && typeof target !== "object") { + throw new TypeError("the target of promisifyAll must be an object or a function"); + } + options = Object(options); + var suffix = options.suffix; + if (typeof suffix !== "string") suffix = defaultSuffix; + var filter = options.filter; + if (typeof filter !== "function") filter = defaultFilter; + var promisifier = options.promisifier; + if (typeof promisifier !== "function") promisifier = makeNodePromisified; + + if (!util.isIdentifier(suffix)) { + throw new RangeError("suffix must be a valid identifier"); + } + + var keys = util.inheritedDataKeys(target, {includeHidden: true}); + for (var i = 0; i < keys.length; ++i) { + var value = target[keys[i]]; + if (keys[i] !== "constructor" && + util.isClass(value)) { + promisifyAll(value.prototype, suffix, filter, promisifier); + promisifyAll(value, suffix, filter, promisifier); + } + } + + return promisifyAll(target, suffix, filter, promisifier); +}; +}; + + +},{"./errors":10,"./promise_resolver.js":22,"./util.js":35}],24:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(Promise, PromiseArray, cast) { +var util = require("./util.js"); +var apiRejection = require("./errors_api_rejection")(Promise); +var isObject = util.isObject; +var es5 = require("./es5.js"); + +function PropertiesPromiseArray(obj) { + var keys = es5.keys(obj); + var len = keys.length; + var values = new Array(len * 2); + for (var i = 0; i < len; ++i) { + var key = keys[i]; + values[i] = obj[key]; + values[i + len] = key; + } + this.constructor$(values); +} +util.inherits(PropertiesPromiseArray, PromiseArray); + +PropertiesPromiseArray.prototype._init = +function PropertiesPromiseArray$_init() { + this._init$(void 0, -3) ; +}; + +PropertiesPromiseArray.prototype._promiseFulfilled = +function PropertiesPromiseArray$_promiseFulfilled(value, index) { + if (this._isResolved()) return; + this._values[index] = value; + var totalResolved = ++this._totalResolved; + if (totalResolved >= this._length) { + var val = {}; + var keyOffset = this.length(); + for (var i = 0, len = this.length(); i < len; ++i) { + val[this._values[i + keyOffset]] = this._values[i]; + } + this._resolve(val); + } +}; + +PropertiesPromiseArray.prototype._promiseProgressed = +function PropertiesPromiseArray$_promiseProgressed(value, index) { + if (this._isResolved()) return; + + this._promise._progress({ + key: this._values[index + this.length()], + value: value + }); +}; + +PropertiesPromiseArray.prototype.shouldCopyValues = +function PropertiesPromiseArray$_shouldCopyValues() { + return false; +}; + +PropertiesPromiseArray.prototype.getActualLength = +function PropertiesPromiseArray$getActualLength(len) { + return len >> 1; +}; + +function Promise$_Props(promises) { + var ret; + var castValue = cast(promises, void 0); + + if (!isObject(castValue)) { + return apiRejection("cannot await properties of a non-object"); + } else if (castValue instanceof Promise) { + ret = castValue._then(Promise.props, void 0, void 0, void 0, void 0); + } else { + ret = new PropertiesPromiseArray(castValue).promise(); + } + + if (castValue instanceof Promise) { + ret._propagateFrom(castValue, 4); + } + return ret; +} + +Promise.prototype.props = function Promise$props() { + return Promise$_Props(this); +}; + +Promise.props = function Promise$Props(promises) { + return Promise$_Props(promises); +}; +}; + +},{"./errors_api_rejection":11,"./es5.js":12,"./util.js":35}],25:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +function arrayCopy(src, srcIndex, dst, dstIndex, len) { + for (var j = 0; j < len; ++j) { + dst[j + dstIndex] = src[j + srcIndex]; + } +} + +function Queue(capacity) { + this._capacity = capacity; + this._length = 0; + this._front = 0; + this._makeCapacity(); +} + +Queue.prototype._willBeOverCapacity = +function Queue$_willBeOverCapacity(size) { + return this._capacity < size; +}; + +Queue.prototype._pushOne = function Queue$_pushOne(arg) { + var length = this.length(); + this._checkCapacity(length + 1); + var i = (this._front + length) & (this._capacity - 1); + this[i] = arg; + this._length = length + 1; +}; + +Queue.prototype.push = function Queue$push(fn, receiver, arg) { + var length = this.length() + 3; + if (this._willBeOverCapacity(length)) { + this._pushOne(fn); + this._pushOne(receiver); + this._pushOne(arg); + return; + } + var j = this._front + length - 3; + this._checkCapacity(length); + var wrapMask = this._capacity - 1; + this[(j + 0) & wrapMask] = fn; + this[(j + 1) & wrapMask] = receiver; + this[(j + 2) & wrapMask] = arg; + this._length = length; +}; + +Queue.prototype.shift = function Queue$shift() { + var front = this._front, + ret = this[front]; + + this[front] = void 0; + this._front = (front + 1) & (this._capacity - 1); + this._length--; + return ret; +}; + +Queue.prototype.length = function Queue$length() { + return this._length; +}; + +Queue.prototype._makeCapacity = function Queue$_makeCapacity() { + var len = this._capacity; + for (var i = 0; i < len; ++i) { + this[i] = void 0; + } +}; + +Queue.prototype._checkCapacity = function Queue$_checkCapacity(size) { + if (this._capacity < size) { + this._resizeTo(this._capacity << 3); + } +}; + +Queue.prototype._resizeTo = function Queue$_resizeTo(capacity) { + var oldFront = this._front; + var oldCapacity = this._capacity; + var oldQueue = new Array(oldCapacity); + var length = this.length(); + + arrayCopy(this, 0, oldQueue, 0, oldCapacity); + this._capacity = capacity; + this._makeCapacity(); + this._front = 0; + if (oldFront + length <= oldCapacity) { + arrayCopy(oldQueue, oldFront, this, 0, length); + } else { var lengthBeforeWrapping = + length - ((oldFront + length) & (oldCapacity - 1)); + + arrayCopy(oldQueue, oldFront, this, 0, lengthBeforeWrapping); + arrayCopy(oldQueue, 0, this, lengthBeforeWrapping, + length - lengthBeforeWrapping); + } +}; + +module.exports = Queue; + +},{}],26:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(Promise, INTERNAL, cast) { +var apiRejection = require("./errors_api_rejection.js")(Promise); +var isArray = require("./util.js").isArray; + +var raceLater = function Promise$_raceLater(promise) { + return promise.then(function(array) { + return Promise$_Race(array, promise); + }); +}; + +var hasOwn = {}.hasOwnProperty; +function Promise$_Race(promises, parent) { + var maybePromise = cast(promises, void 0); + + if (maybePromise instanceof Promise) { + return raceLater(maybePromise); + } else if (!isArray(promises)) { + return apiRejection("expecting an array, a promise or a thenable"); + } + + var ret = new Promise(INTERNAL); + if (parent !== void 0) { + ret._propagateFrom(parent, 7); + } else { + ret._setTrace(void 0); + } + var fulfill = ret._fulfill; + var reject = ret._reject; + for (var i = 0, len = promises.length; i < len; ++i) { + var val = promises[i]; + + if (val === void 0 && !(hasOwn.call(promises, i))) { + continue; + } + + Promise.cast(val)._then(fulfill, reject, void 0, ret, null); + } + return ret; +} + +Promise.race = function Promise$Race(promises) { + return Promise$_Race(promises, void 0); +}; + +Promise.prototype.race = function Promise$race() { + return Promise$_Race(this, void 0); +}; + +}; + +},{"./errors_api_rejection.js":11,"./util.js":35}],27:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(Promise, PromiseArray, apiRejection, cast, INTERNAL) { +var util = require("./util.js"); +var tryCatch4 = util.tryCatch4; +var tryCatch3 = util.tryCatch3; +var errorObj = util.errorObj; +function ReductionPromiseArray(promises, fn, accum, _each) { + this.constructor$(promises); + this._preservedValues = _each === INTERNAL ? [] : null; + this._zerothIsAccum = (accum === void 0); + this._gotAccum = false; + this._reducingIndex = (this._zerothIsAccum ? 1 : 0); + this._valuesPhase = undefined; + + var maybePromise = cast(accum, void 0); + var rejected = false; + var isPromise = maybePromise instanceof Promise; + if (isPromise) { + if (maybePromise.isPending()) { + maybePromise._proxyPromiseArray(this, -1); + } else if (maybePromise.isFulfilled()) { + accum = maybePromise.value(); + this._gotAccum = true; + } else { + maybePromise._unsetRejectionIsUnhandled(); + this._reject(maybePromise.reason()); + rejected = true; + } + } + if (!(isPromise || this._zerothIsAccum)) this._gotAccum = true; + this._callback = fn; + this._accum = accum; + if (!rejected) this._init$(void 0, -5); +} +util.inherits(ReductionPromiseArray, PromiseArray); + +ReductionPromiseArray.prototype._init = +function ReductionPromiseArray$_init() {}; + +ReductionPromiseArray.prototype._resolveEmptyArray = +function ReductionPromiseArray$_resolveEmptyArray() { + if (this._gotAccum || this._zerothIsAccum) { + this._resolve(this._preservedValues !== null + ? [] : this._accum); + } +}; + +ReductionPromiseArray.prototype._promiseFulfilled = +function ReductionPromiseArray$_promiseFulfilled(value, index) { + var values = this._values; + if (values === null) return; + var length = this.length(); + var preservedValues = this._preservedValues; + var isEach = preservedValues !== null; + var gotAccum = this._gotAccum; + var valuesPhase = this._valuesPhase; + var valuesPhaseIndex; + if (!valuesPhase) { + valuesPhase = this._valuesPhase = Array(length); + for (valuesPhaseIndex=0; valuesPhaseIndex + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +var schedule; +var _MutationObserver; +if (typeof process === "object" && typeof process.version === "string") { + schedule = function Promise$_Scheduler(fn) { + process.nextTick(fn); + }; +} +else if ((typeof MutationObserver !== "undefined" && + (_MutationObserver = MutationObserver)) || + (typeof WebKitMutationObserver !== "undefined" && + (_MutationObserver = WebKitMutationObserver))) { + schedule = (function() { + var div = document.createElement("div"); + var queuedFn = void 0; + var observer = new _MutationObserver( + function Promise$_Scheduler() { + var fn = queuedFn; + queuedFn = void 0; + fn(); + } + ); + observer.observe(div, { + attributes: true + }); + return function Promise$_Scheduler(fn) { + queuedFn = fn; + div.classList.toggle("foo"); + }; + + })(); +} +else if (typeof setTimeout !== "undefined") { + schedule = function Promise$_Scheduler(fn) { + setTimeout(fn, 0); + }; +} +else throw new Error("no async scheduler available"); +module.exports = schedule; + +},{}],29:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = + function(Promise, PromiseArray) { +var PromiseInspection = Promise.PromiseInspection; +var util = require("./util.js"); + +function SettledPromiseArray(values) { + this.constructor$(values); +} +util.inherits(SettledPromiseArray, PromiseArray); + +SettledPromiseArray.prototype._promiseResolved = +function SettledPromiseArray$_promiseResolved(index, inspection) { + this._values[index] = inspection; + var totalResolved = ++this._totalResolved; + if (totalResolved >= this._length) { + this._resolve(this._values); + } +}; + +SettledPromiseArray.prototype._promiseFulfilled = +function SettledPromiseArray$_promiseFulfilled(value, index) { + if (this._isResolved()) return; + var ret = new PromiseInspection(); + ret._bitField = 268435456; + ret._settledValue = value; + this._promiseResolved(index, ret); +}; +SettledPromiseArray.prototype._promiseRejected = +function SettledPromiseArray$_promiseRejected(reason, index) { + if (this._isResolved()) return; + var ret = new PromiseInspection(); + ret._bitField = 134217728; + ret._settledValue = reason; + this._promiseResolved(index, ret); +}; + +Promise.settle = function Promise$Settle(promises) { + return new SettledPromiseArray(promises).promise(); +}; + +Promise.prototype.settle = function Promise$settle() { + return new SettledPromiseArray(this).promise(); +}; +}; + +},{"./util.js":35}],30:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = +function(Promise, PromiseArray, apiRejection) { +var util = require("./util.js"); +var RangeError = require("./errors.js").RangeError; +var AggregateError = require("./errors.js").AggregateError; +var isArray = util.isArray; + + +function SomePromiseArray(values) { + this.constructor$(values); + this._howMany = 0; + this._unwrap = false; + this._initialized = false; +} +util.inherits(SomePromiseArray, PromiseArray); + +SomePromiseArray.prototype._init = function SomePromiseArray$_init() { + if (!this._initialized) { + return; + } + if (this._howMany === 0) { + this._resolve([]); + return; + } + this._init$(void 0, -5); + var isArrayResolved = isArray(this._values); + if (!this._isResolved() && + isArrayResolved && + this._howMany > this._canPossiblyFulfill()) { + this._reject(this._getRangeError(this.length())); + } +}; + +SomePromiseArray.prototype.init = function SomePromiseArray$init() { + this._initialized = true; + this._init(); +}; + +SomePromiseArray.prototype.setUnwrap = function SomePromiseArray$setUnwrap() { + this._unwrap = true; +}; + +SomePromiseArray.prototype.howMany = function SomePromiseArray$howMany() { + return this._howMany; +}; + +SomePromiseArray.prototype.setHowMany = +function SomePromiseArray$setHowMany(count) { + if (this._isResolved()) return; + this._howMany = count; +}; + +SomePromiseArray.prototype._promiseFulfilled = +function SomePromiseArray$_promiseFulfilled(value) { + if (this._isResolved()) return; + this._addFulfilled(value); + if (this._fulfilled() === this.howMany()) { + this._values.length = this.howMany(); + if (this.howMany() === 1 && this._unwrap) { + this._resolve(this._values[0]); + } else { + this._resolve(this._values); + } + } + +}; +SomePromiseArray.prototype._promiseRejected = +function SomePromiseArray$_promiseRejected(reason) { + if (this._isResolved()) return; + this._addRejected(reason); + if (this.howMany() > this._canPossiblyFulfill()) { + var e = new AggregateError(); + for (var i = this.length(); i < this._values.length; ++i) { + e.push(this._values[i]); + } + this._reject(e); + } +}; + +SomePromiseArray.prototype._fulfilled = function SomePromiseArray$_fulfilled() { + return this._totalResolved; +}; + +SomePromiseArray.prototype._rejected = function SomePromiseArray$_rejected() { + return this._values.length - this.length(); +}; + +SomePromiseArray.prototype._addRejected = +function SomePromiseArray$_addRejected(reason) { + this._values.push(reason); +}; + +SomePromiseArray.prototype._addFulfilled = +function SomePromiseArray$_addFulfilled(value) { + this._values[this._totalResolved++] = value; +}; + +SomePromiseArray.prototype._canPossiblyFulfill = +function SomePromiseArray$_canPossiblyFulfill() { + return this.length() - this._rejected(); +}; + +SomePromiseArray.prototype._getRangeError = +function SomePromiseArray$_getRangeError(count) { + var message = "Input array must contain at least " + + this._howMany + " items but contains only " + count + " items"; + return new RangeError(message); +}; + +SomePromiseArray.prototype._resolveEmptyArray = +function SomePromiseArray$_resolveEmptyArray() { + this._reject(this._getRangeError(0)); +}; + +function Promise$_Some(promises, howMany) { + if ((howMany | 0) !== howMany || howMany < 0) { + return apiRejection("expecting a positive integer"); + } + var ret = new SomePromiseArray(promises); + var promise = ret.promise(); + if (promise.isRejected()) { + return promise; + } + ret.setHowMany(howMany); + ret.init(); + return promise; +} + +Promise.some = function Promise$Some(promises, howMany) { + return Promise$_Some(promises, howMany); +}; + +Promise.prototype.some = function Promise$some(howMany) { + return Promise$_Some(this, howMany); +}; + +Promise._SomePromiseArray = SomePromiseArray; +}; + +},{"./errors.js":10,"./util.js":35}],31:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(Promise) { +function PromiseInspection(promise) { + if (promise !== void 0) { + this._bitField = promise._bitField; + this._settledValue = promise.isResolved() + ? promise._settledValue + : void 0; + } + else { + this._bitField = 0; + this._settledValue = void 0; + } +} + +PromiseInspection.prototype.isFulfilled = +Promise.prototype.isFulfilled = function Promise$isFulfilled() { + return (this._bitField & 268435456) > 0; +}; + +PromiseInspection.prototype.isRejected = +Promise.prototype.isRejected = function Promise$isRejected() { + return (this._bitField & 134217728) > 0; +}; + +PromiseInspection.prototype.isPending = +Promise.prototype.isPending = function Promise$isPending() { + return (this._bitField & 402653184) === 0; +}; + +PromiseInspection.prototype.value = +Promise.prototype.value = function Promise$value() { + if (!this.isFulfilled()) { + throw new TypeError("cannot get fulfillment value of a non-fulfilled promise"); + } + return this._settledValue; +}; + +PromiseInspection.prototype.error = +PromiseInspection.prototype.reason = +Promise.prototype.reason = function Promise$reason() { + if (!this.isRejected()) { + throw new TypeError("cannot get rejection reason of a non-rejected promise"); + } + return this._settledValue; +}; + +PromiseInspection.prototype.isResolved = +Promise.prototype.isResolved = function Promise$isResolved() { + return (this._bitField & 402653184) > 0; +}; + +Promise.PromiseInspection = PromiseInspection; +}; + +},{}],32:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function(Promise, INTERNAL) { +var util = require("./util.js"); +var canAttach = require("./errors.js").canAttach; +var errorObj = util.errorObj; +var isObject = util.isObject; + +function getThen(obj) { + try { + return obj.then; + } + catch(e) { + errorObj.e = e; + return errorObj; + } +} + +function Promise$_Cast(obj, originalPromise) { + if (isObject(obj)) { + if (obj instanceof Promise) { + return obj; + } + else if (isAnyBluebirdPromise(obj)) { + var ret = new Promise(INTERNAL); + ret._setTrace(void 0); + obj._then( + ret._fulfillUnchecked, + ret._rejectUncheckedCheckError, + ret._progressUnchecked, + ret, + null + ); + ret._setFollowing(); + return ret; + } + var then = getThen(obj); + if (then === errorObj) { + if (originalPromise !== void 0 && canAttach(then.e)) { + originalPromise._attachExtraTrace(then.e); + } + return Promise.reject(then.e); + } else if (typeof then === "function") { + return Promise$_doThenable(obj, then, originalPromise); + } + } + return obj; +} + +var hasProp = {}.hasOwnProperty; +function isAnyBluebirdPromise(obj) { + return hasProp.call(obj, "_promise0"); +} + +function Promise$_doThenable(x, then, originalPromise) { + var resolver = Promise.defer(); + var called = false; + try { + then.call( + x, + Promise$_resolveFromThenable, + Promise$_rejectFromThenable, + Promise$_progressFromThenable + ); + } catch(e) { + if (!called) { + called = true; + var trace = canAttach(e) ? e : new Error(e + ""); + if (originalPromise !== void 0) { + originalPromise._attachExtraTrace(trace); + } + resolver.promise._reject(e, trace); + } + } + return resolver.promise; + + function Promise$_resolveFromThenable(y) { + if (called) return; + called = true; + + if (x === y) { + var e = Promise._makeSelfResolutionError(); + if (originalPromise !== void 0) { + originalPromise._attachExtraTrace(e); + } + resolver.promise._reject(e, void 0); + return; + } + resolver.resolve(y); + } + + function Promise$_rejectFromThenable(r) { + if (called) return; + called = true; + var trace = canAttach(r) ? r : new Error(r + ""); + if (originalPromise !== void 0) { + originalPromise._attachExtraTrace(trace); + } + resolver.promise._reject(r, trace); + } + + function Promise$_progressFromThenable(v) { + if (called) return; + var promise = resolver.promise; + if (typeof promise._progress === "function") { + promise._progress(v); + } + } +} + +return Promise$_Cast; +}; + +},{"./errors.js":10,"./util.js":35}],33:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +var _setTimeout = function(fn, ms) { + var len = arguments.length; + var arg0 = arguments[2]; + var arg1 = arguments[3]; + var arg2 = len >= 5 ? arguments[4] : void 0; + return setTimeout(function() { + fn(arg0, arg1, arg2); + }, ms|0); +}; + +module.exports = function(Promise, INTERNAL, cast) { +var util = require("./util.js"); +var errors = require("./errors.js"); +var apiRejection = require("./errors_api_rejection")(Promise); +var TimeoutError = Promise.TimeoutError; + +var afterTimeout = function Promise$_afterTimeout(promise, message, ms) { + if (!promise.isPending()) return; + if (typeof message !== "string") { + message = "operation timed out after" + " " + ms + " ms" + } + var err = new TimeoutError(message); + errors.markAsOriginatingFromRejection(err); + promise._attachExtraTrace(err); + promise._cancel(err); +}; + +var afterDelay = function Promise$_afterDelay(value, promise) { + promise._fulfill(value); +}; + +var delay = Promise.delay = function Promise$Delay(value, ms) { + if (ms === void 0) { + ms = value; + value = void 0; + } + ms = +ms; + var maybePromise = cast(value, void 0); + var promise = new Promise(INTERNAL); + + if (maybePromise instanceof Promise) { + promise._propagateFrom(maybePromise, 7); + promise._follow(maybePromise); + return promise.then(function(value) { + return Promise.delay(value, ms); + }); + } else { + promise._setTrace(void 0); + _setTimeout(afterDelay, ms, value, promise); + } + return promise; +}; + +Promise.prototype.delay = function Promise$delay(ms) { + return delay(this, ms); +}; + +function successClear(value) { + var handle = this; + if (handle instanceof Number) handle = +handle; + clearTimeout(handle); + return value; +} + +function failureClear(reason) { + var handle = this; + if (handle instanceof Number) handle = +handle; + clearTimeout(handle); + throw reason; +} + +Promise.prototype.timeout = function Promise$timeout(ms, message) { + ms = +ms; + + var ret = new Promise(INTERNAL); + ret._propagateFrom(this, 7); + ret._follow(this); + var handle = _setTimeout(afterTimeout, ms, ret, message, ms); + return ret.cancellable() + ._then(successClear, failureClear, void 0, handle, void 0); +}; + +}; + +},{"./errors.js":10,"./errors_api_rejection":11,"./util.js":35}],34:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +module.exports = function (Promise, apiRejection, cast) { + var TypeError = require("./errors.js").TypeError; + var inherits = require("./util.js").inherits; + var PromiseInspection = Promise.PromiseInspection; + + function inspectionMapper(inspections) { + var len = inspections.length; + for (var i = 0; i < len; ++i) { + var inspection = inspections[i]; + if (inspection.isRejected()) { + return Promise.reject(inspection.error()); + } + inspections[i] = inspection.value(); + } + return inspections; + } + + function thrower(e) { + setTimeout(function(){throw e;}, 0); + } + + function castPreservingDisposable(thenable) { + var maybePromise = cast(thenable, void 0); + if (maybePromise !== thenable && + typeof thenable._isDisposable === "function" && + typeof thenable._getDisposer === "function" && + thenable._isDisposable()) { + maybePromise._setDisposable(thenable._getDisposer()); + } + return maybePromise; + } + function dispose(resources, inspection) { + var i = 0; + var len = resources.length; + var ret = Promise.defer(); + function iterator() { + if (i >= len) return ret.resolve(); + var maybePromise = castPreservingDisposable(resources[i++]); + if (maybePromise instanceof Promise && + maybePromise._isDisposable()) { + try { + maybePromise = cast(maybePromise._getDisposer() + .tryDispose(inspection), void 0); + } catch (e) { + return thrower(e); + } + if (maybePromise instanceof Promise) { + return maybePromise._then(iterator, thrower, + null, null, null); + } + } + iterator(); + } + iterator(); + return ret.promise; + } + + function disposerSuccess(value) { + var inspection = new PromiseInspection(); + inspection._settledValue = value; + inspection._bitField = 268435456; + return dispose(this, inspection).thenReturn(value); + } + + function disposerFail(reason) { + var inspection = new PromiseInspection(); + inspection._settledValue = reason; + inspection._bitField = 134217728; + return dispose(this, inspection).thenThrow(reason); + } + + function Disposer(data, promise) { + this._data = data; + this._promise = promise; + } + + Disposer.prototype.data = function Disposer$data() { + return this._data; + }; + + Disposer.prototype.promise = function Disposer$promise() { + return this._promise; + }; + + Disposer.prototype.resource = function Disposer$resource() { + if (this.promise().isFulfilled()) { + return this.promise().value(); + } + return null; + }; + + Disposer.prototype.tryDispose = function(inspection) { + var resource = this.resource(); + var ret = resource !== null + ? this.doDispose(resource, inspection) : null; + this._promise._unsetDisposable(); + this._data = this._promise = null; + return ret; + }; + + Disposer.isDisposer = function Disposer$isDisposer(d) { + return (d != null && + typeof d.resource === "function" && + typeof d.tryDispose === "function"); + }; + + function FunctionDisposer(fn, promise) { + this.constructor$(fn, promise); + } + inherits(FunctionDisposer, Disposer); + + FunctionDisposer.prototype.doDispose = function (resource, inspection) { + var fn = this.data(); + return fn.call(resource, resource, inspection); + }; + + Promise.using = function Promise$using() { + var len = arguments.length; + if (len < 2) return apiRejection( + "you must pass at least 2 arguments to Promise.using"); + var fn = arguments[len - 1]; + if (typeof fn !== "function") return apiRejection("fn must be a function"); + len--; + var resources = new Array(len); + for (var i = 0; i < len; ++i) { + var resource = arguments[i]; + if (Disposer.isDisposer(resource)) { + var disposer = resource; + resource = resource.promise(); + resource._setDisposable(disposer); + } + resources[i] = resource; + } + + return Promise.settle(resources) + .then(inspectionMapper) + .spread(fn) + ._then(disposerSuccess, disposerFail, void 0, resources, void 0); + }; + + Promise.prototype._setDisposable = + function Promise$_setDisposable(disposer) { + this._bitField = this._bitField | 262144; + this._disposer = disposer; + }; + + Promise.prototype._isDisposable = function Promise$_isDisposable() { + return (this._bitField & 262144) > 0; + }; + + Promise.prototype._getDisposer = function Promise$_getDisposer() { + return this._disposer; + }; + + Promise.prototype._unsetDisposable = function Promise$_unsetDisposable() { + this._bitField = this._bitField & (~262144); + this._disposer = void 0; + }; + + Promise.prototype.disposer = function Promise$disposer(fn) { + if (typeof fn === "function") { + return new FunctionDisposer(fn, this); + } + throw new TypeError(); + }; + +}; + +},{"./errors.js":10,"./util.js":35}],35:[function(require,module,exports){ +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Petka Antonov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions:

+ * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +"use strict"; +var es5 = require("./es5.js"); +var haveGetters = (function(){ + try { + var o = {}; + es5.defineProperty(o, "f", { + get: function () { + return 3; + } + }); + return o.f === 3; + } + catch (e) { + return false; + } + +})(); +var canEvaluate = typeof navigator == "undefined"; +var errorObj = {e: {}}; +function tryCatch1(fn, receiver, arg) { + try { return fn.call(receiver, arg); } + catch (e) { + errorObj.e = e; + return errorObj; + } +} + +function tryCatch2(fn, receiver, arg, arg2) { + try { return fn.call(receiver, arg, arg2); } + catch (e) { + errorObj.e = e; + return errorObj; + } +} + +function tryCatch3(fn, receiver, arg, arg2, arg3) { + try { return fn.call(receiver, arg, arg2, arg3); } + catch (e) { + errorObj.e = e; + return errorObj; + } +} + +function tryCatch4(fn, receiver, arg, arg2, arg3, arg4) { + try { return fn.call(receiver, arg, arg2, arg3, arg4); } + catch (e) { + errorObj.e = e; + return errorObj; + } +} + +function tryCatchApply(fn, args, receiver) { + try { return fn.apply(receiver, args); } + catch (e) { + errorObj.e = e; + return errorObj; + } +} + +var inherits = function(Child, Parent) { + var hasProp = {}.hasOwnProperty; + + function T() { + this.constructor = Child; + this.constructor$ = Parent; + for (var propertyName in Parent.prototype) { + if (hasProp.call(Parent.prototype, propertyName) && + propertyName.charAt(propertyName.length-1) !== "$" + ) { + this[propertyName + "$"] = Parent.prototype[propertyName]; + } + } + } + T.prototype = Parent.prototype; + Child.prototype = new T(); + return Child.prototype; +}; + +function asString(val) { + return typeof val === "string" ? val : ("" + val); +} + +function isPrimitive(val) { + return val == null || val === true || val === false || + typeof val === "string" || typeof val === "number"; + +} + +function isObject(value) { + return !isPrimitive(value); +} + +function maybeWrapAsError(maybeError) { + if (!isPrimitive(maybeError)) return maybeError; + + return new Error(asString(maybeError)); +} + +function withAppended(target, appendee) { + var len = target.length; + var ret = new Array(len + 1); + var i; + for (i = 0; i < len; ++i) { + ret[i] = target[i]; + } + ret[i] = appendee; + return ret; +} + +function getDataPropertyOrDefault(obj, key, defaultValue) { + if (es5.isES5) { + var desc = Object.getOwnPropertyDescriptor(obj, key); + if (desc != null) { + return desc.get == null && desc.set == null + ? desc.value + : defaultValue; + } + } else { + return {}.hasOwnProperty.call(obj, key) ? obj[key] : void 0; + } +} + +function notEnumerableProp(obj, name, value) { + if (isPrimitive(obj)) return obj; + var descriptor = { + value: value, + configurable: true, + enumerable: false, + writable: true + }; + es5.defineProperty(obj, name, descriptor); + return obj; +} + + +var wrapsPrimitiveReceiver = (function() { + return this !== "string"; +}).call("string"); + +function thrower(r) { + throw r; +} + +var inheritedDataKeys = (function() { + if (es5.isES5) { + return function(obj, opts) { + var ret = []; + var visitedKeys = Object.create(null); + var getKeys = Object(opts).includeHidden + ? Object.getOwnPropertyNames + : Object.keys; + while (obj != null) { + var keys; + try { + keys = getKeys(obj); + } catch (e) { + return ret; + } + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + if (visitedKeys[key]) continue; + visitedKeys[key] = true; + var desc = Object.getOwnPropertyDescriptor(obj, key); + if (desc != null && desc.get == null && desc.set == null) { + ret.push(key); + } + } + obj = es5.getPrototypeOf(obj); + } + return ret; + }; + } else { + return function(obj) { + var ret = []; + /*jshint forin:false */ + for (var key in obj) { + ret.push(key); + } + return ret; + }; + } + +})(); + +function isClass(fn) { + try { + if (typeof fn === "function") { + var keys = es5.keys(fn.prototype); + return keys.length > 0 && + !(keys.length === 1 && keys[0] === "constructor"); + } + return false; + } catch (e) { + return false; + } +} + +function toFastProperties(obj) { + /*jshint -W027*/ + function f() {} + f.prototype = obj; + return f; +} + +var rident = /^[a-z$_][a-z$_0-9]*$/i; +function isIdentifier(str) { + return rident.test(str); +} + +function filledRange(count, prefix, suffix) { + var ret = new Array(count); + for(var i = 0; i < count; ++i) { + ret[i] = prefix + i + suffix; + } + return ret; +} + +var ret = { + isClass: isClass, + isIdentifier: isIdentifier, + inheritedDataKeys: inheritedDataKeys, + getDataPropertyOrDefault: getDataPropertyOrDefault, + thrower: thrower, + isArray: es5.isArray, + haveGetters: haveGetters, + notEnumerableProp: notEnumerableProp, + isPrimitive: isPrimitive, + isObject: isObject, + canEvaluate: canEvaluate, + errorObj: errorObj, + tryCatch1: tryCatch1, + tryCatch2: tryCatch2, + tryCatch3: tryCatch3, + tryCatch4: tryCatch4, + tryCatchApply: tryCatchApply, + inherits: inherits, + withAppended: withAppended, + asString: asString, + maybeWrapAsError: maybeWrapAsError, + wrapsPrimitiveReceiver: wrapsPrimitiveReceiver, + toFastProperties: toFastProperties, + filledRange: filledRange +}; + +module.exports = ret; + +},{"./es5.js":12}]},{},[3]) +(3) +}); +; ;if (typeof window !== 'undefined' && window !== null) { window.P = window.Promise; } else if (typeof self !== 'undefined' && self !== null) { self.P = self.Promise; } \ No newline at end of file From 18ee5254f9bf0e8d6d04d6d25a24c6e5c8810fff Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Thu, 2 Apr 2020 13:56:25 +0200 Subject: [PATCH 124/130] Make Geo service calls more resilient in /shops page The Geo service is used heavily in the /shops page and especially in the search function. If the google maps js library has failed to load it was throwing a lot of fatal errors, so this change ensures the /shops page can at least: a) load, b) show some shops, and c) search for shops by name (but not location) --- app/assets/javascripts/darkswarm/services/enterprises.js.coffee | 2 +- app/assets/javascripts/darkswarm/services/geo.js.erb.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/darkswarm/services/enterprises.js.coffee b/app/assets/javascripts/darkswarm/services/enterprises.js.coffee index d3b7deec3e..b7b43ca040 100644 --- a/app/assets/javascripts/darkswarm/services/enterprises.js.coffee +++ b/app/assets/javascripts/darkswarm/services/enterprises.js.coffee @@ -50,7 +50,7 @@ Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, false calculateDistance: (query, firstMatching) -> - if query?.length > 0 + if query?.length > 0 and Geo.OK if firstMatching? @setDistanceFrom firstMatching else diff --git a/app/assets/javascripts/darkswarm/services/geo.js.erb.coffee b/app/assets/javascripts/darkswarm/services/geo.js.erb.coffee index 2f96722b08..866dc2f742 100644 --- a/app/assets/javascripts/darkswarm/services/geo.js.erb.coffee +++ b/app/assets/javascripts/darkswarm/services/geo.js.erb.coffee @@ -1,6 +1,6 @@ Darkswarm.service "Geo", -> new class Geo - OK: google.maps.GeocoderStatus.OK + OK: google?.maps?.GeocoderStatus?.OK # Usage: # Geo.geocode address, (results, status) -> From 838ffdbf009de027ab62db57a7114e17b60a8470 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Thu, 2 Apr 2020 17:33:10 +0200 Subject: [PATCH 125/130] Add code comment for dependency --- app/assets/javascripts/darkswarm/all.js.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/javascripts/darkswarm/all.js.coffee b/app/assets/javascripts/darkswarm/all.js.coffee index 566ba5d996..aa0fafa058 100644 --- a/app/assets/javascripts/darkswarm/all.js.coffee +++ b/app/assets/javascripts/darkswarm/all.js.coffee @@ -9,6 +9,7 @@ #= require angular-animate #= require angular-resource #= require lodash.underscore.js +# bluebird.js is a dependency of angular-google-maps.js 2.0.0 #= require bluebird.js #= require angular-scroll.min.js #= require angular-google-maps.min.js From adb61e48c5878833ff89c67054cea5e5d2a585b0 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Thu, 2 Apr 2020 19:09:03 +0200 Subject: [PATCH 126/130] Cache counts used in homepage for 24 hours --- app/controllers/home_controller.rb | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index f8268cf464..0a39b00d9b 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -5,12 +5,23 @@ class HomeController < BaseController def index if ContentConfig.home_show_stats - @num_distributors = Enterprise.is_distributor.activated.visible.count - @num_producers = Enterprise.is_primary_producer.activated.visible.count - @num_users = Spree::Order.complete.count('DISTINCT user_id') - @num_orders = Spree::Order.complete.count + @num_distributors = cached_count('distributors', Enterprise.is_distributor.activated.visible) + @num_producers = cached_count('producers', Enterprise.is_primary_producer.activated.visible) + @num_orders = cached_count('orders', Spree::Order.complete) + @num_users = cached_count( + 'users', Spree::Order.complete.select('DISTINCT spree_orders.user_id') + ) end end def sell; end + + private + + # Cache the value of the query count for 24 hours + def cached_count(key, query) + Rails.cache.fetch("home_stats_count_#{key}", expires_in: 1.day, race_condition_ttl: 10) do + query.count + end + end end From 72b47fbceb81fe1af34af567fb9c631744c88e10 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Fri, 3 Apr 2020 19:22:55 +1100 Subject: [PATCH 127/130] Updating translations for config/locales/de_DE.yml --- config/locales/de_DE.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/config/locales/de_DE.yml b/config/locales/de_DE.yml index 6d4705dfdb..749a5f4b0d 100644 --- a/config/locales/de_DE.yml +++ b/config/locales/de_DE.yml @@ -19,6 +19,8 @@ de_DE: shipping_category_id: "Versandkategorie" variant_unit: "Varianteneinheit" variant_unit_name: "Name der Varianteneinheit" + spree/credit_card: + base: "Kreditkarte" order_cycle: orders_close_at: Schlussdatum errors: @@ -29,6 +31,10 @@ de_DE: taken: "Es gibt bereits ein Konto für diese E-Mail-Adresse. Bitte versuchen Sie sich einzuloggen oder setzen Sie Ihr Passwort zurück." spree/order: no_card: Es sind keine belastbaren Karten verfügbar. + spree/credit_card: + attributes: + base: + card_expired: "abgelaufen" order_cycle: attributes: orders_close_at: @@ -36,7 +42,7 @@ de_DE: variant_override: count_on_hand: using_producer_stock_settings_but_count_on_hand_set: "muss leer sein, da die Einstellungen des Produzentenbestands verwendet werden" - on_demand_but_count_on_hand_set: "muss bei Bedarf leer sein" + on_demand_but_count_on_hand_set: "muss leer sein falls Produktion auf Nachfrage" limited_stock_but_no_count_on_hand: "muss angegeben werden, da nur begrenzte Lagerbestände erforderlich sind" activemodel: attributes: @@ -46,7 +52,7 @@ de_DE: distributor_ids: "Hubs" producer_ids: "Erzeuger" order_cycle_ids: "Bestellrunden" - enterprise_fee_ids: "Gebühren Namen" + enterprise_fee_ids: "Gebührennamen" shipping_method_ids: "Lieferart" payment_method_ids: "Zahlungsarten" errors: From d06b7b86063ccfb46a9b39fee69495457eb0ab59 Mon Sep 17 00:00:00 2001 From: Transifex-Openfoodnetwork Date: Fri, 3 Apr 2020 19:26:04 +1100 Subject: [PATCH 128/130] Updating translations for config/locales/de_DE.yml --- config/locales/de_DE.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/de_DE.yml b/config/locales/de_DE.yml index 749a5f4b0d..82bc742e43 100644 --- a/config/locales/de_DE.yml +++ b/config/locales/de_DE.yml @@ -1145,7 +1145,7 @@ de_DE: checkout: "Zur Kasse" already_ordered_products: "Bereits in diesem Bestellzyklus bestellt" register_call: - selling_on_ofn: "Interesse am Open Food Network?" + selling_on_ofn: "Sie möchten selbst im Open Food Network verkaufen?" register: "Hier anmelden" footer: footer_secure: "Sicher und vertrauenswürdig." From 7858a26e5ef73ea4be9f8b90e1bcb32b5e8dbf89 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Fri, 3 Apr 2020 12:03:40 +0100 Subject: [PATCH 129/130] Update all locales with the latest Transifex translations --- .../admin/variant_overrides_controller.rb | 1 + config/locales/ar.yml | 7 ++ config/locales/ca.yml | 2 + config/locales/de_DE.yml | 8 +- config/locales/en_AU.yml | 17 ++++ config/locales/en_BE.yml | 7 ++ config/locales/en_CA.yml | 7 ++ config/locales/en_DE.yml | 7 ++ config/locales/en_GB.yml | 2 + config/locales/en_NZ.yml | 7 ++ config/locales/en_US.yml | 82 +++++++++++++++++-- config/locales/en_ZA.yml | 7 ++ config/locales/es.yml | 7 ++ config/locales/fr_BE.yml | 9 +- config/locales/fr_CA.yml | 7 ++ config/locales/it.yml | 7 ++ config/locales/nb.yml | 7 ++ config/locales/nl_BE.yml | 9 +- config/locales/pt.yml | 7 ++ config/locales/pt_BR.yml | 2 +- config/locales/sv.yml | 5 ++ config/locales/tr.yml | 7 ++ 22 files changed, 209 insertions(+), 12 deletions(-) diff --git a/app/controllers/admin/variant_overrides_controller.rb b/app/controllers/admin/variant_overrides_controller.rb index e12b43825b..9fe5c9fc0b 100644 --- a/app/controllers/admin/variant_overrides_controller.rb +++ b/app/controllers/admin/variant_overrides_controller.rb @@ -12,6 +12,7 @@ module Admin def index; end def bulk_update + byebug # Ensure we're authorised to update all variant overrides @vo_set.collection.each { |vo| authorize! :update, vo } diff --git a/config/locales/ar.yml b/config/locales/ar.yml index 56fac2a708..b7c5a59b6c 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -19,6 +19,8 @@ ar: shipping_category_id: "نوع الشحن" variant_unit: "وحدة النوع" variant_unit_name: "اسم وحدة النوع" + spree/credit_card: + base: "بطاقة ائتمان" order_cycle: orders_close_at: تاريخ الاغلاق errors: @@ -860,6 +862,11 @@ ar: cancel: "إلغاء" back_to_list: "العودة للقائمة" outgoing: + outgoing: "الصادر" + distributor: "الموزع" + products: "منتجات" + tags: "الاوسمة" + fees: "رسوم" previous: "السابق" save: "حفظ" save_and_back_to_list: "حفظ والعودة إلى القائمة" diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 737c9ba7dc..35a2a9657a 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -19,6 +19,8 @@ ca: shipping_category_id: "Categoria d'enviament" variant_unit: "Unitat de la variant" variant_unit_name: "Nom de la unitat de la variant" + spree/credit_card: + base: "Targeta de crèdit" order_cycle: orders_close_at: Data de tancament errors: diff --git a/config/locales/de_DE.yml b/config/locales/de_DE.yml index 82bc742e43..bfdacc350e 100644 --- a/config/locales/de_DE.yml +++ b/config/locales/de_DE.yml @@ -1784,13 +1784,13 @@ de_DE: address1_field_placeholder: "z.B. 123 Cranberry-Laufwerk" address1_field_error: "Bitte geben Sie eine Adresse an" address2_field: "Anschrift Zeile 2:" - suburb_field: "Vorort:" + suburb_field: "Ort:" suburb_field_placeholder: "z.B. Northcote" suburb_field_error: "Bitte geben Sie einen Vorort ein" postcode_field: "Postleitzahl:" postcode_field_placeholder: "z.B. 3070" postcode_field_error: "Postleitzahl erforderlich" - state_field: "Zustand:" + state_field: "Bundesland:" state_field_error: "Staat erforderlich" country_field: "Land:" country_field_error: "Bitte wähle ein Land" @@ -1809,8 +1809,8 @@ de_DE: yes_producer: "Ja, ich bin ein Produzent." no_producer: "Nein, ich bin kein Produzent" producer_field_error: "Bitte wählen Sie: Sind Sie ein Produzent?" - yes_producer_help: "Hersteller machen leckere Dinge zu essen und / oder zu trinken. Du bist ein Produzent, wenn du ihn anbaust, erziehst ihn, braue ihn, backe ihn, gähre ihn, melke ihn oder forme ihn." - no_producer_help: "Wenn Sie kein Produzent sind, sind Sie wahrscheinlich jemand, der Lebensmittel verkauft und verteilt. Sie könnten ein Hub, Coop, Einkaufsgruppe, Einzelhändler, Großhändler oder andere sein." + yes_producer_help: "Die Produzenten machen leckere Sachen zum Essen und / oder Trinken. Sie sind ein Produzent, wenn Sie anbauen, brauen, backen, fermentieren, melken oder sonst wie Lebenmittel produzieren." + no_producer_help: "Wenn Sie kein Produzent sind, sind Sie wahrscheinlich jemand, der Lebensmittel verkauft und verteilt. Sie könnten ein Foodhub, eine Coop, eine Einkaufsgruppe, Einzelhändler, ein Hofladen, Großhändler oder vergleichbares sein." create_profile: "Profil erstellen" about: title: "Über Uns" diff --git a/config/locales/en_AU.yml b/config/locales/en_AU.yml index 741661f508..ecc81b40ce 100644 --- a/config/locales/en_AU.yml +++ b/config/locales/en_AU.yml @@ -19,6 +19,8 @@ en_AU: shipping_category_id: "Shipping Category" variant_unit: "Variant Unit" variant_unit_name: "Variant Unit Name" + spree/credit_card: + base: "Credit Card" order_cycle: orders_close_at: Close date errors: @@ -699,6 +701,16 @@ en_AU: enable_subscriptions_false: "Disabled" enable_subscriptions_true: "Enabled" shopfront_message: "\"Home\" message" + shopfront_message_placeholder: > + Create your home page content to welcome customers and explain how people + can shop with you. + + Include details about your delivery and pick up options, how often you + open the shop for orders, and all the details your customers will need + to understand the process of buying from you. + + You can also include links to your newsletter sign up, so that people + can connect with you to hear when your next order cycle opens. shopfront_message_link_tooltip: "Insert / edit link" shopfront_message_link_prompt: "Please enter a URL to insert" shopfront_closed_message: "Shopfront Closed Message" @@ -860,6 +872,11 @@ en_AU: cancel: "Cancel" back_to_list: "Back To List" outgoing: + outgoing: "Outgoing" + distributor: "Distributor" + products: "Products" + tags: "Tags" + fees: "Fees" previous: "Previous" save: "Save" save_and_back_to_list: "Save and Back to List" diff --git a/config/locales/en_BE.yml b/config/locales/en_BE.yml index 51cf1d2479..7f6c05a7c3 100644 --- a/config/locales/en_BE.yml +++ b/config/locales/en_BE.yml @@ -19,6 +19,8 @@ en_BE: shipping_category_id: "Shipping Category" variant_unit: "Variant Unit" variant_unit_name: "Variant Unit Name" + spree/credit_card: + base: "Credit Card" order_cycle: orders_close_at: Close date errors: @@ -836,6 +838,11 @@ en_BE: next: "Next" cancel: "Cancel" outgoing: + outgoing: "Outgoing" + distributor: "Distributor" + products: "Products" + tags: "Tags" + fees: "Fees" previous: "Previous" save: "Save" cancel: "Cancel" diff --git a/config/locales/en_CA.yml b/config/locales/en_CA.yml index cb02b83181..193b756e57 100644 --- a/config/locales/en_CA.yml +++ b/config/locales/en_CA.yml @@ -19,6 +19,8 @@ en_CA: shipping_category_id: "Shipping Category" variant_unit: "Variant Unit" variant_unit_name: "Variant Unit Name" + spree/credit_card: + base: "Credit Card" order_cycle: orders_close_at: Close date errors: @@ -861,6 +863,11 @@ en_CA: cancel: "Cancel" back_to_list: "Back to List" outgoing: + outgoing: "Outgoing" + distributor: "Distributor" + products: "Products" + tags: "Tags" + fees: "Fees" previous: "Previous" save: "Save" save_and_back_to_list: "Save and Back to List" diff --git a/config/locales/en_DE.yml b/config/locales/en_DE.yml index 462ab3502f..8c81c87986 100644 --- a/config/locales/en_DE.yml +++ b/config/locales/en_DE.yml @@ -19,6 +19,8 @@ en_DE: shipping_category_id: "Shipping Category" variant_unit: "Variant Unit" variant_unit_name: "Variant Unit Name" + spree/credit_card: + base: "Credit Card" order_cycle: orders_close_at: Close date errors: @@ -844,6 +846,11 @@ en_DE: next: "Next" cancel: "Cancel" outgoing: + outgoing: "Outgoing" + distributor: "Distributor" + products: "Products" + tags: "Tags" + fees: "Fees" previous: "Previous" save: "Save" cancel: "Cancel" diff --git a/config/locales/en_GB.yml b/config/locales/en_GB.yml index 78ab3801e2..dfa17eb46e 100644 --- a/config/locales/en_GB.yml +++ b/config/locales/en_GB.yml @@ -19,6 +19,8 @@ en_GB: shipping_category_id: "Shipping Category" variant_unit: "Variant Unit" variant_unit_name: "Variant Unit Name" + spree/credit_card: + base: "Credit Card" order_cycle: orders_close_at: Close date errors: diff --git a/config/locales/en_NZ.yml b/config/locales/en_NZ.yml index 8299a63b7d..15ad362a51 100644 --- a/config/locales/en_NZ.yml +++ b/config/locales/en_NZ.yml @@ -19,6 +19,8 @@ en_NZ: shipping_category_id: "Shipping Category" variant_unit: "Variant Unit" variant_unit_name: "Variant Unit Name" + spree/credit_card: + base: "Credit Card" order_cycle: orders_close_at: Close date errors: @@ -861,6 +863,11 @@ en_NZ: cancel: "Cancel" back_to_list: "Back To List" outgoing: + outgoing: "Outgoing" + distributor: "Distributor" + products: "Products" + tags: "Tags" + fees: "Fees" previous: "Previous" save: "Save" save_and_back_to_list: "Save and Back to List" diff --git a/config/locales/en_US.yml b/config/locales/en_US.yml index 1cb6cfdcb4..1e04a29f90 100644 --- a/config/locales/en_US.yml +++ b/config/locales/en_US.yml @@ -19,6 +19,8 @@ en_US: shipping_category_id: "Shipping Category" variant_unit: "Variant Unit" variant_unit_name: "Variant Unit Name" + spree/credit_card: + base: "Credit Card" order_cycle: orders_close_at: Close Date errors: @@ -608,7 +610,7 @@ en_US: desc_long: About Us desc_long_placeholder: Tell customers about yourself. This information appears on your public profile. business_details: - abn: Tax ID Number or EIN (optional) + abn: Tax ID, DUNS Number, or other business ID abn_placeholder: eg. 123456789 acn: Legal Business Name acn_placeholder: eg. Martin's Produce LLC @@ -861,6 +863,12 @@ en_US: cancel: "Cancel" back_to_list: "Back To List" outgoing: + outgoing: "Outgoing" + distributor: "Distributor" + products: "Products" + tags: "Tags" + delivery_details: "Delivery Details" + fees: "Fees" previous: "Previous" save: "Save" save_and_back_to_list: "Save and Back to List" @@ -1172,11 +1180,11 @@ en_US: invoice_column_price_without_taxes: "Total price (Excl. tax)" invoice_column_tax_rate: "Tax rate" invoice_tax_total: "Tax total:" - tax_invoice: "TAX INVOICE" + tax_invoice: "INVOICE" tax_total: "Total tax (%{rate}):" total_excl_tax: "Total (Excl. tax):" total_incl_tax: "Total (Incl. tax):" - abn: "Tax ID Number or EIN (optional)" + abn: "Tax ID, DUNS Number, or other business ID" acn: "Legal Business Name" invoice_issued_on: "Invoice issued on:" order_number: "Invoice number:" @@ -1809,7 +1817,7 @@ en_US: enterprise_long_desc: "Long Description" enterprise_long_desc_placeholder: "This is your opportunity to tell the story of your enterprise - what makes you different and wonderful? We'd suggest keeping your description to under 600 characters or 150 words." enterprise_long_desc_length: "%{num} characters / up to 600 recommended" - enterprise_abn: "Tax ID Number or EIN (optional)" + enterprise_abn: "Tax ID, DUNS Number, or other business ID" enterprise_abn_placeholder: "eg. 123456789" enterprise_acn: "Legal Business Name" enterprise_acn_placeholder: "eg. Justins Produce LLC" @@ -2888,6 +2896,12 @@ en_US: minimal_amount: "Minimal Amount" normal_amount: "Normal Amount" discount_amount: "Discount Amount" + no_images_found: "No Images Found" + new_image: "New Image" + filename: "Filename" + alt_text: "Alternative Text" + thumbnail: "Thumbnail" + back_to_images_list: "Back To Images List" email: Email account_updated: "Account updated!" email_updated: "The account will be updated once the new email is confirmed." @@ -2899,6 +2913,9 @@ en_US: zipcode: Zipcode weight: Weight (per lb) error_user_destroy_with_orders: "Users with completed orders may not be deleted" + cannot_create_payment_without_payment_methods: "You cannot create a payment for an order without any payment methods defined." + please_define_payment_methods: "Please define some payment methods first." + options: "Options" actions: update: "Update" errors: @@ -2930,27 +2947,53 @@ en_US: product_properties: index: inherits_properties_checkbox_hint: "Inherit properties from %{supplier}? (unless overridden above)" + add_product_properties: "Add Product Properties" + select_from_prototype: "Select From Prototype" properties: index: properties: "Properties" + new_property: "New Property" name: "Name" + presentation: "Presentation" + new: + new_property: "New Property" + edit: + editing_property: "Editing Property" + back_to_properties_list: "Back To Properties List" form: name: "Name" + presentation: "Presentation" return_authorizations: index: + new_return_authorization: "New Return Authorization" return_authorizations: "Return Authorizations" back_to_orders_list: "Back To Orders List" + rma_number: "RMA Number" status: "Status" amount: "Amount" cannot_create_returns: "Cannot create returns as this order has no shipped units." continue: "Continue" new: + new_return_authorization: "New Return Authorization" + back_to_return_authorizations_list: "Back To Return Authorization List" continue: "Continue" edit: + receive: "receive" are_you_sure: "Are you sure?" + return_authorization: "Return Authorization" form: product: "Product" + quantity_shipped: "Quantity Shipped" + quantity_returned: "Quantity Returned" + return_quantity: "Return Quantity" amount: "Amount" + rma_value: "RMA Value" + reason: "Reason" + stock_location: "Stock Location" + states: + authorized: "Authorized" + received: "Received" + canceled: "Canceled" orders: index: listing_orders: "Listing Orders" @@ -2958,6 +3001,7 @@ en_US: capture: "Capture" ship: "Ship" edit: "Edit" + order_not_updated: "The order could not be updated" note: "Note" first: "First" last: "Last" @@ -2977,9 +3021,11 @@ en_US: email: "Customer E-mail" invoice: issued_on: "Issued on" - tax_invoice: "TAX INVOICE" + tax_invoice: "INVOICE" code: "Code" from: "From" + to: "Bill to" + shipping: "Shipping" form: distribution_fields: title: "Distribution" @@ -3140,12 +3186,22 @@ en_US: index: sku: "SKU" price: "Price" + options: "Options" no_results: "No results" + to_add_variants_you_must_first_define: "To add variants, you must first define" option_types: "Option Types" + option_values: "Option Values" + and: "and" + new_variant: "New Variant" + show_active: "Show Active" + show_deleted: "Show Deleted" + new: + new_variant: "New Variant" form: sku: "SKU" price: "Price" display_as: "Display As" + display_name: "Display Name" autocomplete: producer_name: "Producer" unit: "Unit" @@ -3291,3 +3347,19 @@ en_US: allow_charges?: "Allow Charges?" localized_number: invalid_format: has an invalid format. Please enter a number. + api: + invalid_api_key: "Invalid API key (%{key}) specified." + unauthorized: "You are not authorized to perform that action." + invalid_resource: "Invalid resource. Please fix errors and try again." + resource_not_found: "The resource you were looking for could not be found." + access: "API Access" + key: "Key" + clear_key: "Clear key" + regenerate_key: "Regenerate Key" + no_key: "No key" + generate_key: "Generate API key" + key_generated: "Key generated" + key_cleared: "Key cleared" + shipment: + cannot_ready: "Cannot ready shipment." + invalid_taxonomy_id: "Invalid taxonomy id." diff --git a/config/locales/en_ZA.yml b/config/locales/en_ZA.yml index 95a6626d62..71e4279ab3 100644 --- a/config/locales/en_ZA.yml +++ b/config/locales/en_ZA.yml @@ -19,6 +19,8 @@ en_ZA: shipping_category_id: "Shipping Category" variant_unit: "Variant Unit" variant_unit_name: "Variant Unit Name" + spree/credit_card: + base: "Credit Card" order_cycle: orders_close_at: Close date errors: @@ -840,6 +842,11 @@ en_ZA: next: "Next" cancel: "Cancel" outgoing: + outgoing: "Outgoing" + distributor: "Distributor" + products: "Products" + tags: "Tags" + fees: "Fees" previous: "Previous" save: "Save" cancel: "Cancel" diff --git a/config/locales/es.yml b/config/locales/es.yml index d7cfd0eef1..ab12b1328f 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -19,6 +19,8 @@ es: shipping_category_id: "Categoría de envío" variant_unit: "Unidad Variante" variant_unit_name: "Nombre de la unidad de la variante" + spree/credit_card: + base: "Tarjeta de crédito" order_cycle: orders_close_at: Fecha de cierre errors: @@ -845,6 +847,11 @@ es: next: "Siguiente" cancel: "Cancelar" outgoing: + outgoing: "Saliente" + distributor: "Distribuidora" + products: "Productos" + tags: "Tags" + fees: "Comisiones" previous: "Anterior" save: "Guardar" cancel: "Cancelar" diff --git a/config/locales/fr_BE.yml b/config/locales/fr_BE.yml index cebb76a300..8ca1872694 100644 --- a/config/locales/fr_BE.yml +++ b/config/locales/fr_BE.yml @@ -19,6 +19,8 @@ fr_BE: shipping_category_id: "Catégorie de livraison" variant_unit: "Unité de variante" variant_unit_name: "Nom de la variante" + spree/credit_card: + base: "Carte de crédit" order_cycle: orders_close_at: Date de fermeture errors: @@ -861,6 +863,11 @@ fr_BE: cancel: "Annuler" back_to_list: "Retour à la liste." outgoing: + outgoing: "Produits sortants (mis en vente par/via un ou plusieurs comptoir·s)" + distributor: "Distributeur·trice" + products: "Produits" + tags: "Tags" + fees: "Commission" previous: "Précédent" save: "Sauvergarder" save_and_back_to_list: "Sauver et retour à la liste" @@ -1131,7 +1138,7 @@ fr_BE: checkout: "Passer la commande" already_ordered_products: "Déjà commandé dans ce cycle de vente" register_call: - selling_on_ofn: "Intéressé à participer à Open Food Network?" + selling_on_ofn: "Intéressé·e à vendre sur Open Food Network?" register: "Démarrez ici" footer: footer_secure: "Fiable et sécurisé." diff --git a/config/locales/fr_CA.yml b/config/locales/fr_CA.yml index c9c76894c2..cf300ce168 100644 --- a/config/locales/fr_CA.yml +++ b/config/locales/fr_CA.yml @@ -19,6 +19,8 @@ fr_CA: shipping_category_id: "Condition de transport" variant_unit: "Unité" variant_unit_name: "Unité de la variante" + spree/credit_card: + base: "Carte de crédit" order_cycle: orders_close_at: Date de fermeture errors: @@ -863,6 +865,11 @@ fr_CA: cancel: "Annuler" back_to_list: "Retour à la liste" outgoing: + outgoing: "Produits sortants (mis en vente par/via un ou plusieurs hubs)" + distributor: "Hub-distributeur" + products: "Produits" + tags: "Tags" + fees: "Commissions" previous: "Précédent" save: "Enregistrer" save_and_back_to_list: "Sauvegarder et suivant" diff --git a/config/locales/it.yml b/config/locales/it.yml index a0f7731518..67f67b0a28 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -19,6 +19,8 @@ it: shipping_category_id: "Categoria di spedizione" variant_unit: "Unità Variante" variant_unit_name: "Nome Unità Variante" + spree/credit_card: + base: "Carta di Credito" order_cycle: orders_close_at: Data chiusura errors: @@ -862,6 +864,11 @@ it: cancel: "Annulla" back_to_list: "Indietro alla lista" outgoing: + outgoing: "In uscita" + distributor: "Distributore" + products: "Prodotti" + tags: "Tag" + fees: "Tariffe" previous: "Precedente" save: "Salva" save_and_back_to_list: "Salva e torna alla lista" diff --git a/config/locales/nb.yml b/config/locales/nb.yml index e7d33d52a0..471207ccca 100644 --- a/config/locales/nb.yml +++ b/config/locales/nb.yml @@ -19,6 +19,8 @@ nb: shipping_category_id: "Leveringskategori" variant_unit: "Variant Enhet" variant_unit_name: "Enhetsnavn Variant" + spree/credit_card: + base: "Kredittkort" order_cycle: orders_close_at: Lukkedato errors: @@ -861,6 +863,11 @@ nb: cancel: "Avbryt" back_to_list: "Tilbake til Listen" outgoing: + outgoing: "Utgående" + distributor: "Distributør" + products: "Produkter" + tags: "Merkelapper" + fees: "Avgifter" previous: "Tidligere" save: "Lagre" save_and_back_to_list: "Lagre og Tilbake til Listen" diff --git a/config/locales/nl_BE.yml b/config/locales/nl_BE.yml index 1ea003122e..d6d40e6fdc 100644 --- a/config/locales/nl_BE.yml +++ b/config/locales/nl_BE.yml @@ -3,7 +3,7 @@ nl_BE: activerecord: attributes: enterprise_fee: - fee_type: Sprookjesachtig type + fee_type: Soort commissie spree/order: payment_state: Status Betaling shipment_state: Status Verzending @@ -19,6 +19,8 @@ nl_BE: shipping_category_id: "Verzendcategorie" variant_unit: "Eénheid" variant_unit_name: "Variant Unit Name" + spree/credit_card: + base: "Kredietkaart" order_cycle: orders_close_at: Sluitingsdatum errors: @@ -839,6 +841,11 @@ nl_BE: next: "Volgende" cancel: "Annuleren" outgoing: + outgoing: "Uitgaande" + distributor: "Distributeur" + products: "Producten" + tags: "Tags" + fees: "Vergoedingen" previous: "Voorgaande" save: "Save" cancel: "Annuleren" diff --git a/config/locales/pt.yml b/config/locales/pt.yml index b9035d7f10..b5444331ec 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -17,6 +17,8 @@ pt: primary_taxon: "Categoria de Produto" supplier: "Fornecedor" variant_unit: "Unidade da Variante" + spree/credit_card: + base: "Cartão de Crédito" order_cycle: orders_close_at: Data de fecho errors: @@ -826,6 +828,11 @@ pt: next: "Seguinte" cancel: "Cancelar" outgoing: + outgoing: "Saída" + distributor: "Distribuidor" + products: "Produtos" + tags: "Etiquetas" + fees: "Taxas" previous: "Anterior" save: "Guardar" cancel: "Cancelar" diff --git a/config/locales/pt_BR.yml b/config/locales/pt_BR.yml index 87be993fe0..d5dbc46d8d 100644 --- a/config/locales/pt_BR.yml +++ b/config/locales/pt_BR.yml @@ -1575,7 +1575,7 @@ pt_BR: groups_signup_motivation9: 'Se você precisa de recursos, nós te conectamos a uma rede global de parceiros. ' groups_signup_pricing: Conta de Grupos groups_signup_studies: Estudos de Caso - groups_signup_contact: Pronto para discutir? + groups_signup_contact: Pronto para conversar? groups_signup_contact_text: "Entre em contato para descobrir o que a OFB pode fazer por você" groups_signup_detail: "Aqui está o detalhe. " login_invalid: "E-mail ou senha inválidos" diff --git a/config/locales/sv.yml b/config/locales/sv.yml index ae32c53f2e..ca90a48659 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -495,6 +495,11 @@ sv: next: "Näst" cancel: "Avbryt" outgoing: + outgoing: "Utgående" + distributor: "Distributör" + products: "Produkter" + tags: "Taggar" + fees: "Avgifter" cancel: "Avbryt" exchange_form: pickup_instructions_placeholder: "Instruktioner för upphämtning" diff --git a/config/locales/tr.yml b/config/locales/tr.yml index 8260ea4341..b36a8da76b 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -19,6 +19,8 @@ tr: shipping_category_id: "Nakliye Kategorisi" variant_unit: "Varyant Birimi" variant_unit_name: "Varyant Birim Adı" + spree/credit_card: + base: "Kredi kartı" order_cycle: orders_close_at: Bitiş tarihi errors: @@ -858,6 +860,11 @@ tr: cancel: "İptal et" back_to_list: "Listeye geri dön" outgoing: + outgoing: "Giden" + distributor: "Dağıtımcı" + products: "Ürünler" + tags: "Etiketler" + fees: "Ücretler" previous: "Önceki" save: "Kaydet" save_and_back_to_list: "Kaydet ve Listeye Dön" From e64d573337241b7a652099ecf629d8b513562325 Mon Sep 17 00:00:00 2001 From: Luis Ramos Date: Fri, 3 Apr 2020 12:06:39 +0100 Subject: [PATCH 130/130] Revert accidental push of a byebug statement with the direct translations push to master :see_no_evil: --- app/controllers/admin/variant_overrides_controller.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/app/controllers/admin/variant_overrides_controller.rb b/app/controllers/admin/variant_overrides_controller.rb index 9fe5c9fc0b..e12b43825b 100644 --- a/app/controllers/admin/variant_overrides_controller.rb +++ b/app/controllers/admin/variant_overrides_controller.rb @@ -12,7 +12,6 @@ module Admin def index; end def bulk_update - byebug # Ensure we're authorised to update all variant overrides @vo_set.collection.each { |vo| authorize! :update, vo }