diff --git a/.gitignore b/.gitignore
index 8392b92c1a..f465ad7306 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@ tmp/
.#*
*~
*.~lock.*
+tags
.emacs.desktop
.DS_Store
*.sublime-project*
@@ -36,7 +37,4 @@ config/initializers/feature_toggle.rb
NERD_tree*
coverage
libpeerconnection.log
-tags
-
-# Ignore application configuration
/config/application.yml
diff --git a/Gemfile b/Gemfile
index 3f84c28183..521dbaeab1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -42,6 +42,7 @@ gem 'gmaps4rails'
gem 'spinjs-rails'
gem 'rack-ssl', :require => 'rack/ssl'
gem 'custom_error_message', :github => 'jeremydurham/custom-err-msg'
+gem 'angularjs-file-upload-rails', '~> 1.1.0'
gem 'figaro'
gem 'foreigner'
diff --git a/Gemfile.lock b/Gemfile.lock
index 94c6ead27a..2eab9c7f59 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -153,6 +153,7 @@ GEM
railties (>= 3.1)
sprockets
tilt
+ angularjs-file-upload-rails (1.1.0)
angularjs-rails (1.2.13)
ansi (1.4.2)
arel (3.0.3)
@@ -508,6 +509,7 @@ DEPENDENCIES
active_model_serializers
andand
angular-rails-templates
+ angularjs-file-upload-rails (~> 1.1.0)
angularjs-rails
awesome_print
aws-sdk
diff --git a/app/assets/javascripts/admin/admin.js.coffee b/app/assets/javascripts/admin/admin.js.coffee
index c80e6252de..cbb824f97d 100644
--- a/app/assets/javascripts/admin/admin.js.coffee
+++ b/app/assets/javascripts/admin/admin.js.coffee
@@ -1,3 +1,3 @@
angular.module("ofn.admin", ["ngResource", "ngAnimate", "ofn.dropdown", "admin.products", "infinite-scroll"]).config ($httpProvider) ->
$httpProvider.defaults.headers.common["X-CSRF-Token"] = $("meta[name=csrf-token]").attr("content")
- $httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"
\ No newline at end of file
+ $httpProvider.defaults.headers.common["Accept"] = "application/json, text/javascript, */*"
diff --git a/app/assets/javascripts/admin/all.js b/app/assets/javascripts/admin/all.js
index 3fb5e69499..7dc9d1116f 100644
--- a/app/assets/javascripts/admin/all.js
+++ b/app/assets/javascripts/admin/all.js
@@ -22,6 +22,9 @@
//= require ./payment_methods/payment_methods
//= require ./products/products
//= require ./shipping_methods/shipping_methods
+//= require ./utils/utils
//= require ./users/users
+//= require textAngular.min.js
+//= require textAngular-sanitize.min.js
//= require_tree .
diff --git a/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee b/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee
index c5b38191ba..cfcf6319af 100644
--- a/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee
+++ b/app/assets/javascripts/admin/enterprises/controllers/enterprise_controller.js.coffee
@@ -1,8 +1,17 @@
angular.module("admin.enterprises")
- .controller "enterpriseCtrl", ($scope, Enterprise, PaymentMethods, ShippingMethods) ->
+ .controller "enterpriseCtrl", ($scope, longDescription, NavigationCheck, Enterprise, PaymentMethods, ShippingMethods) ->
$scope.Enterprise = Enterprise.enterprise
$scope.PaymentMethods = PaymentMethods.paymentMethods
$scope.ShippingMethods = ShippingMethods.shippingMethods
+ $scope.navClear = NavigationCheck.clear
+ # htmlVariable is used by textAngular wysiwyg for the long descrtiption.
+ $scope.htmlVariable = longDescription
+
+ # Provide a callback for generating warning messages displayed before leaving the page. This is passed in
+ # from a directive "nav-check" in the page - if we pass it here it will be called in the test suite,
+ # and on all new uses of this contoller, and we might not want that .
+ $scope.enterpriseNavCallback = ->
+ "You are editing an enterprise!"
for payment_method in $scope.PaymentMethods
payment_method.selected = payment_method.id in $scope.Enterprise.payment_method_ids
@@ -32,4 +41,4 @@ angular.module("admin.enterprises")
$scope.ShippingMethods.reduce (count, shipping_method) ->
count++ if shipping_method.selected
count
- , 0
\ No newline at end of file
+ , 0
diff --git a/app/assets/javascripts/admin/enterprises/enterprises.js.coffee b/app/assets/javascripts/admin/enterprises/enterprises.js.coffee
index 6189661035..e1e43854d1 100644
--- a/app/assets/javascripts/admin/enterprises/enterprises.js.coffee
+++ b/app/assets/javascripts/admin/enterprises/enterprises.js.coffee
@@ -1 +1 @@
-angular.module("admin.enterprises", ["admin.payment_methods", "admin.shipping_methods", "admin.users"])
\ No newline at end of file
+angular.module("admin.enterprises", ["admin.payment_methods", "admin.utils", "admin.shipping_methods", "admin.users", "textAngular"])
\ No newline at end of file
diff --git a/app/assets/javascripts/admin/enterprises/services/enterprise.js.coffee b/app/assets/javascripts/admin/enterprises/services/enterprise.js.coffee
index b6e6a6147e..26061ef720 100644
--- a/app/assets/javascripts/admin/enterprises/services/enterprise.js.coffee
+++ b/app/assets/javascripts/admin/enterprises/services/enterprise.js.coffee
@@ -1,4 +1,5 @@
angular.module("admin.enterprises")
+ # Populate Enterprise.enterprise with enterprise json array from the page.
.factory 'Enterprise', (enterprise) ->
new class Enterprise
- enterprise: enterprise
\ No newline at end of file
+ enterprise: enterprise
diff --git a/app/assets/javascripts/admin/utils/directives/navigation_check.js.coffee b/app/assets/javascripts/admin/utils/directives/navigation_check.js.coffee
new file mode 100644
index 0000000000..95f52505eb
--- /dev/null
+++ b/app/assets/javascripts/admin/utils/directives/navigation_check.js.coffee
@@ -0,0 +1,9 @@
+angular.module("admin.utils").directive "navCheck", (NavigationCheck)->
+ restrict: 'A'
+ scope:
+ navCallback: '&'
+ link: (scope,element,attributes) ->
+ # Define navigationCallback on a controller in scope, otherwise this default will be used:
+ scope.navCallback ||= ->
+ "You will lose any unsaved work!"
+ NavigationCheck.register(scope.navCallback)
diff --git a/app/assets/javascripts/admin/utils/services/navigation_check.js.coffee b/app/assets/javascripts/admin/utils/services/navigation_check.js.coffee
new file mode 100644
index 0000000000..ff1041474c
--- /dev/null
+++ b/app/assets/javascripts/admin/utils/services/navigation_check.js.coffee
@@ -0,0 +1,46 @@
+angular.module("admin.utils")
+ .factory "NavigationCheck", ($window, $rootScope) ->
+ new class NavigationCheck
+ callbacks = []
+ constructor: ->
+ if $window.addEventListener
+ $window.addEventListener "beforeunload", @onBeforeUnloadHandler
+ else
+ $window.onbeforeunload = @onBeforeUnloadHandler
+
+ $rootScope.$on "$locationChangeStart", @locationChangeStartHandler
+
+
+ # Action for regular browser navigation.
+ onBeforeUnloadHandler: ($event) =>
+ message = @getMessage()
+ if message
+ ($event or $window.event).preventDefault()
+ message
+
+ # Action for angular navigation.
+ locationChangeStartHandler: ($event) =>
+ message = @getMessage()
+ if message and not $window.confirm(message)
+ $event.stopPropagation() if $event.stopPropagation
+ $event.preventDefault() if $event.preventDefault
+ $event.cancelBubble = true
+ $event.returnValue = false
+
+ # Runs callback functions to retreive most recently added non-empty message.
+ getMessage: ->
+ message = null
+ message = callback() ? message for callback in callbacks
+ message
+
+ register: (callback) =>
+ callbacks.push callback
+
+ clear: =>
+ if $window.addEventListener
+ $window.removeEventListener "beforeunload", @onBeforeUnloadHandler
+ else
+ $window.onbeforeunload = null
+
+ $rootScope.$on "$locationChangeStart", null
+
diff --git a/app/assets/javascripts/admin/utils/utils.js.coffee b/app/assets/javascripts/admin/utils/utils.js.coffee
new file mode 100644
index 0000000000..4d58ae930a
--- /dev/null
+++ b/app/assets/javascripts/admin/utils/utils.js.coffee
@@ -0,0 +1 @@
+angular.module("admin.utils", [])
\ No newline at end of file
diff --git a/app/assets/javascripts/darkswarm/all.js.coffee b/app/assets/javascripts/darkswarm/all.js.coffee
index f529ac3255..45acfd6523 100644
--- a/app/assets/javascripts/darkswarm/all.js.coffee
+++ b/app/assets/javascripts/darkswarm/all.js.coffee
@@ -15,6 +15,8 @@
#= require ../shared/bindonce.min.js
#= require ../shared/ng-infinite-scroll.min.js
#= require ../shared/angular-local-storage.js
+#= require angularjs-file-upload
+
#= require angular-rails-templates
#= require_tree ../templates
diff --git a/app/assets/javascripts/darkswarm/controllers/enterprise_image_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/enterprise_image_controller.js.coffee
new file mode 100644
index 0000000000..e12d55b50c
--- /dev/null
+++ b/app/assets/javascripts/darkswarm/controllers/enterprise_image_controller.js.coffee
@@ -0,0 +1,13 @@
+angular.module('Darkswarm').controller "EnterpriseImageCtrl", ($scope, EnterpriseImageService) ->
+ $scope.imageStep = 'logo'
+
+ $scope.imageSteps = ['logo', 'promo']
+
+ $scope.imageUploader = EnterpriseImageService.imageUploader
+
+ $scope.imageSelect = (image_step) ->
+ EnterpriseImageService.imageSrc = null
+ $scope.imageStep = image_step
+
+ $scope.imageSrc = ->
+ EnterpriseImageService.imageSrc
diff --git a/app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee
similarity index 59%
rename from app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee
rename to app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee
index 98053daebf..1e43c17465 100644
--- a/app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/enterprises_controller.js.coffee
@@ -1,11 +1,14 @@
-Darkswarm.controller "HubsCtrl", ($scope, Hubs, Search, $document, $rootScope, HashNavigation, FilterSelectorsService) ->
- $scope.Hubs = Hubs
- $scope.hubs = Hubs.visible
+Darkswarm.controller "EnterprisesCtrl", ($scope, Enterprises, Search, $document, $rootScope, HashNavigation, FilterSelectorsService, EnterpriseModal) ->
+ $scope.Enterprises = Enterprises
$scope.totalActive = FilterSelectorsService.totalActive
$scope.clearAll = FilterSelectorsService.clearAll
$scope.filterText = FilterSelectorsService.filterText
$scope.FilterSelectorsService = FilterSelectorsService
$scope.query = Search.search()
+ $scope.openModal = EnterpriseModal.open
+ $scope.activeTaxons = []
+ $scope.show_profiles = false
+ $scope.filtersActive = false
$scope.$watch "query", (query)->
Search.search query
diff --git a/app/assets/javascripts/darkswarm/controllers/producers_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/producers_controller.js.coffee
deleted file mode 100644
index d88af1e53d..0000000000
--- a/app/assets/javascripts/darkswarm/controllers/producers_controller.js.coffee
+++ /dev/null
@@ -1,12 +0,0 @@
-Darkswarm.controller "ProducersCtrl", ($scope, Producers, $filter, FilterSelectorsService, Search) ->
- $scope.Producers = Producers
- $scope.totalActive = FilterSelectorsService.totalActive
- $scope.clearAll = FilterSelectorsService.clearAll
- $scope.filterText = FilterSelectorsService.filterText
- $scope.FilterSelectorsService = FilterSelectorsService
- $scope.filtersActive = false
- $scope.activeTaxons = []
- $scope.query = Search.search()
-
- $scope.$watch "query", (query)->
- Search.search query
diff --git a/app/assets/javascripts/darkswarm/controllers/registration_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/registration/registration_controller.js.coffee
similarity index 84%
rename from app/assets/javascripts/darkswarm/controllers/registration_controller.js.coffee
rename to app/assets/javascripts/darkswarm/controllers/registration/registration_controller.js.coffee
index 2fcc0f32fd..3be0378619 100644
--- a/app/assets/javascripts/darkswarm/controllers/registration_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/registration/registration_controller.js.coffee
@@ -3,7 +3,7 @@ Darkswarm.controller "RegistrationCtrl", ($scope, RegistrationService, Enterpris
$scope.enterprise = EnterpriseRegistrationService.enterprise
$scope.select = RegistrationService.select
- $scope.steps = ['details','address','contact','about','images','social']
+ $scope.steps = ['details','contact','type','about','images','social']
$scope.countries = availableCountries
diff --git a/app/assets/javascripts/darkswarm/controllers/registration_form_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/registration/registration_form_controller.js.coffee
similarity index 88%
rename from app/assets/javascripts/darkswarm/controllers/registration_form_controller.js.coffee
rename to app/assets/javascripts/darkswarm/controllers/registration/registration_form_controller.js.coffee
index 84f133da54..fabc2c382a 100644
--- a/app/assets/javascripts/darkswarm/controllers/registration_form_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/registration/registration_form_controller.js.coffee
@@ -12,4 +12,4 @@ Darkswarm.controller "RegistrationFormCtrl", ($scope, RegistrationService, Enter
EnterpriseRegistrationService.update(nextStep) if $scope.valid(form)
$scope.selectIfValid = (nextStep, form) ->
- RegistrationService.select(nextStep) if $scope.valid(form)
\ No newline at end of file
+ RegistrationService.select(nextStep) if $scope.valid(form)
diff --git a/app/assets/javascripts/darkswarm/controllers/tabs/producers_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/tabs/producers_controller.js.coffee
index 6db73f7af3..7a5ebb78f3 100644
--- a/app/assets/javascripts/darkswarm/controllers/tabs/producers_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/tabs/producers_controller.js.coffee
@@ -1,3 +1,4 @@
-Darkswarm.controller "ProducersTabCtrl", ($scope, CurrentHub, Enterprises) ->
- # Injecting Enterprises so CurrentHub.producers is dereferenced
+Darkswarm.controller "ProducersTabCtrl", ($scope, CurrentHub, Enterprises, EnterpriseModal) ->
+ # Injecting Enterprises so CurrentHub.producers is dereferenced.
+ # We should probably dereference here instead and separate out CurrentHub dereferencing from the Enterprise factory.
$scope.CurrentHub = CurrentHub
diff --git a/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee
index eb501bcb4a..0bbe0bbd23 100644
--- a/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee
@@ -1,18 +1,11 @@
Darkswarm.controller "TabsCtrl", ($scope, $rootScope, $location, OrderCycle) ->
+ # Return active if supplied path matches url hash path.
$scope.active = (path)->
- if !OrderCycle.selected() and $location.hash() == "" and path == "about"
- true
- else
- $location.hash() == path
+ $location.hash() == path
-
- $scope.tabs = ["contact", "about", "groups", "producers"]
- for tab in $scope.tabs
- $scope[tab] =
- path: tab
-
- $scope.select = (tab)->
- if $scope.active(tab.path)
+ # Toggle tab selected status by setting the url hash path.
+ $scope.select = (path)->
+ if $scope.active(path)
$location.hash ""
else
- $location.hash tab.path
+ $location.hash path
diff --git a/app/assets/javascripts/darkswarm/darkswarm.js.coffee b/app/assets/javascripts/darkswarm/darkswarm.js.coffee
index 1e58fe7294..e8ea9dce3c 100644
--- a/app/assets/javascripts/darkswarm/darkswarm.js.coffee
+++ b/app/assets/javascripts/darkswarm/darkswarm.js.coffee
@@ -1,18 +1,19 @@
-window.Darkswarm = angular.module("Darkswarm", ["ngResource",
- 'mm.foundation',
- 'angularLocalStorage',
- 'pasvaz.bindonce',
- 'infinite-scroll',
- 'angular-flash.service',
+window.Darkswarm = angular.module("Darkswarm", ["ngResource",
+ 'mm.foundation',
+ 'angularLocalStorage',
+ 'pasvaz.bindonce',
+ 'infinite-scroll',
+ 'angular-flash.service',
'templates',
'ngSanitize',
'ngAnimate',
'google-maps',
'duScroll',
+ 'angularFileUpload',
]).config ($httpProvider, $tooltipProvider, $locationProvider, $anchorScrollProvider) ->
- $httpProvider.defaults.headers.post['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')
- $httpProvider.defaults.headers.put['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')
- $httpProvider.defaults.headers['common']['X-Requested-With'] = 'XMLHttpRequest'
+ $httpProvider.defaults.headers.post['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')
+ $httpProvider.defaults.headers.put['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content')
+ $httpProvider.defaults.headers['common']['X-Requested-With'] = 'XMLHttpRequest'
$httpProvider.defaults.headers.common.Accept = "application/json, text/javascript, */*"
# This allows us to trigger these two events on tooltips
@@ -20,4 +21,3 @@ window.Darkswarm = angular.module("Darkswarm", ["ngResource",
# We manually handle our scrolling
$anchorScrollProvider.disableAutoScrolling()
-
diff --git a/app/assets/javascripts/darkswarm/directives/producer_modal.js.coffee b/app/assets/javascripts/darkswarm/directives/enterprise_modal.js.coffee
similarity index 71%
rename from app/assets/javascripts/darkswarm/directives/producer_modal.js.coffee
rename to app/assets/javascripts/darkswarm/directives/enterprise_modal.js.coffee
index af2b13f157..fa6378afe2 100644
--- a/app/assets/javascripts/darkswarm/directives/producer_modal.js.coffee
+++ b/app/assets/javascripts/darkswarm/directives/enterprise_modal.js.coffee
@@ -1,4 +1,4 @@
-Darkswarm.directive "producerModal", ($modal)->
+Darkswarm.directive "enterpriseModal", ($modal)->
restrict: 'E'
replace: true
template: ""
@@ -6,5 +6,4 @@ Darkswarm.directive "producerModal", ($modal)->
link: (scope, elem, attrs, ctrl)->
elem.on "click", (ev)=>
ev.stopPropagation()
- scope.modalInstance = $modal.open(controller: ctrl, templateUrl: 'producer_modal.html', scope: scope)
-
+ scope.modalInstance = $modal.open(controller: ctrl, templateUrl: 'enterprise_modal.html', scope: scope)
diff --git a/app/assets/javascripts/darkswarm/directives/hub_modal.js.coffee b/app/assets/javascripts/darkswarm/directives/hub_modal.js.coffee
deleted file mode 100644
index 6eb0299ab4..0000000000
--- a/app/assets/javascripts/darkswarm/directives/hub_modal.js.coffee
+++ /dev/null
@@ -1,8 +0,0 @@
-Darkswarm.directive "hubModal", ($modal)->
- restrict: 'E'
- replace: true
- template: "{{enterprise.name}}"
- link: (scope, elem, attrs, ctrl)->
- elem.on "click", (ev)=>
- ev.stopPropagation()
- scope.modalInstance = $modal.open(controller: ctrl, templateUrl: 'hub_modal.html', scope: scope)
diff --git a/app/assets/javascripts/darkswarm/directives/inline_alert.js.coffee b/app/assets/javascripts/darkswarm/directives/inline_alert.js.coffee
new file mode 100644
index 0000000000..9b37934375
--- /dev/null
+++ b/app/assets/javascripts/darkswarm/directives/inline_alert.js.coffee
@@ -0,0 +1,7 @@
+Darkswarm.directive "ofnInlineAlert", ->
+ restrict: 'A'
+ scope: true
+ link: (scope, elem, attrs) ->
+ scope.visible = true
+ scope.close = ->
+ scope.visible = false
diff --git a/app/assets/javascripts/darkswarm/directives/inline_flash.js.coffee b/app/assets/javascripts/darkswarm/directives/inline_flash.js.coffee
deleted file mode 100644
index 46550b854f..0000000000
--- a/app/assets/javascripts/darkswarm/directives/inline_flash.js.coffee
+++ /dev/null
@@ -1,6 +0,0 @@
-Darkswarm.directive "ofnInlineFlash", ->
- restrict: 'E'
- controller: ($scope) ->
- $scope.visible = true
- $scope.closeFlash = ->
- $scope.visible = false
diff --git a/app/assets/javascripts/darkswarm/directives/registration_limit_modal.js.coffee b/app/assets/javascripts/darkswarm/directives/registration_limit_modal.js.coffee
new file mode 100644
index 0000000000..2b38b3d31f
--- /dev/null
+++ b/app/assets/javascripts/darkswarm/directives/registration_limit_modal.js.coffee
@@ -0,0 +1,13 @@
+Darkswarm.directive "ofnRegistrationLimitModal", (Navigation, $modal, Loading) ->
+ restrict: 'A'
+ link: (scope, elem, attr)->
+ scope.modalInstance = $modal.open
+ templateUrl: 'registration/limit_reached.html'
+ windowClass: "login-modal register-modal xlarge"
+ backdrop: 'static'
+
+ scope.modalInstance.result.then scope.close, scope.close
+
+ scope.close = ->
+ Loading.message = "Taking you back to the home page"
+ Navigation.go "/"
diff --git a/app/assets/javascripts/darkswarm/filters/capitalize.js.coffee b/app/assets/javascripts/darkswarm/filters/capitalize.js.coffee
index acbd3fd637..5c2272135b 100644
--- a/app/assets/javascripts/darkswarm/filters/capitalize.js.coffee
+++ b/app/assets/javascripts/darkswarm/filters/capitalize.js.coffee
@@ -1,4 +1,5 @@
Darkswarm.filter "capitalize", ->
+ # Convert to basic sentence case.
(input, scope) ->
- input = input.toLowerCase() if input?
+ input = input.toLowerCase() if input?
input.substring(0, 1).toUpperCase() + input.substring(1)
diff --git a/app/assets/javascripts/darkswarm/filters/filter_hubs.js.coffee b/app/assets/javascripts/darkswarm/filters/filter_hubs.js.coffee
deleted file mode 100644
index b6adb84508..0000000000
--- a/app/assets/javascripts/darkswarm/filters/filter_hubs.js.coffee
+++ /dev/null
@@ -1,9 +0,0 @@
-Darkswarm.filter 'hubs', (Matcher)->
- (hubs, text) ->
- hubs ||= []
- text ?= ""
-
- hubs.filter (hub)=>
- Matcher.match [
- hub.name, hub.address.zipcode, hub.address.city, hub.address.state
- ], text
diff --git a/app/assets/javascripts/darkswarm/filters/filter_producers.js.coffee b/app/assets/javascripts/darkswarm/filters/filter_producers.js.coffee
deleted file mode 100644
index ed5b8c1bea..0000000000
--- a/app/assets/javascripts/darkswarm/filters/filter_producers.js.coffee
+++ /dev/null
@@ -1,6 +0,0 @@
-Darkswarm.filter 'filterProducers', (hubsFilter)->
- (producers, text) ->
- producers ||= []
- text ?= ""
- hubsFilter(producers, text)
-
diff --git a/app/assets/javascripts/darkswarm/filters/localize_currency.js.coffee b/app/assets/javascripts/darkswarm/filters/localize_currency.js.coffee
new file mode 100644
index 0000000000..7087e09fc3
--- /dev/null
+++ b/app/assets/javascripts/darkswarm/filters/localize_currency.js.coffee
@@ -0,0 +1,15 @@
+Darkswarm.filter "localizeCurrency", (currencyConfig)->
+ # Convert number to string currency using injected currency configuration.
+ (amount) ->
+ # Set country code (eg. "US").
+ currency_code = if currencyConfig.display_currency then " " + currencyConfig.currency else ""
+ # Set decimal points, 2 or 0 if hide_cents.
+ decimals = if currencyConfig.hide_cents == "true" then 0 else 2
+ # We need to use parseFloat before toFixed as the amount should come in as a string.
+ amount_fixed = parseFloat(amount).toFixed(decimals)
+
+ # Build the final price string. TODO use spree decimal point and spacer character settings.
+ if currencyConfig.symbol_position == 'before'
+ currencyConfig.symbol + amount_fixed + currency_code
+ else
+ amount_fixed + " " + currencyConfig.symbol + currency_code
diff --git a/app/assets/javascripts/darkswarm/filters/search_enterprises.js.coffee b/app/assets/javascripts/darkswarm/filters/search_enterprises.js.coffee
new file mode 100644
index 0000000000..2c42fe93d3
--- /dev/null
+++ b/app/assets/javascripts/darkswarm/filters/search_enterprises.js.coffee
@@ -0,0 +1,10 @@
+Darkswarm.filter 'searchEnterprises', (Matcher)->
+ # Search multiple fields of enterprises for matching text fragment.
+ (enterprises, text) ->
+ enterprises ||= []
+ text ?= ""
+
+ enterprises.filter (enterprise)=>
+ Matcher.match [
+ enterprise.name, enterprise.address.zipcode, enterprise.address.city, enterprise.address.state
+ ], text
diff --git a/app/assets/javascripts/darkswarm/filters/show_hub_profiles.js.coffee b/app/assets/javascripts/darkswarm/filters/show_hub_profiles.js.coffee
new file mode 100644
index 0000000000..b97a0c35e4
--- /dev/null
+++ b/app/assets/javascripts/darkswarm/filters/show_hub_profiles.js.coffee
@@ -0,0 +1,8 @@
+Darkswarm.filter 'showHubProfiles', ()->
+ # Filter hub_profile enterprises in or out.
+ (enterprises, show_profiles) ->
+ enterprises ||= []
+ show_profiles ?= false
+
+ enterprises.filter (enterprise)=>
+ show_profiles or enterprise.is_distributor
diff --git a/app/assets/javascripts/darkswarm/filters/taxons.js.coffee b/app/assets/javascripts/darkswarm/filters/taxons.js.coffee
index 3eb32ce1a4..a7ccc9b0cc 100644
--- a/app/assets/javascripts/darkswarm/filters/taxons.js.coffee
+++ b/app/assets/javascripts/darkswarm/filters/taxons.js.coffee
@@ -1,13 +1,16 @@
-Darkswarm.filter 'taxons', (Matcher)->
- # Filter anything that responds to object.taxons, and/or object.primary_taxon
+Darkswarm.filter 'taxons', ()->
+ # Filter anything that responds to object.taxons, object.supplied_taxon or object.primary_taxon.
(objects, ids) ->
objects ||= []
ids ?= []
if ids.length == 0
+ # No taxons selected, pass all objects through.
objects
else
objects.filter (obj)->
taxons = obj.taxons
- taxons.concat obj.supplied_taxons if obj.supplied_taxons
+ # Combine object taxons with supplied taxons, if they exist.
+ taxons = taxons.concat obj.supplied_taxons if obj.supplied_taxons
+ # Match primary taxon if it exists, then taxon array.
obj.primary_taxon?.id in ids || taxons.some (taxon)->
taxon.id in ids
diff --git a/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee b/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee
index 5f5ae520e3..f2d7049e67 100644
--- a/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/authentication_service.js.coffee
@@ -1,4 +1,4 @@
-Darkswarm.factory "AuthenticationService", (Navigation, $modal, $location, Redirections)->
+Darkswarm.factory "AuthenticationService", (Navigation, $modal, $location, Redirections, Loading)->
new class AuthenticationService
selectedPath: "/login"
@@ -25,4 +25,9 @@ Darkswarm.factory "AuthenticationService", (Navigation, $modal, $location, Redir
active: Navigation.active
close: ->
- Navigation.navigate "/"
+ if location.pathname == "/"
+ Navigation.navigate "/"
+ else
+ Loading.message = "Taking you back to the home page"
+ location.hash = ""
+ location.pathname = "/"
diff --git a/app/assets/javascripts/darkswarm/services/cart.js.coffee b/app/assets/javascripts/darkswarm/services/cart.js.coffee
index def1c9b6d0..2b58bb70ae 100644
--- a/app/assets/javascripts/darkswarm/services/cart.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/cart.js.coffee
@@ -3,7 +3,7 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)->
new class Cart
dirty: false
order: CurrentOrder.order
- line_items: CurrentOrder.order?.line_items || []
+ line_items: CurrentOrder.order?.line_items || []
constructor: ->
for line_item in @line_items
line_item.variant.line_item = line_item
@@ -22,13 +22,13 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)->
# TODO what shall we do here?
data: =>
- variants = {}
+ variants = {}
for li in @line_items_present()
- variants[li.variant.id] =
+ variants[li.variant.id] =
quantity: li.quantity
max_quantity: li.max_quantity
{variants: variants}
-
+
saved: =>
@dirty = false
@@ -48,15 +48,15 @@ Darkswarm.factory 'Cart', (CurrentOrder, Variants, $timeout, $http)->
total: =>
@line_items_present().map (li)->
- li.variant.getPrice()
+ li.variant.totalPrice()
.reduce (t, price)->
t + price
, 0
register_variant: (variant)=>
exists = @line_items.some (li)-> li.variant == variant
- @create_line_item(variant) unless exists
-
+ @create_line_item(variant) unless exists
+
create_line_item: (variant)->
variant.line_item =
variant: variant
diff --git a/app/assets/javascripts/darkswarm/services/current_hub.js.coffee b/app/assets/javascripts/darkswarm/services/current_hub.js.coffee
index 5e6a385b8a..ade8d75d18 100644
--- a/app/assets/javascripts/darkswarm/services/current_hub.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/current_hub.js.coffee
@@ -1,3 +1,4 @@
-Darkswarm.factory 'CurrentHub', ($location, $filter, currentHub) ->
+Darkswarm.factory 'CurrentHub', (currentHub) ->
+ # Populate CurrentHub.hub from json in page. This is probably redundant now.
new class CurrentHub
hub: currentHub
diff --git a/app/assets/javascripts/darkswarm/services/current_order.js.coffee b/app/assets/javascripts/darkswarm/services/current_order.js.coffee
index cd2402f0c0..6570ddcd06 100644
--- a/app/assets/javascripts/darkswarm/services/current_order.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/current_order.js.coffee
@@ -1,3 +1,4 @@
Darkswarm.factory 'CurrentOrder', (currentOrder) ->
+ # Populate Currentorder.order from json in page. This is probably redundant now.
new class CurrentOrder
order: currentOrder
diff --git a/app/assets/javascripts/darkswarm/services/enterprise_image_service.js.coffee b/app/assets/javascripts/darkswarm/services/enterprise_image_service.js.coffee
new file mode 100644
index 0000000000..c8cd64e93a
--- /dev/null
+++ b/app/assets/javascripts/darkswarm/services/enterprise_image_service.js.coffee
@@ -0,0 +1,12 @@
+Darkswarm.factory "EnterpriseImageService", (FileUploader, spreeApiKey) ->
+ new class EnterpriseImageService
+ imageSrc: null
+
+ imageUploader: new FileUploader
+ headers:
+ 'X-Spree-Token': spreeApiKey
+ autoUpload: true
+
+ configure: (enterprise) =>
+ @imageUploader.url = "/api/enterprises/#{enterprise.id}/update_image"
+ @imageUploader.onSuccessItem = (image, response) => @imageSrc = response
diff --git a/app/assets/javascripts/darkswarm/services/enterprise_modal.js.coffee b/app/assets/javascripts/darkswarm/services/enterprise_modal.js.coffee
new file mode 100644
index 0000000000..a0fb3ce5c5
--- /dev/null
+++ b/app/assets/javascripts/darkswarm/services/enterprise_modal.js.coffee
@@ -0,0 +1,8 @@
+Darkswarm.factory "EnterpriseModal", ($modal, $rootScope)->
+ # Build a modal popup for an enterprise.
+ new class EnterpriseModal
+ open: (enterprise)->
+ scope = $rootScope.$new(true) # Spawn an isolate to contain the enterprise
+
+ scope.enterprise = enterprise
+ $modal.open(templateUrl: "enterprise_modal.html", scope: scope)
diff --git a/app/assets/javascripts/darkswarm/services/enterprise_registration_service.js.coffee b/app/assets/javascripts/darkswarm/services/enterprise_registration_service.js.coffee
index 68915193ee..77b2204316 100644
--- a/app/assets/javascripts/darkswarm/services/enterprise_registration_service.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/enterprise_registration_service.js.coffee
@@ -1,4 +1,4 @@
-Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService, CurrentUser, spreeApiKey, Loading, availableCountries, enterpriseAttributes) ->
+Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService, EnterpriseImageService, CurrentUser, spreeApiKey, Loading, availableCountries, enterpriseAttributes) ->
new class EnterpriseRegistrationService
enterprise:
user_ids: [CurrentUser.id]
@@ -22,6 +22,7 @@ Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService,
).success((data) =>
Loading.clear()
@enterprise.id = data
+ EnterpriseImageService.configure(@enterprise)
RegistrationService.select('about')
).error((data) =>
Loading.clear()
@@ -54,4 +55,4 @@ Darkswarm.factory "EnterpriseRegistrationService", ($http, RegistrationService,
enterprise[key] = value
enterprise.address_attributes = @enterprise.address if @enterprise.address?
enterprise.address_attributes.country_id = @enterprise.country.id if @enterprise.country?
- enterprise
\ No newline at end of file
+ enterprise
diff --git a/app/assets/javascripts/darkswarm/services/enterprises.js.coffee b/app/assets/javascripts/darkswarm/services/enterprises.js.coffee
index 50143e006b..6040f1d150 100644
--- a/app/assets/javascripts/darkswarm/services/enterprises.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/enterprises.js.coffee
@@ -1,13 +1,21 @@
-Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer)->
+Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer, visibleFilter)->
new class Enterprises
- enterprises_by_id: {} # id/object pairs for lookup
+ enterprises_by_id: {}
constructor: ->
+ # Populate Enterprises.enterprises from json in page.
@enterprises = enterprises
+ # Map enterprises to id/object pairs for lookup.
for enterprise in enterprises
@enterprises_by_id[enterprise.id] = enterprise
+ # Replace enterprise and taxons ids with actual objects.
@dereferenceEnterprises()
@dereferenceTaxons()
-
+ @visible_enterprises = visibleFilter @enterprises
+ @producers = @visible_enterprises.filter (enterprise)->
+ enterprise.category in ["producer_hub", "producer_shop", "producer"]
+ @hubs = @visible_enterprises.filter (enterprise)->
+ enterprise.category in ["hub", "hub_profile", "producer_hub", "producer_shop"]
+
dereferenceEnterprises: ->
if CurrentHub.hub?.id
CurrentHub.hub = @enterprises_by_id[CurrentHub.hub.id]
@@ -16,6 +24,7 @@ Darkswarm.factory 'Enterprises', (enterprises, CurrentHub, Taxons, Dereferencer)
Dereferencer.dereference enterprise.producers, @enterprises_by_id
dereferenceTaxons: ->
- for enterprise in @enterprises
+ for enterprise in @enterprises
Dereferencer.dereference enterprise.taxons, Taxons.taxons_by_id
Dereferencer.dereference enterprise.supplied_taxons, Taxons.taxons_by_id
+
diff --git a/app/assets/javascripts/darkswarm/services/hubs.js.coffee b/app/assets/javascripts/darkswarm/services/hubs.js.coffee
deleted file mode 100644
index de9900866f..0000000000
--- a/app/assets/javascripts/darkswarm/services/hubs.js.coffee
+++ /dev/null
@@ -1,9 +0,0 @@
-Darkswarm.factory 'Hubs', ($filter, Enterprises, visibleFilter) ->
- new class Hubs
- constructor: ->
- @hubs = @order Enterprises.enterprises.filter (hub)->
- hub.is_distributor && hub.has_shopfront
- @visible = visibleFilter @hubs
-
- order: (hubs)->
- $filter('orderBy')(hubs, ['-active', '+orders_close_at'])
diff --git a/app/assets/javascripts/darkswarm/services/map.js.coffee b/app/assets/javascripts/darkswarm/services/map.js.coffee
index 43750acdb2..703c3c54bf 100644
--- a/app/assets/javascripts/darkswarm/services/map.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/map.js.coffee
@@ -1,7 +1,7 @@
-Darkswarm.factory "OfnMap", (Enterprises, MapModal, visibleFilter)->
+Darkswarm.factory "OfnMap", (Enterprises, EnterpriseModal, visibleFilter)->
new class OfnMap
constructor: ->
- @enterprises = (@extend(enterprise) for enterprise in visibleFilter(Enterprises.enterprises))
+ @enterprises = (@extend(enterprise) for enterprise in visibleFilter(Enterprises.enterprises))
# Adding methods to each enterprise
@@ -14,4 +14,4 @@ Darkswarm.factory "OfnMap", (Enterprises, MapModal, visibleFilter)->
icon: enterprise.icon
id: enterprise.id
reveal: =>
- MapModal.open enterprise
+ EnterpriseModal.open enterprise
diff --git a/app/assets/javascripts/darkswarm/services/map_modal.js.coffee b/app/assets/javascripts/darkswarm/services/map_modal.js.coffee
deleted file mode 100644
index c9ed30f558..0000000000
--- a/app/assets/javascripts/darkswarm/services/map_modal.js.coffee
+++ /dev/null
@@ -1,12 +0,0 @@
-Darkswarm.factory "MapModal", ($modal, $rootScope)->
- new class MapModal
- open: (enterprise)->
- scope = $rootScope.$new(true) # Spawn an isolate to contain the enterprise
-
- scope.enterprise = enterprise
- if enterprise.is_distributor
- scope.hub = enterprise
- $modal.open(templateUrl: "hub_modal.html", scope: scope)
- else
- scope.producer = enterprise
- $modal.open(templateUrl: "map_modal_producer.html", scope: scope)
diff --git a/app/assets/javascripts/darkswarm/services/matcher.js.coffee b/app/assets/javascripts/darkswarm/services/matcher.js.coffee
index aadd6a4128..9360afdd1f 100644
--- a/app/assets/javascripts/darkswarm/services/matcher.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/matcher.js.coffee
@@ -1,6 +1,7 @@
Darkswarm.factory "Matcher", ->
- new class Matcher
- match: (properties, text)->
- properties.some (prop)->
- prop ||= ""
- prop.toLowerCase().indexOf(text.toLowerCase()) != -1
+ # Match text fragment in an array of strings.
+ new class Matcher
+ match: (properties, text)->
+ properties.some (prop)->
+ prop ||= ""
+ prop.toLowerCase().indexOf(text.toLowerCase()) != -1
diff --git a/app/assets/javascripts/darkswarm/services/producers.js.coffee b/app/assets/javascripts/darkswarm/services/producers.js.coffee
deleted file mode 100644
index 65d8e42c5d..0000000000
--- a/app/assets/javascripts/darkswarm/services/producers.js.coffee
+++ /dev/null
@@ -1,7 +0,0 @@
-Darkswarm.factory 'Producers', (Enterprises, visibleFilter) ->
- new class Producers
- constructor: ->
- @producers = Enterprises.enterprises.filter (enterprise)->
- enterprise.is_primary_producer
- @visible = visibleFilter @producers
-
diff --git a/app/assets/javascripts/darkswarm/services/registration_service.js.coffee b/app/assets/javascripts/darkswarm/services/registration_service.js.coffee
index a2a1fe2dc4..530d118025 100644
--- a/app/assets/javascripts/darkswarm/services/registration_service.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/registration_service.js.coffee
@@ -1,4 +1,4 @@
-Darkswarm.factory "RegistrationService", (Navigation, $modal, Loading)->
+angular.module('Darkswarm').factory "RegistrationService", (Navigation, $modal, Loading)->
new class RegistrationService
constructor: ->
@@ -20,4 +20,4 @@ Darkswarm.factory "RegistrationService", (Navigation, $modal, Loading)->
close: ->
Loading.message = "Taking you back to the home page"
- Navigation.go "/"
\ No newline at end of file
+ Navigation.go "/"
diff --git a/app/assets/javascripts/darkswarm/services/taxons.js.coffee b/app/assets/javascripts/darkswarm/services/taxons.js.coffee
index cbe6c118e0..984eb0df22 100644
--- a/app/assets/javascripts/darkswarm/services/taxons.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/taxons.js.coffee
@@ -1,8 +1,10 @@
Darkswarm.factory "Taxons", (taxons)->
new class Taxons
- taxons: taxons
+ # Populate Taxons.taxons from json in page.
+ taxons: taxons
taxons_by_id: {}
constructor: ->
+ # Map taxons to id/object pairs for lookup.
for taxon in @taxons
@taxons_by_id[taxon.id] = taxon
diff --git a/app/assets/javascripts/darkswarm/services/variants.js.coffee b/app/assets/javascripts/darkswarm/services/variants.js.coffee
index 0f231ac030..6562bd9e0b 100644
--- a/app/assets/javascripts/darkswarm/services/variants.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/variants.js.coffee
@@ -5,7 +5,8 @@ Darkswarm.factory 'Variants', ->
@variants[variant.id] ||= @extend variant
extend: (variant)->
- variant.getPrice = ->
- variant.price * variant.line_item.quantity
- variant.basePricePercentage = Math.round(variant.base_price / variant.price * 100)
+ # Add totalPrice method to calculate line item total. This should be on a line item!
+ variant.totalPrice = ->
+ variant.price_with_fees * variant.line_item.quantity
+ variant.basePricePercentage = Math.round(variant.price / variant.price_with_fees * 100)
variant
diff --git a/app/assets/javascripts/search/all.js b/app/assets/javascripts/search/all.js
deleted file mode 100644
index c684eed4b1..0000000000
--- a/app/assets/javascripts/search/all.js
+++ /dev/null
@@ -1,18 +0,0 @@
-// This is a manifest file that'll be compiled into including all the files listed below.
-// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
-// be included in the compiled file accessible from http://example.com/assets/application.js
-// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
-// the compiled file.
-//
-
-//= require jquery
-//= require jquery_ujs
-//= require jquery-ui
-//= require spin
-//= require foundation
-//= require_tree .
-//
-
-// Hacky fix for issue - http://foundation.zurb.com/forum/posts/2112-foundation-5100-syntax-error-in-js
-Foundation.set_namespace = function() {};
-$(function(){ $(document).foundation(); });
diff --git a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.base.js.coffee b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.base.js.coffee
deleted file mode 100644
index 684215a438..0000000000
--- a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.base.js.coffee
+++ /dev/null
@@ -1,444 +0,0 @@
-Gmaps = {}
-
-Gmaps.triggerOldOnload = ->
- Gmaps.oldOnload() if typeof(Gmaps.oldOnload) == 'function'
-
-Gmaps.loadMaps = ->
- #loop through all variable names.
- #there should only be maps inside so it trigger their load function
- for key, value of Gmaps
- searchLoadIncluded = key.search(/load/)
- if searchLoadIncluded == -1
- load_function_name = "load_" + key
- Gmaps[load_function_name]()
-
-window.Gmaps = Gmaps
-
-class @Gmaps4Rails
-
- constructor: ->
- #map config
- @map = null #DEPRECATED: will still contain a copy of serviceObject below as transition
- @serviceObject = null #contains the map we're working on
- @visibleInfoWindow = null #contains the current opened infowindow
- @userLocation = null #contains user's location if geolocalization was performed and successful
-
- #empty slots
- @geolocationSuccess = -> false #triggered when geolocation succeeds. Can be customized.
- @geolocationFailure = -> false #triggered when geolocation fails. If customized, must be like= function(navigator_handles_geolocation){} where 'navigator_handles_geolocation' is a boolean
- @callback = -> false #to let user set a custom callback function
- @customClusterer = -> false #to let user set custom clusterer pictures
- @infobox = -> false #to let user use custom infoboxes
- @jsTemplate = false #to let user create infowindows client side
-
- @default_map_options =
- id: 'map'
- draggable: true
- detect_location: false # should the browser attempt to use geolocation detection features of HTML5?
- center_on_user: false # centers map on the location detected through the browser
- center_latitude: 0
- center_longitude: 0
- zoom: 7
- maxZoom: null
- minZoom: null
- auto_adjust : true # adjust the map to the markers if set to true
- auto_zoom: true # zoom given by auto-adjust
- bounds: [] # adjust map to these limits. Should be [{"lat": , "lng": }]
- raw: {} # raw json to pass additional options
-
- @default_markers_conf =
- #Marker config
- title: ""
- #MarkerImage config
- picture : ""
- width: 22
- length: 32
- draggable: false # how to modify: <%= gmaps( "markers" => { "data" => @object.to_gmaps4rails, "options" => { "draggable" => true }}) %>
- #clustering config
- do_clustering: false # do clustering if set to true
- randomize: false # Google maps can't display two markers which have the same coordinates. This randomizer enables to prevent this situation from happening.
- max_random_distance: 100 # in meters. Each marker coordinate could be altered by this distance in a random direction
- list_container: null # id of the ul that will host links to all markers
- offset: 0 # used when adding_markers to an existing map. Because new markers are concated with previous one, offset is here to prevent the existing from being re-created.
- raw: {} # raw json to pass additional options
-
- #Stored variables
- @markers = [] # contains all markers. A marker contains the following: {"description": , "longitude": , "title":, "latitude":, "picture": "", "width": "", "length": "", "sidebar": "", "serviceObject": google_marker}
- @boundsObject = null # contains current bounds from markers, polylines etc...
- @polygons = [] # contains raw data, array of arrays (first element could be a hash containing options)
- @polylines = [] # contains raw data, array of arrays (first element could be a hash containing options)
- @circles = [] # contains raw data, array of hash
- @markerClusterer = null # contains all marker clusterers
- @markerImages = []
-
- #Polyline Styling
- @polylines_conf = #default style for polylines
- strokeColor: "#FF0000"
- strokeOpacity: 1
- strokeWeight: 2
- clickable: false
- zIndex: null
-
- #tnitializes the map
- initialize : ->
- @serviceObject = @createMap()
- @map = @serviceObject #beware, soon deprecated
- if (@map_options.detect_location == true or @map_options.center_on_user == true)
- @findUserLocation(this)
- #resets sidebar if needed
- @resetSidebarContent()
-
- findUserLocation : (map_object) ->
- if (navigator.geolocation)
- #try to retrieve user's position
- positionSuccessful = (position) ->
- map_object.userLocation = map_object.createLatLng(position.coords.latitude, position.coords.longitude)
- #change map's center to focus on user's geoloc if asked
- if(map_object.map_options.center_on_user == true)
- map_object.centerMapOnUser()
- map_object.geolocationSuccess()
- positionFailure = ->
- map_object.geolocationFailure(true)
-
- navigator.geolocation.getCurrentPosition( positionSuccessful, positionFailure)
- else
- #failure but the navigator doesn't handle geolocation
- map_object.geolocationFailure(false)
-
-
- #////////////////////////////////////////////////////
- #//////////////////// DIRECTIONS ////////////////////
- #////////////////////////////////////////////////////
-
- create_direction : ->
- directionsDisplay = new google.maps.DirectionsRenderer()
- directionsService = new google.maps.DirectionsService()
-
- directionsDisplay.setMap(@serviceObject)
- #display panel only if required
- if @direction_conf.display_panel
- directionsDisplay.setPanel(document.getElementById(@direction_conf.panel_id))
-
- directionsDisplay.setOptions
- suppressMarkers: false
- suppressInfoWindows: false
- suppressPolylines: false
-
- request =
- origin: @direction_conf.origin
- destination: @direction_conf.destination
- waypoints: @direction_conf.waypoints
- optimizeWaypoints: @direction_conf.optimizeWaypoints
- unitSystem: google.maps.DirectionsUnitSystem[@direction_conf.unitSystem]
- avoidHighways: @direction_conf.avoidHighways
- avoidTolls: @direction_conf.avoidTolls
- region: @direction_conf.region
- travelMode: google.maps.DirectionsTravelMode[@direction_conf.travelMode]
- language: "en"
-
- directionsService.route request, (response, status) ->
- if (status == google.maps.DirectionsStatus.OK)
- directionsDisplay.setDirections(response)
-
- #////////////////////////////////////////////////////
- #///////////////////// CIRCLES //////////////////////
- #////////////////////////////////////////////////////
-
- #Loops through all circles
- #Loops through all circles and draws them
- create_circles : ->
- for circle in @circles
- @create_circle circle
-
- create_circle : (circle) ->
- #by convention, default style configuration could be integrated in the first element
- if circle == @circles[0]
- @circles_conf.strokeColor = circle.strokeColor if circle.strokeColor?
- @circles_conf.strokeOpacity = circle.strokeOpacity if circle.strokeOpacity?
- @circles_conf.strokeWeight = circle.strokeWeight if circle.strokeWeight?
- @circles_conf.fillColor = circle.fillColor if circle.fillColor?
- @circles_conf.fillOpacity = circle.fillOpacity if circle.fillOpacity?
-
- if circle.lat? and circle.lng?
- # always check if a config is given, if not, use defaults
- # NOTE: is there a cleaner way to do this? Maybe a hash merge of some sort?
- newCircle = new google.maps.Circle
- center: @createLatLng(circle.lat, circle.lng)
- strokeColor: circle.strokeColor || @circles_conf.strokeColor
- strokeOpacity: circle.strokeOpacity || @circles_conf.strokeOpacity
- strokeWeight: circle.strokeWeight || @circles_conf.strokeWeight
- fillOpacity: circle.fillOpacity || @circles_conf.fillOpacity
- fillColor: circle.fillColor || @circles_conf.fillColor
- clickable: circle.clickable || @circles_conf.clickable
- zIndex: circle.zIndex || @circles_conf.zIndex
- radius: circle.radius
-
- circle.serviceObject = newCircle
- newCircle.setMap(@serviceObject)
-
- # clear circles
- clear_circles : ->
- for circle in @circles
- @clear_circle circle
-
- clear_circle : (circle) ->
- circle.serviceObject.setMap(null)
-
- hide_circles : ->
- for circle in @circles
- @hide_circle circle
-
- hide_circle : (circle) ->
- circle.serviceObject.setMap(null)
-
- show_circles : ->
- for circle in @circles
- @show_circle @circle
-
- show_circle : (circle) ->
- circle.serviceObject.setMap(@serviceObject)
-
- #////////////////////////////////////////////////////
- #///////////////////// POLYGONS /////////////////////
- #////////////////////////////////////////////////////
-
- #polygons is an array of arrays. It loops.
- create_polygons : ->
- for polygon in @polygons
- @create_polygon(polygon)
-
- #creates a single polygon, triggered by create_polygons
- create_polygon : (polygon) ->
- polygon_coordinates = []
-
- #Polygon points are in an Array, that's why looping is necessary
- for point in polygon
- latlng = @createLatLng(point.lat, point.lng)
- polygon_coordinates.push(latlng)
- #first element of an Array could contain specific configuration for this particular polygon. If no config given, use default
- if point == polygon[0]
- strokeColor = point.strokeColor || @polygons_conf.strokeColor
- strokeOpacity = point.strokeOpacity || @polygons_conf.strokeOpacity
- strokeWeight = point.strokeWeight || @polygons_conf.strokeWeight
- fillColor = point.fillColor || @polygons_conf.fillColor
- fillOpacity = point.fillOpacity || @polygons_conf.fillOpacity
- clickable = point.clickable || @polygons_conf.clickable
-
- #Construct the polygon
- new_poly = new google.maps.Polygon
- paths: polygon_coordinates
- strokeColor: strokeColor
- strokeOpacity: strokeOpacity
- strokeWeight: strokeWeight
- fillColor: fillColor
- fillOpacity: fillOpacity
- clickable: clickable
- map: @serviceObject
-
- #save polygon in list
- polygon.serviceObject = new_poly
-
-
-
- #////////////////////////////////////////////////////
- #///////////////////// MARKERS //////////////////////
- #////////////////////////////////////////////////////
-
- #creates, clusterizes and adjusts map
- create_markers : ->
- @createServiceMarkersFromMarkers()
- @clusterize()
-
- #create google.maps Markers from data provided by user
- createServiceMarkersFromMarkers : ->
- for marker, index in @markers
- if not @markers[index].serviceObject?
- #extract options, test if value passed or use default
- Lat = @markers[index].lat
- Lng = @markers[index].lng
-
- #alter coordinates if randomize is true
- if @markers_conf.randomize
- LatLng = @randomize(Lat, Lng)
- #retrieve coordinates from the array
- Lat = LatLng[0]
- Lng = LatLng[1]
-
- #save object
- @markers[index].serviceObject = @createMarker
- "marker_picture": if @markers[index].picture then @markers[index].picture else @markers_conf.picture
- "marker_width": if @markers[index].width then @markers[index].width else @markers_conf.width
- "marker_height": if @markers[index].height then @markers[index].height else @markers_conf.length
- "marker_title": if @markers[index].title then @markers[index].title else null
- "marker_anchor": if @markers[index].marker_anchor then @markers[index].marker_anchor else null
- "shadow_anchor": if @markers[index].shadow_anchor then @markers[index].shadow_anchor else null
- "shadow_picture": if @markers[index].shadow_picture then @markers[index].shadow_picture else null
- "shadow_width": if @markers[index].shadow_width then @markers[index].shadow_width else null
- "shadow_height": if @markers[index].shadow_height then @markers[index].shadow_height else null
- "marker_draggable": if @markers[index].draggable then @markers[index].draggable else @markers_conf.draggable
- "rich_marker": if @markers[index].rich_marker then @markers[index].rich_marker else null
- "zindex": if @markers[index].zindex then @markers[index].zindex else null
- "Lat": Lat
- "Lng": Lng
- "index": index
-
- #add infowindowstuff if enabled
- @createInfoWindow(@markers[index])
- #create sidebar if enabled
- @createSidebar(@markers[index])
-
- @markers_conf.offset = @markers.length
-
- #creates Image Anchor Position or return null if nothing passed
- createImageAnchorPosition : (anchorLocation) ->
- if (anchorLocation == null)
- return null
- else
- return @createPoint(anchorLocation[0], anchorLocation[1])
-
-
- #replace old markers with new markers on an existing map
- replaceMarkers : (new_markers, adjustBounds = true) ->
- @clearMarkers()
- #reset previous markers
- @markers = new Array
- #reset current bounds
- @boundsObject = @createLatLngBounds() if adjustBounds
- #reset sidebar content if exists
- @resetSidebarContent()
- #add new markers
- @markers_conf.offset = 0
- @addMarkers(new_markers, adjustBounds)
-
- #add new markers to on an existing map
- addMarkers : (new_markers, adjustBounds = true) ->
- #update the list of markers to take into account
- @markers = @markers.concat(new_markers)
- #put markers on the map
- @create_markers()
- @adjustMapToBounds() if adjustBounds
-
- #////////////////////////////////////////////////////
- #///////////////////// SIDEBAR //////////////////////
- #////////////////////////////////////////////////////
-
- #//creates sidebar
- createSidebar : (marker_container) ->
- if (@markers_conf.list_container)
- ul = document.getElementById(@markers_conf.list_container)
- li = document.createElement('li')
- aSel = document.createElement('a')
- aSel.href = 'javascript:void(0);'
- html = if marker_container.sidebar? then marker_container.sidebar else "Marker"
- aSel.innerHTML = html
- currentMap = this
- aSel.onclick = @sidebar_element_handler(currentMap, marker_container.serviceObject, 'click')
- li.appendChild(aSel)
- ul.appendChild(li)
-
- #moves map to marker clicked + open infowindow
- sidebar_element_handler : (currentMap, marker, eventType) ->
- return () ->
- currentMap.map.panTo(marker.position)
- google.maps.event.trigger(marker, eventType)
-
-
- resetSidebarContent : ->
- if @markers_conf.list_container isnt null
- ul = document.getElementById(@markers_conf.list_container)
- ul.innerHTML = ""
-
- #////////////////////////////////////////////////////
- #////////////////// MISCELLANEOUS ///////////////////
- #////////////////////////////////////////////////////
-
- #to make the map fit the different LatLng points
- adjustMapToBounds : ->
- #FIRST_STEP: retrieve all bounds
- #create the bounds object only if necessary
- if @map_options.auto_adjust or @map_options.bounds isnt null
- @boundsObject = @createLatLngBounds()
-
- #if autodjust is true, must get bounds from markers polylines etc...
- if @map_options.auto_adjust
- #from markers
- @extendBoundsWithMarkers()
-
- #from polylines:
- @updateBoundsWithPolylines()
-
- #from polygons:
- @updateBoundsWithPolygons()
-
- #from circles
- @updateBoundsWithCircles()
-
- #in every case, I've to take into account the bounds set up by the user
- @extendMapBounds()
-
- #SECOND_STEP: ajust the map to the bounds
- @adaptMapToBounds()
-
- #////////////////////////////////////////////////////
- #/////////////////// POLYLINES //////////////////////
- #////////////////////////////////////////////////////
-
- #replace old markers with new markers on an existing map
- replacePolylines : (new_polylines) ->
- #reset previous polylines and kill them from map
- @destroy_polylines()
- #set new polylines
- @polylines = new_polylines
- #create
- @create_polylines()
- #.... and adjust map boundaries
- @adjustMapToBounds()
-
- destroy_polylines : ->
- for polyline in @polylines
- #delete polylines from map
- polyline.serviceObject.setMap(null)
- #empty array
- @polylines = []
-
- #polylines is an array of arrays. It loops.
- create_polylines : ->
- for polyline in @polylines
- @create_polyline polyline
-
- #////////////////////////////////////////////////////
- #///////////////// Basic functions //////////////////
- #///////////////////tests coded//////////////////////
-
- #//basic function to check existence of a variable
- exists : (var_name) ->
- return (var_name != "" and typeof var_name != "undefined")
-
-
- #randomize
- randomize : (Lat0, Lng0) ->
- #distance in meters between 0 and max_random_distance (positive or negative)
- dx = @markers_conf.max_random_distance * @random()
- dy = @markers_conf.max_random_distance * @random()
- Lat = parseFloat(Lat0) + (180/Math.PI)*(dy/6378137)
- Lng = parseFloat(Lng0) + ( 90/Math.PI)*(dx/6378137)/Math.cos(Lat0)
- return [Lat, Lng]
-
- mergeObjectWithDefault : (object1, object2) ->
- copy_object1 = {}
- for key, value of object1
- copy_object1[key] = value
-
- for key, value of object2
- unless copy_object1[key]?
- copy_object1[key] = value
- return copy_object1
-
- mergeWithDefault : (objectName) ->
- default_object = @["default_" + objectName]
- object = @[objectName]
- @[objectName] = @mergeObjectWithDefault(object, default_object)
- return true
-
- #gives a value between -1 and 1
- random : -> return(Math.random() * 2 -1)
diff --git a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.bing.js.coffee b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.bing.js.coffee
deleted file mode 100644
index 9eb53a6b76..0000000000
--- a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.bing.js.coffee
+++ /dev/null
@@ -1,174 +0,0 @@
-######################################################################################################
-############################################## Bing Maps ##########################################
-######################################################################################################
-
-#// http://wiki.openstreetmap.org/wiki/OpenLayers
-#// http://openlayers.org/dev/examples
-#//http://docs.openlayers.org/contents.html
-
-class @Gmaps4RailsBing extends Gmaps4Rails
-
- constructor: ->
- super
- @map_options =
- type: "road" # aerial, auto, birdseye, collinsBart, mercator, ordnanceSurvey, road
- @markers_conf =
- infobox: "description" #description or htmlContent
-
- @mergeWithDefault("map_options")
- @mergeWithDefault("markers_conf")
-
- #////////////////////////////////////////////////////
- #/////////////// Basic Objects //////////////
- #////////////////////////////////////////////////////
-
- getMapType: ->
- switch @map_options.type
- when "road" then return Microsoft.Maps.MapTypeId.road
- when "aerial" then return Microsoft.Maps.MapTypeId.aerial
- when "auto" then return Microsoft.Maps.MapTypeId.auto
- when "birdseye" then return Microsoft.Maps.MapTypeId.birdseye
- when "collinsBart" then return Microsoft.Maps.MapTypeId.collinsBart
- when "mercator" then return Microsoft.Maps.MapTypeId.mercator
- when "ordnanceSurvey" then return Microsoft.Maps.MapTypeId.ordnanceSurvey
- else return Microsoft.Maps.MapTypeId.auto
-
- createPoint: (lat, lng) ->
- return new Microsoft.Maps.Point(lat, lng)
-
- createLatLng:(lat, lng) ->
- return new Microsoft.Maps.Location(lat, lng)
-
- createLatLngBounds: ->
-
- createMap: ->
- return new Microsoft.Maps.Map(document.getElementById(@map_options.id), {
- credentials: @map_options.provider_key,
- mapTypeId: @getMapType(),
- center: @createLatLng(@map_options.center_latitude, @map_options.center_longitude),
- zoom: @map_options.zoom
- })
-
- createSize: (width, height) ->
- return new google.maps.Size(width, height)
-
- #////////////////////////////////////////////////////
- #////////////////////// Markers /////////////////////
- #////////////////////////////////////////////////////
-
- createMarker: (args) ->
- markerLatLng = @createLatLng(args.Lat, args.Lng)
- marker
- #// Marker sizes are expressed as a Size of X,Y
- if args.marker_picture == ""
- marker = new Microsoft.Maps.Pushpin(@createLatLng(args.Lat, args.Lng), {
- draggable: args.marker_draggable,
- anchor: @createImageAnchorPosition(args.Lat, args.Lng),
- text: args.marker_title
- }
- );
- else
- marker = new Microsoft.Maps.Pushpin(@createLatLng(args.Lat, args.Lng), {
- draggable: args.marker_draggable,
- anchor: @createImageAnchorPosition(args.Lat, args.Lng),
- icon: args.marker_picture,
- height: args.marker_height,
- text: args.marker_title,
- width: args.marker_width
- }
- );
- @addToMap(marker)
- return marker
-
- #// clear markers
- clearMarkers: ->
- for marker in @markers
- @clearMarker marker
-
- clearMarker: (marker) ->
- @removeFromMap(marker.serviceObject)
-
- #//show and hide markers
- showMarkers: ->
- for marker in @markers
- @showMarker marker
-
- showMarker: (marker) ->
- marker.serviceObject.setOptions({ visible: true })
-
- hideMarkers: ->
- for marker in @markers
- @hideMarker marker
-
- hideMarker: (marker) ->
- marker.serviceObject.setOptions({ visible: false })
-
- extendBoundsWithMarkers: ->
- locationsArray = []
- for marker in @markers
- locationsArray.push(marker.serviceObject.getLocation())
- @boundsObject = Microsoft.Maps.LocationRect.fromLocations(locationsArray)
-
- #////////////////////////////////////////////////////
- #/////////////////// Clusterer //////////////////////
- #////////////////////////////////////////////////////
-
- createClusterer: (markers_array) ->
-
- clearClusterer: ->
-
- #//creates clusters
- clusterize: ->
-
- #////////////////////////////////////////////////////
- #/////////////////// INFO WINDOW ////////////////////
- #////////////////////////////////////////////////////
-
- #// creates infowindows
- createInfoWindow: (marker_container) ->
- if marker_container.description?
- #//create the infowindow
- if @markers_conf.infobox == "description"
- marker_container.info_window = new Microsoft.Maps.Infobox(marker_container.serviceObject.getLocation(), { description: marker_container.description, visible: false, showCloseButton: true})
- else
- marker_container.info_window = new Microsoft.Maps.Infobox(marker_container.serviceObject.getLocation(), { htmlContent: marker_container.description, visible: false})
-
- #//add the listener associated
- currentMap = this
- Microsoft.Maps.Events.addHandler(marker_container.serviceObject, 'click', @openInfoWindow(currentMap, marker_container.info_window))
- @addToMap(marker_container.info_window)
-
- openInfoWindow: (currentMap, infoWindow) ->
- return ->
- # Close the latest selected marker before opening the current one.
- if currentMap.visibleInfoWindow
- currentMap.visibleInfoWindow.setOptions({ visible: false })
- infoWindow.setOptions({ visible:true })
- currentMap.visibleInfoWindow = infoWindow
-
- #////////////////////////////////////////////////////
- #/////////////////// Other methods //////////////////
- #////////////////////////////////////////////////////
-
- fitBounds: ->
- @serviceObject.setView({bounds: @boundsObject})
-
- addToMap: (object)->
- @serviceObject.entities.push(object)
-
- removeFromMap: (object)->
- @serviceObject.entities.remove(object)
-
- centerMapOnUser: ->
- @serviceObject.setView({ center: @userLocation})
-
- updateBoundsWithPolylines: ()->
-
- updateBoundsWithPolygons: ()->
-
- updateBoundsWithCircles: ()->
-
- extendMapBounds :->
-
- adaptMapToBounds: ->
- @fitBounds()
\ No newline at end of file
diff --git a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.googlemaps.js.coffee b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.googlemaps.js.coffee
deleted file mode 100644
index ed52ddc15a..0000000000
--- a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.googlemaps.js.coffee
+++ /dev/null
@@ -1,339 +0,0 @@
-#######################################################################################################
-############################################## Google maps ##########################################
-#######################################################################################################
-
-class @Gmaps4RailsGoogle extends Gmaps4Rails
-
- constructor: ->
- super
- #Map settings
- @map_options =
- disableDefaultUI: false
- disableDoubleClickZoom: false
- type: "ROADMAP" # HYBRID, ROADMAP, SATELLITE, TERRAIN
-
- #markers + info styling
- @markers_conf =
- clusterer_gridSize: 50
- clusterer_maxZoom: 5
- custom_cluster_pictures: null
- custom_infowindow_class: null
-
- @mergeWithDefault("map_options")
- @mergeWithDefault("markers_conf")
-
- @kml_options =
- clickable: true
- preserveViewport: false
- suppressInfoWindows: false
-
- #Polygon Styling
- @polygons_conf = # default style for polygons
- strokeColor: "#FFAA00"
- strokeOpacity: 0.8
- strokeWeight: 2
- fillColor: "#000000"
- fillOpacity: 0.35
- clickable: false
-
- #Circle Styling
- @circles_conf = #default style for circles
- fillColor: "#00AAFF"
- fillOpacity: 0.35
- strokeColor: "#FFAA00"
- strokeOpacity: 0.8
- strokeWeight: 2
- clickable: false
- zIndex: null
-
- #Direction Settings
- @direction_conf =
- panel_id: null
- display_panel: false
- origin: null
- destination: null
- waypoints: [] #[{location: "toulouse,fr", stopover: true}, {location: "Clermont-Ferrand, fr", stopover: true}]
- optimizeWaypoints: false
- unitSystem: "METRIC" #IMPERIAL
- avoidHighways: false
- avoidTolls: false
- region: null
- travelMode: "DRIVING" #WALKING, BICYCLING
-
- #////////////////////////////////////////////////////
- #/////////////// Basic Objects //////////////
- #////////////////////////////////////////////////////
-
- createPoint : (lat, lng) ->
- return new google.maps.Point(lat, lng)
-
- createLatLng : (lat, lng) ->
- return new google.maps.LatLng(lat, lng)
-
- createLatLngBounds : ->
- return new google.maps.LatLngBounds()
-
- createMap : ->
- defaultOptions =
- maxZoom: @map_options.maxZoom
- minZoom: @map_options.minZoom
- zoom: @map_options.zoom
- center: @createLatLng(@map_options.center_latitude, @map_options.center_longitude)
- mapTypeId: google.maps.MapTypeId[@map_options.type]
- mapTypeControl: @map_options.mapTypeControl
- disableDefaultUI: @map_options.disableDefaultUI
- disableDoubleClickZoom: @map_options.disableDoubleClickZoom
- draggable: @map_options.draggable
-
- mergedOptions = @mergeObjectWithDefault @map_options.raw, defaultOptions
-
- return new google.maps.Map document.getElementById(@map_options.id), mergedOptions
-
-
- createMarkerImage : (markerPicture, markerSize, origin, anchor, scaledSize) ->
- return new google.maps.MarkerImage(markerPicture, markerSize, origin, anchor, scaledSize)
-
- createSize : (width, height) ->
- return new google.maps.Size(width, height)
-
- #////////////////////////////////////////////////////
- #////////////////////// Markers /////////////////////
- #////////////////////////////////////////////////////
-
- createMarker : (args) ->
- markerLatLng = @createLatLng(args.Lat, args.Lng)
- #Marker sizes are expressed as a Size of X,Y
- if args.marker_picture == "" and args.rich_marker == null
- defaultOptions = {position: markerLatLng, map: @serviceObject, title: args.marker_title, draggable: args.marker_draggable, zIndex: args.zindex}
- mergedOptions = @mergeObjectWithDefault @markers_conf.raw, defaultOptions
- return new google.maps.Marker mergedOptions
-
- if (args.rich_marker != null)
- return new RichMarker({
- position: markerLatLng
- map: @serviceObject
- draggable: args.marker_draggable
- content: args.rich_marker
- flat: if args.marker_anchor == null then false else args.marker_anchor[1]
- anchor: if args.marker_anchor == null then 0 else args.marker_anchor[0]
- zIndex: args.zindex
- })
-
- #default behavior
- #calculate MarkerImage anchor location
- imageAnchorPosition = @createImageAnchorPosition args.marker_anchor
- shadowAnchorPosition = @createImageAnchorPosition args.shadow_anchor
- #create or retrieve existing MarkerImages
- markerImage = @createOrRetrieveImage(args.marker_picture, args.marker_width, args.marker_height, imageAnchorPosition)
- shadowImage = @createOrRetrieveImage(args.shadow_picture, args.shadow_width, args.shadow_height, shadowAnchorPosition)
- defaultOptions = {position: markerLatLng, map: @serviceObject, icon: markerImage, title: args.marker_title, draggable: args.marker_draggable, shadow: shadowImage, zIndex: args.zindex}
- mergedOptions = @mergeObjectWithDefault @markers_conf.raw, defaultOptions
- return new google.maps.Marker mergedOptions
-
- #checks if obj is included in arr Array and returns the position or false
- includeMarkerImage : (arr, obj) ->
- for object, index in arr
- return index if object.url == obj
- return false
-
- #checks if MarkerImage exists before creating a new one
- #returns a MarkerImage or false if ever something wrong is passed as argument
- createOrRetrieveImage : (currentMarkerPicture, markerWidth, markerHeight, imageAnchorPosition) ->
- return null if (currentMarkerPicture == "" or currentMarkerPicture == null )
-
- test_image_index = @includeMarkerImage(@markerImages, currentMarkerPicture)
- switch test_image_index
- when false
- markerImage = @createMarkerImage(currentMarkerPicture, @createSize(markerWidth, markerHeight), null, imageAnchorPosition, null )
- @markerImages.push(markerImage)
- return markerImage
- break
- else
- return @markerImages[test_image_index] if typeof test_image_index == 'number'
- return false
-
- #clear markers
- clearMarkers : ->
- for marker in @markers
- @clearMarker marker
-
- #show and hide markers
- showMarkers : ->
- for marker in @markers
- @showMarker marker
-
- hideMarkers : ->
- for marker in @markers
- @hideMarker marker
-
- clearMarker : (marker) ->
- marker.serviceObject.setMap(null)
-
- showMarker : (marker) ->
- marker.serviceObject.setVisible(true)
-
- hideMarker : (marker) ->
- marker.serviceObject.setVisible(false)
-
- extendBoundsWithMarkers : ->
- for marker in @markers
- @boundsObject.extend(marker.serviceObject.position)
-
- #////////////////////////////////////////////////////
- #/////////////////// Clusterer //////////////////////
- #////////////////////////////////////////////////////
-
- createClusterer : (markers_array) ->
- return new MarkerClusterer( @serviceObject, markers_array, { maxZoom: @markers_conf.clusterer_maxZoom, gridSize: @markers_conf.clusterer_gridSize, styles: @customClusterer() })
-
- clearClusterer : ->
- @markerClusterer.clearMarkers()
-
- #creates clusters
- clusterize : ->
- if @markers_conf.do_clustering == true
- #first clear the existing clusterer if any
- @clearClusterer() if @markerClusterer != null
-
- markers_array = new Array
- for marker in @markers
- markers_array.push(marker.serviceObject)
-
- @markerClusterer = @createClusterer(markers_array)
-
- #////////////////////////////////////////////////////
- #/////////////////// INFO WINDOW ////////////////////
- #////////////////////////////////////////////////////
-
- #// creates infowindows
- createInfoWindow : (marker_container) ->
- if typeof(@jsTemplate) == "function" or marker_container.description?
- marker_container.description = @jsTemplate(marker_container) if typeof(@jsTemplate) == "function"
- if @markers_conf.custom_infowindow_class != null
- #creating custom infowindow
- boxText = document.createElement("div")
- boxText.setAttribute("class", @markers_conf.custom_infowindow_class) #to customize
- boxText.innerHTML = marker_container.description
- marker_container.infowindow = new InfoBox(@infobox(boxText))
- currentMap = this
- google.maps.event.addListener(marker_container.serviceObject, 'click', @openInfoWindow(currentMap, marker_container.infowindow, marker_container.serviceObject))
- else
- #create default infowindow
- marker_container.infowindow = new google.maps.InfoWindow({content: marker_container.description })
- #add the listener associated
- currentMap = this
- google.maps.event.addListener(marker_container.serviceObject, 'click', @openInfoWindow(currentMap, marker_container.infowindow, marker_container.serviceObject))
-
- openInfoWindow : (currentMap, infoWindow, marker) ->
- return ->
- # Close the latest selected marker before opening the current one.
- currentMap.visibleInfoWindow.close() if currentMap.visibleInfoWindow != null
- infoWindow.open(currentMap.serviceObject, marker)
- currentMap.visibleInfoWindow = infoWindow
-
- #////////////////////////////////////////////////////
- #///////////////// KML //////////////////
- #////////////////////////////////////////////////////
-
- createKmlLayer : (kml) ->
- kml_options = kml.options || {}
- kml_options = @mergeObjectWithDefault(kml_options, @kml_options)
- kml = new google.maps.KmlLayer( kml.url, kml_options)
- kml.setMap(@serviceObject)
- return kml
-
- #////////////////////////////////////////////////////
- #/////////////////// POLYLINES //////////////////////
- #////////////////////////////////////////////////////
-
- #creates a single polyline, triggered by create_polylines
- create_polyline : (polyline) ->
- polyline_coordinates = []
-
- #2 cases here, either we have a coded array of LatLng or we have an Array of LatLng
- for element in polyline
- #if we have a coded array
- if element.coded_array?
- decoded_array = new google.maps.geometry.encoding.decodePath(element.coded_array)
- #loop through every point in the array
- for point in decoded_array
- polyline_coordinates.push(point)
-
- #or we have an array of latlng
- else
- #by convention, a single polyline could be customized in the first array or it uses default values
- if element == polyline[0]
- strokeColor = element.strokeColor || @polylines_conf.strokeColor
- strokeOpacity = element.strokeOpacity || @polylines_conf.strokeOpacity
- strokeWeight = element.strokeWeight || @polylines_conf.strokeWeight
- clickable = element.clickable || @polylines_conf.clickable
- zIndex = element.zIndex || @polylines_conf.zIndex
-
- #add latlng if positions provided
- if element.lat? && element.lng?
- latlng = @createLatLng(element.lat, element.lng)
- polyline_coordinates.push(latlng)
-
- # Construct the polyline
- new_poly = new google.maps.Polyline
- path: polyline_coordinates
- strokeColor: strokeColor
- strokeOpacity: strokeOpacity
- strokeWeight: strokeWeight
- clickable: clickable
- zIndex: zIndex
-
- #save polyline
- polyline.serviceObject = new_poly
- new_poly.setMap(@serviceObject)
-
-
- updateBoundsWithPolylines: ()->
- for polyline in @polylines
- polyline_points = polyline.serviceObject.latLngs.getArray()[0].getArray()
- for point in polyline_points
- @boundsObject.extend point
-
- #////////////////////////////////////////////////////
- #///////////////// KML //////////////////
- #////////////////////////////////////////////////////
-
- create_kml : ->
- for kml in @kml
- kml.serviceObject = @createKmlLayer kml
-
- #////////////////////////////////////////////////////
- #/////////////////// Other methods //////////////////
- #////////////////////////////////////////////////////
-
- fitBounds : ->
- @serviceObject.fitBounds(@boundsObject) unless @boundsObject.isEmpty()
-
- centerMapOnUser : ->
- @serviceObject.setCenter(@userLocation)
-
- updateBoundsWithPolygons: ()->
- for polygon in @polygons
- polygon_points = polygon.serviceObject.latLngs.getArray()[0].getArray()
- for point in polygon_points
- @boundsObject.extend point
-
- updateBoundsWithCircles: ()->
- for circle in @circles
- @boundsObject.extend(circle.serviceObject.getBounds().getNorthEast())
- @boundsObject.extend(circle.serviceObject.getBounds().getSouthWest())
-
- extendMapBounds: ()->
- for bound in @map_options.bounds
- #create points from bounds provided
- @boundsObject.extend @createLatLng(bound.lat, bound.lng)
-
- adaptMapToBounds:()->
- #if autozoom is false, take user info into account
- if !@map_options.auto_zoom
- map_center = @boundsObject.getCenter()
- @map_options.center_latitude = map_center.lat()
- @map_options.center_longitude = map_center.lng()
- @serviceObject.setCenter(map_center)
- else
- @fitBounds()
diff --git a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.mapquest.js.coffee b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.mapquest.js.coffee
deleted file mode 100644
index 08fca694d0..0000000000
--- a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.mapquest.js.coffee
+++ /dev/null
@@ -1,145 +0,0 @@
-#######################################################################################################
-############################################## Map Quest #############################################
-#######################################################################################################
-# http://developer.mapquest.com/web/documentation/sdk/javascript/v7.0/api/MQA.Poi.html
-
-class @Gmaps4RailsMapquest extends Gmaps4Rails
-
- constructor: ->
- super
- #Map settings
- @map_options = {type: "map"} #map type (map, sat, hyb)
- @markers_conf = {}
- @mergeWithDefault "markers_conf"
- @mergeWithDefault "map_options"
-
- #////////////////////////////////////////////////////
- #/////////////// Basic Objects //////////////
- #////////////////////////////////////////////////////
-
- createPoint: (lat, lng) ->
- return new MQA.Poi({lat: lat, lng: lng})
-
- createLatLng: (lat, lng) ->
- return {lat: lat, lng: lng}
-
- createLatLngBounds: ->
-
- createMap: ->
- map = new MQA.TileMap( #// Constructs an instance of MQA.TileMap
- document.getElementById(@map_options.id), #//the id of the element on the page you want the map to be added into
- @map_options.zoom, #//intial zoom level of the map
- {lat: @map_options.center_latitude, lng: @map_options.center_longitude},
- @map_options.type) #//map type (map, sat, hyb)
-
- MQA.withModule('zoomcontrol3', (->
- map.addControl(
- new MQA.LargeZoomControl3(),
- new MQA.MapCornerPlacement(MQA.MapCorner.TOP_LEFT)
- )
- ))
- return map
-
- createMarkerImage: (markerPicture, markerSize, origin, anchor, scaledSize) ->
-
- #////////////////////////////////////////////////////
- #////////////////////// Markers /////////////////////
- #////////////////////////////////////////////////////
-
- createMarker: (args)->
- marker = new MQA.Poi( {lat: args.Lat, lng: args.Lng} )
-
- if args.marker_picture != ""
- icon = new MQA.Icon(args.marker_picture, args.marker_height, args.marker_width)
- marker.setIcon(icon)
- if args.marker_anchor != null
- marker.setBias({x: args.marker_anchor[0], y: args.marker_anchor[1]})
-
- if args.shadow_picture != ""
- icon = new MQA.Icon(args.shadow_picture, args.shadow_height, args.shadow_width)
- marker.setShadow(icon)
-
- if args.shadow_anchor != null
- marker.setShadowOffset({x: args.shadow_anchor[0], y: args.shadow_anchor[1]})
-
- @addToMap marker
- return marker
-
-
- #// clear markers
- clearMarkers: ->
- for marker in markers
- @clearMarker marker
-
- #//show and hide markers
- showMarkers: ->
- for marker in markers
- @showMarker marker
-
- hideMarkers: ->
- for marker in markers
- @hideMarker marker
-
- clearMarker: (marker) ->
- @removeFromMap(marker.serviceObject)
-
- showMarker: (marker) ->
- #// marker.serviceObject
-
- hideMarker: (marker) ->
- #// marker.serviceObject
-
- extendBoundsWithMarkers: ->
- if @markers.length >=2
- @boundsObject = new MQA.RectLL(@markers[0].serviceObject.latLng, @markers[1].serviceObject.latLng)
- for marker in @markers
- @boundsObject.extend marker.serviceObject.latLng
-
- #////////////////////////////////////////////////////
- #/////////////////// Clusterer //////////////////////
- #////////////////////////////////////////////////////
-
- createClusterer: (markers_array) ->
-
- clearClusterer: ->
-
- #//creates clusters
- clusterize: ->
-
- #////////////////////////////////////////////////////
- #/////////////////// INFO WINDOW ////////////////////
- #////////////////////////////////////////////////////
-
- #// creates infowindows
- createInfoWindow: (marker_container) ->
- marker_container.serviceObject.setInfoTitleHTML(marker_container.description)
- #//TODO: how to disable the mouseover display when using setInfoContentHTML?
- #//marker_container.serviceObject.setInfoContentHTML(marker_container.description);
-
- #////////////////////////////////////////////////////
- #/////////////////// Other methods //////////////////
- #////////////////////////////////////////////////////
-
- fitBounds: ->
- @serviceObject.zoomToRect @boundsObject if @markers.length >=2
- @serviceObject.setCenter @markers[0].serviceObject.latLng if @markers.length == 1
-
- centerMapOnUser: ->
- @serviceObject.setCenter @userLocation
-
- addToMap: (object) ->
- @serviceObject.addShape object
-
- removeFromMap: (object)->
- @serviceObject.removeShape object
-
- updateBoundsWithPolylines: ()->
-
- updateBoundsWithPolygons: ()->
-
- updateBoundsWithCircles: ()->
-
- extendMapBounds :->
-
- adaptMapToBounds: ->
- @fitBounds()
\ No newline at end of file
diff --git a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.openlayers.js.coffee b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.openlayers.js.coffee
deleted file mode 100644
index 1cddc04e39..0000000000
--- a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.openlayers.js.coffee
+++ /dev/null
@@ -1,261 +0,0 @@
-#######################################################################################################
-############################################## Open Layers ##########################################
-#######################################################################################################
-
-#// http://wiki.openstreetmap.org/wiki/OpenLayers
-#// http://openlayers.org/dev/examples
-#//http://docs.openlayers.org/contents.html
-
-class @Gmaps4RailsOpenlayers extends Gmaps4Rails
-
- constructor: ->
- super
- @map_options = {}
- @mergeWithDefault "map_options"
- @markers_conf = {}
- @mergeWithDefault "markers_conf"
-
- @openMarkers = null
- @markersLayer = null
- @markersControl = null
- @polylinesLayer = null
-
- #////////////////////////////////////////////////////
- #/////////////// Basic Objects ////////////////////
- #////////////////////////////////////////////////////
-
- createPoint: (lat, lng)->
-
- createLatLng: (lat, lng)->
- return new OpenLayers.LonLat(lng, lat).transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913")) # transform from WGS 1984 to Spherical Mercator Projection
-
- createAnchor: (offset)->
- return null if offset == null
- return new OpenLayers.Pixel(offset[0], offset[1])
-
- createSize: (width, height)->
- return new OpenLayers.Size(width, height)
-
- createLatLngBounds: ->
- return new OpenLayers.Bounds()
-
- createMap: ->
- #//todo add customization: kind of map and other map options
- map = new OpenLayers.Map(@map_options.id)
- map.addLayer(new OpenLayers.Layer.OSM())
- map.setCenter(@createLatLng(@map_options.center_latitude, @map_options.center_longitude), #// Center of the map
- @map_options.zoom) #// Zoom level
- return map
-
- #////////////////////////////////////////////////////
- #////////////////////// Markers /////////////////////
- #////////////////////////////////////////////////////
- #//http://openlayers.org/dev/examples/marker-shadow.html
- createMarker: (args) ->
- style_mark = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style['default'])
- style_mark.fillOpacity = 1
-
- #//creating markers' dedicated layer
- if (@markersLayer == null)
- @markersLayer = new OpenLayers.Layer.Vector("Markers", null)
- @serviceObject.addLayer(@markersLayer)
- #//TODO move?
- @markersLayer.events.register("featureselected", @markersLayer, @onFeatureSelect)
- @markersLayer.events.register("featureunselected", @markersLayer, @onFeatureUnselect)
- @markersControl = new OpenLayers.Control.SelectFeature(@markersLayer)
- @serviceObject.addControl(@markersControl)
- @markersControl.activate()
- #//showing default pic if none available
- if args.marker_picture == ""
- #style_mark.graphicWidth = 24
- style_mark.graphicHeight = 30
- style_mark.externalGraphic = "http://openlayers.org/dev/img/marker-blue.png"
- #//creating custom pic
- else
- style_mark.graphicWidth = args.marker_width
- style_mark.graphicHeight = args.marker_height
- style_mark.externalGraphic = args.marker_picture
- #//adding anchor if any
- if args.marker_anchor != null
- style_mark.graphicXOffset = args.marker_anchor[0]
- style_mark.graphicYOffset = args.marker_anchor[1]
- #//adding shadow if any
- if args.shadow_picture != ""
- style_mark.backgroundGraphic = args.shadow_picture
- style_mark.backgroundWidth = args.shadow_width
- style_mark.backgroundHeight = args.shadow_height
- #//adding shadow's anchor if any
- if args.shadow_anchor != null
- style_mark.backgroundXOffset = args.shadow_anchor[0]
- style_mark.backgroundYOffset = args.shadow_anchor[1]
-
- style_mark.graphicTitle = args.marker_title
- marker = new OpenLayers.Feature.Vector(
- new OpenLayers.Geometry.Point(args.Lng, args.Lat),
- null,
- style_mark)
- #//changing coordinates so that it actually appears on the map!
- marker.geometry.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913"))
- #//adding layer to the map
- @markersLayer.addFeatures([marker])
-
- return marker
-
- #//clear markers
- clearMarkers: ->
- @clearMarkersLayerIfExists()
- @markersLayer = null
- @boundsObject = new OpenLayers.Bounds()
-
- clearMarkersLayerIfExists: ->
- @serviceObject.removeLayer(@markersLayer) if @markersLayer != null and @serviceObject.getLayer(@markersLayer.id) != null
-
- extendBoundsWithMarkers: ->
- console.log "here"
- for marker in @markers
- @boundsObject.extend(@createLatLng(marker.lat,marker.lng))
-
- #////////////////////////////////////////////////////
- #/////////////////// Clusterer //////////////////////
- #////////////////////////////////////////////////////
- #//too ugly to be considered valid :(
-
- createClusterer: (markers_array)->
- options =
- pointRadius: "${radius}"
- fillColor: "#ffcc66"
- fillOpacity: 0.8
- strokeColor: "#cc6633"
- strokeWidth: "${width}"
- strokeOpacity: 0.8
- funcs =
- context:
- width: (feature) ->
- return (feature.cluster) ? 2 : 1
- radius: (feature) ->
- pix = 2
- pix = Math.min(feature.attributes.count, 7) + 2 if feature.cluster
- return pix
-
- style = new OpenLayers.Style options, funcs
-
- strategy = new OpenLayers.Strategy.Cluster()
-
- clusters = new OpenLayers.Layer.Vector "Clusters",
- strategies: [strategy]
- styleMap: new OpenLayers.StyleMap
- "default": style
- "select":
- fillColor: "#8aeeef"
- strokeColor: "#32a8a9"
-
- @clearMarkersLayerIfExists()
- @serviceObject.addLayer(clusters)
- clusters.addFeatures(markers_array)
- return clusters
-
- clusterize: ->
-
- if @markers_conf.do_clustering == true
- #//first clear the existing clusterer if any
- if @markerClusterer != null
- @clearClusterer()
- markers_array = new Array
- for marker in @markers
- markers_array.push(marker.serviceObject)
- @markerClusterer = @createClusterer markers_array
-
- clearClusterer: ->
- @serviceObject.removeLayer @markerClusterer
-
- #////////////////////////////////////////////////////
- #/////////////////// INFO WINDOW ////////////////////
- #////////////////////////////////////////////////////
-
- #// creates infowindows
- createInfoWindow: (marker_container) ->
- marker_container.serviceObject.infoWindow = marker_container.description if marker_container.description?
-
- onPopupClose: (evt) ->
- #// 'this' is the popup.
- @markersControl.unselect @feature
-
- onFeatureSelect: (evt) ->
- feature = evt.feature
- popup = new OpenLayers.Popup.FramedCloud("featurePopup",
- feature.geometry.getBounds().getCenterLonLat(),
- new OpenLayers.Size(300,200),
- feature.infoWindow,
- null, true, @onPopupClose)
- feature.popup = popup
- popup.feature = feature
- @map.addPopup popup
-
- onFeatureUnselect: (evt) ->
- feature = evt.feature
- if feature.popup
- #//popup.feature = null;
- @map.removePopup feature.popup
- feature.popup.destroy()
- feature.popup = null
-
- #////////////////////////////////////////////////////
- #/////////////////// POLYLINES //////////////////////
- #////////////////////////////////////////////////////
-
- create_polyline : (polyline) ->
-
- if(@polylinesLayer == null)
- @polylinesLayer = new OpenLayers.Layer.Vector("Polylines", null)
- @serviceObject.addLayer(@polylinesLayer)
- @polylinesLayer.events.register("featureselected", @polylinesLayer, @onFeatureSelect)
- @polylinesLayer.events.register("featureunselected", @polylinesLayer, @onFeatureUnselect)
- @polylinesControl = new OpenLayers.Control.DrawFeature(@polylinesLayer, OpenLayers.Handler.Path)
- @serviceObject.addControl(@polylinesControl)
-
- polyline_coordinates = []
-
- for element in polyline
- #by convention, a single polyline could be customized in the first array or it uses default values
- if element == polyline[0]
- strokeColor = element.strokeColor || @polylines_conf.strokeColor
- strokeOpacity = element.strokeOpacity || @polylines_conf.strokeOpacity
- strokeWeight = element.strokeWeight || @polylines_conf.strokeWeight
- clickable = element.clickable || @polylines_conf.clickable
- zIndex = element.zIndex || @polylines_conf.zIndex
-
- #add latlng if positions provided
- if element.lat? && element.lng?
- latlng = new OpenLayers.Geometry.Point(element.lng, element.lat)
- polyline_coordinates.push(latlng)
-
- line_points = new OpenLayers.Geometry.LineString(polyline_coordinates);
- line_style = { strokeColor: strokeColor, strokeOpacity: strokeOpacity, strokeWidth: strokeWeight };
-
- polyline = new OpenLayers.Feature.Vector(line_points, null, line_style);
- polyline.geometry.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913"))
-
- @polylinesLayer.addFeatures([polyline])
-
- return polyline
-
- updateBoundsWithPolylines: ()->
-
- updateBoundsWithPolygons: ()->
-
- updateBoundsWithCircles: ()->
-
- # #////////////////////////////////////////////////////
- # #/////////////////// Other methods //////////////////
- # #////////////////////////////////////////////////////
-
- fitBounds: ->
- @serviceObject.zoomToExtent(@boundsObject, true)
-
- centerMapOnUser: ->
- @serviceObject.setCenter @userLocation
-
- extendMapBounds :->
-
- adaptMapToBounds: ->
- @fitBounds()
diff --git a/app/assets/javascripts/search/jquery.backstretch.js b/app/assets/javascripts/search/jquery.backstretch.js
deleted file mode 100644
index 4cb7175e99..0000000000
--- a/app/assets/javascripts/search/jquery.backstretch.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/*! Backstretch - v2.0.4 - 2013-06-19
- * http://srobbin.com/jquery-plugins/backstretch/
- * Copyright (c) 2013 Scott Robbin; Licensed MIT */
-(function(a,d,p){a.fn.backstretch=function(c,b){(c===p||0===c.length)&&a.error("No images were supplied for Backstretch");0===a(d).scrollTop()&&d.scrollTo(0,0);return this.each(function(){var d=a(this),g=d.data("backstretch");if(g){if("string"==typeof c&&"function"==typeof g[c]){g[c](b);return}b=a.extend(g.options,b);g.destroy(!0)}g=new q(this,c,b);d.data("backstretch",g)})};a.backstretch=function(c,b){return a("body").backstretch(c,b).data("backstretch")};a.expr[":"].backstretch=function(c){return a(c).data("backstretch")!==p};a.fn.backstretch.defaults={centeredX:!0,centeredY:!0,duration:5E3,fade:0};var r={left:0,top:0,overflow:"hidden",margin:0,padding:0,height:"100%",width:"100%",zIndex:-999999},s={position:"absolute",display:"none",margin:0,padding:0,border:"none",width:"auto",height:"auto",maxHeight:"none",maxWidth:"none",zIndex:-999999},q=function(c,b,e){this.options=a.extend({},a.fn.backstretch.defaults,e||{});this.images=a.isArray(b)?b:[b];a.each(this.images,function(){a("")[0].src=this});this.isBody=c===document.body;this.$container=a(c);this.$root=this.isBody?l?a(d):a(document):this.$container;c=this.$container.children(".backstretch").first();this.$wrap=c.length?c:a('
Hello, world!
This is a paragraph.
' email 'enterprise@example.com' address { FactoryGirl.create(:address) } + confirmed_at { Time.now } end factory :supplier_enterprise, :parent => :enterprise do is_primary_producer true - is_distributor false + sells "none" end factory :distributor_enterprise, :parent => :enterprise do is_primary_producer false - is_distributor true + sells "any" end factory :enterprise_relationship do diff --git a/spec/features/admin/bulk_product_update_spec.rb b/spec/features/admin/bulk_product_update_spec.rb index d47c699c8f..8bb54cfca8 100644 --- a/spec/features/admin/bulk_product_update_spec.rb +++ b/spec/features/admin/bulk_product_update_spec.rb @@ -6,7 +6,7 @@ feature %q{ } , js: true do include AuthenticationWorkflow include WebHelper - + describe "listing products" do before :each do login_to_admin_section @@ -69,7 +69,7 @@ feature %q{ expect(page).to have_field "price", with: "44.0" expect(page).to have_no_field "price", with: "66.0", visible: true end - + it "displays an on hand count input for each product (ie. for master variant) if no regular variants exist" do p1 = FactoryGirl.create(:product) p2 = FactoryGirl.create(:product) @@ -84,7 +84,7 @@ feature %q{ expect(page).to have_field "on_hand", with: "15" expect(page).to have_field "on_hand", with: "12" end - + it "displays an on hand count in a span for each product (ie. for master variant) if other variants exist" do p1 = FactoryGirl.create(:product) p2 = FactoryGirl.create(:product) @@ -142,7 +142,7 @@ feature %q{ expect(page).to have_field "variant_unit_name", with: "packet" end end - + describe "listing variants" do before :each do login_to_admin_section @@ -174,8 +174,8 @@ feature %q{ expect(page).to have_field "variant_on_hand", with: "15" expect(page).to have_field "variant_on_hand", with: "6" end - - + + it "displays a price input (for each variant) for each product" do p1 = FactoryGirl.create(:product, price: 2.0) v1 = FactoryGirl.create(:variant, product: p1, is_master: false, price: 12.75) @@ -295,7 +295,7 @@ feature %q{ login_to_admin_section visit '/admin/products/bulk_edit' - + first("div#columns_dropdown", :text => "COLUMNS").click first("div#columns_dropdown div.menu div.menu_item", text: "Available On").click first("div#columns_dropdown div.menu div.menu_item", text: "Category").click @@ -311,7 +311,7 @@ feature %q{ fill_in "product_name", with: "Big Bag Of Potatoes" select s2.name, :from => 'producer' - fill_in "available_on", with: (Date.today-3).strftime("%F %T") + fill_in "available_on", with: (3.days.ago.beginning_of_day).strftime("%F %T") fill_in "price", with: "20" select "Weight (kg)", from: "variant_unit_with_scale" select2_select t1.name, from: "p#{p.id}_category" @@ -333,7 +333,7 @@ feature %q{ expect(p.on_hand).to eq 18 expect(p.primary_taxon).to eq t1 end - + scenario "updating a product with a variant unit of 'items'" do p = FactoryGirl.create(:product, variant_unit: 'weight', variant_unit_scale: 1000) @@ -589,7 +589,7 @@ feature %q{ within "tr#v_#{v1.id}" do first("a.delete-variant").click end - + expect(page).to have_selector "a.delete-variant", :count => 2 visit '/admin/products/bulk_edit' @@ -672,7 +672,7 @@ feature %q{ login_to_admin_section visit '/admin/products/bulk_edit' - + first("div#columns_dropdown", :text => "COLUMNS").click first("div#columns_dropdown div.menu div.menu_item", text: "Available On").click @@ -814,7 +814,7 @@ feature %q{ fill_in "product_name", with: "Big Bag Of Potatoes" select(supplier_managed2.name, :from => 'producer') - fill_in "available_on", with: (Date.today-3).strftime("%F %T") + fill_in "available_on", with: (3.days.ago.beginning_of_day).strftime("%F %T") fill_in "price", with: "20" select "Weight (kg)", from: "variant_unit_with_scale" fill_in "on_hand", with: "18" diff --git a/spec/features/admin/enterprise_relationships_spec.rb b/spec/features/admin/enterprise_relationships_spec.rb index bfc624bce5..5e4366b6f7 100644 --- a/spec/features/admin/enterprise_relationships_spec.rb +++ b/spec/features/admin/enterprise_relationships_spec.rb @@ -89,7 +89,7 @@ feature %q{ let!(:d1) { create(:distributor_enterprise) } let!(:d2) { create(:distributor_enterprise) } let!(:d3) { create(:distributor_enterprise) } - let(:enterprise_user) { create_enterprise_user([d1]) } + let(:enterprise_user) { create_enterprise_user( enterprises: [d1] ) } let!(:er1) { create(:enterprise_relationship, parent: d1, child: d2) } let!(:er2) { create(:enterprise_relationship, parent: d2, child: d1) } diff --git a/spec/features/admin/enterprise_user_spec.rb b/spec/features/admin/enterprise_user_spec.rb index 77378f6cbd..232ffb68ae 100644 --- a/spec/features/admin/enterprise_user_spec.rb +++ b/spec/features/admin/enterprise_user_spec.rb @@ -11,10 +11,10 @@ feature %q{ let!(:user) { create_enterprise_user } let!(:supplier1) { create(:supplier_enterprise, name: 'Supplier 1') } let!(:supplier2) { create(:supplier_enterprise, name: 'Supplier 2') } - let(:supplier_profile) { create(:supplier_enterprise, name: 'Supplier profile', type: 'profile') } + let(:supplier_profile) { create(:supplier_enterprise, name: 'Supplier profile', sells: 'none') } let!(:distributor1) { create(:distributor_enterprise, name: 'Distributor 3') } let!(:distributor2) { create(:distributor_enterprise, name: 'Distributor 4') } - let(:distributor_profile) { create(:distributor_enterprise, name: 'Distributor profile', type: 'profile') } + let(:distributor_profile) { create(:distributor_enterprise, name: 'Distributor profile', sells: 'none') } describe "creating an enterprise user" do context "with a limitted number of owned enterprises" do @@ -53,7 +53,10 @@ feature %q{ end end - describe "with only a profile-level enterprise" do + # This case no longer exists as anyone with an enterprise can supply into the system. + # Or can they?? There is no producer profile anyway. + # TODO discuss what parts of this are still necessary in which cases. + pending "with only a profile-level enterprise" do before do user.enterprise_roles.create! enterprise: supplier_profile user.enterprise_roles.create! enterprise: distributor_profile @@ -64,7 +67,7 @@ feature %q{ page.should have_admin_menu_item 'Dashboard' page.should have_admin_menu_item 'Enterprises' - ['Orders', 'Products', 'Reports', 'Configuration', 'Promotions', 'Users', 'Order Cycles'].each do |menu_item_name| + ['Orders', 'Reports', 'Configuration', 'Promotions', 'Users', 'Order Cycles'].each do |menu_item_name| page.should_not have_admin_menu_item menu_item_name end end @@ -79,15 +82,15 @@ feature %q{ end end - it "does not show me product management controls" do - page.should_not have_selector '#products' + it "shows me product management controls, but not order_cycle controls" do + page.should have_selector '#products' page.should_not have_selector '#order_cycles' end - it "does not show me enterprise product info, payment methods, shipping methods or enterprise fees" do + it "shows me enterprise product info but not payment methods, shipping methods or enterprise fees" do # Producer product info - page.should_not have_selector '.producers_tab span', text: 'Total Products' - page.should_not have_selector '.producers_tab span', text: 'Active Products' + page.should have_selector '.producers_tab span', text: 'Total Products' + page.should have_selector '.producers_tab span', text: 'Active Products' page.should_not have_selector '.producers_tab span', text: 'Products in OCs' # Payment methods, shipping methods, enterprise fees diff --git a/spec/features/admin/enterprises_spec.rb b/spec/features/admin/enterprises_spec.rb index abaab5cd2d..06b1fd31a6 100644 --- a/spec/features/admin/enterprises_spec.rb +++ b/spec/features/admin/enterprises_spec.rb @@ -16,7 +16,7 @@ feature %q{ within("tr.enterprise-#{s.id}") do expect(page).to have_content s.name - expect(page).to have_select "enterprise_set_collection_attributes_1_type" + expect(page).to have_select "enterprise_set_collection_attributes_1_sells" expect(page).to have_content "Edit Profile" expect(page).to have_content "Delete" expect(page).to_not have_content "Payment Methods" @@ -26,7 +26,7 @@ feature %q{ within("tr.enterprise-#{d.id}") do expect(page).to have_content d.name - expect(page).to have_select "enterprise_set_collection_attributes_0_type" + expect(page).to have_select "enterprise_set_collection_attributes_0_sells" expect(page).to have_content "Edit Profile" expect(page).to have_content "Delete" expect(page).to have_content "Payment Methods" @@ -37,7 +37,7 @@ feature %q{ scenario "editing enterprises in bulk" do s = create(:supplier_enterprise) - d = create(:distributor_enterprise, type: 'profile') + d = create(:distributor_enterprise, sells: 'none') d_manager = create_enterprise_user d_manager.enterprise_roles.build(enterprise: d).save expect(d.owner).to_not eq d_manager @@ -48,14 +48,14 @@ feature %q{ within("tr.enterprise-#{d.id}") do expect(page).to have_checked_field "enterprise_set_collection_attributes_0_visible" uncheck "enterprise_set_collection_attributes_0_visible" - select 'full', from: "enterprise_set_collection_attributes_0_type" + select 'any', from: "enterprise_set_collection_attributes_0_sells" select d_manager.email, from: 'enterprise_set_collection_attributes_0_owner_id' end click_button "Update" flash_message.should == 'Enterprises updated successfully' distributor = Enterprise.find(d.id) expect(distributor.visible).to eq false - expect(distributor.type).to eq 'full' + expect(distributor.sells).to eq 'any' expect(distributor.owner).to eq d_manager end @@ -82,15 +82,16 @@ feature %q{ click_link 'New Enterprise' # Checking shipping and payment method sidebars work + choose "Any" uncheck 'enterprise_is_primary_producer' - check 'enterprise_is_distributor' + page.should_not have_checked_field "enterprise_payment_method_ids_#{payment_method.id}" page.should_not have_checked_field "enterprise_shipping_method_ids_#{shipping_method.id}" # Filling in details fill_in 'enterprise_name', :with => 'Eaterprises' select2_search admin.email, from: 'Owner' - choose 'Full' + choose 'Any' check "enterprise_payment_method_ids_#{payment_method.id}" check "enterprise_shipping_method_ids_#{shipping_method.id}" select2_search eg1.name, from: 'Groups' @@ -109,8 +110,8 @@ feature %q{ fill_in 'enterprise_address_attributes_zipcode', :with => '3072' select2_search 'Australia', :from => 'Country' select2_search 'Victoria', :from => 'State' - fill_in 'enterprise_description', :with => 'Connecting farmers and eaters' - fill_in 'enterprise_long_description', :with => 'Zombie ipsum reversus ab viral inferno, nam rick grimes malum cerebro.' + long_description = find :css, "text-angular div.ta-scroll-window div.ta-bind" + long_description.set 'Connecting farmers and eaters' click_button 'Create' flash_message.should == 'Enterprise "Eaterprises" has been successfully created!' @@ -134,21 +135,31 @@ feature %q{ end fill_in 'enterprise_name', :with => 'Eaterprises' - choose 'Single' + choose 'Own' select2_search user.email, from: 'Owner' fill_in 'enterprise_description', :with => 'Connecting farmers and eaters' - fill_in 'enterprise_long_description', :with => 'Zombie ipsum reversus ab viral inferno, nam rick grimes malum cerebro.' + long_description = find :css, "text-angular div.ta-scroll-window div.ta-bind" + long_description.set 'This is an interesting long description' # Check Angularjs switching of sidebar elements uncheck 'enterprise_is_primary_producer' - uncheck 'enterprise_is_distributor' + choose 'None' + page.should have_selector "#enterprise_fees", visible: false page.should have_selector "#payment_methods", visible: false page.should have_selector "#shipping_methods", visible: false - page.should have_selector "#enterprise_fees", visible: false - check 'enterprise_is_distributor' + check 'enterprise_is_primary_producer' + page.should have_selector "#enterprise_fees" + page.should have_selector "#payment_methods", visible: false + page.should have_selector "#shipping_methods", visible: false + uncheck 'enterprise_is_primary_producer' + choose 'Own' + page.should have_selector "#enterprise_fees" page.should have_selector "#payment_methods" page.should have_selector "#shipping_methods" + choose 'Any' page.should have_selector "#enterprise_fees" + page.should have_selector "#payment_methods" + page.should have_selector "#shipping_methods" select2_search eg1.name, from: 'Groups' @@ -182,6 +193,7 @@ feature %q{ page.should have_checked_field "enterprise_payment_method_ids_#{payment_method.id}" page.should have_checked_field "enterprise_shipping_method_ids_#{shipping_method.id}" page.should have_selector "a.list-item", text: enterprise_fee.name + page.should have_content 'This is an interesting long description' end describe "producer properties" do @@ -275,16 +287,14 @@ feature %q{ within("tr.enterprise-#{distributor1.id}") do expect(page).to have_content distributor1.name - expect(page).to have_checked_field "enterprise_set_collection_attributes_0_is_distributor" expect(page).to have_unchecked_field "enterprise_set_collection_attributes_0_is_primary_producer" - expect(page).to_not have_select "enterprise_set_collection_attributes_0_type" + expect(page).to_not have_select "enterprise_set_collection_attributes_0_sells" end within("tr.enterprise-#{supplier1.id}") do expect(page).to have_content supplier1.name - expect(page).to have_unchecked_field "enterprise_set_collection_attributes_1_is_distributor" expect(page).to have_checked_field "enterprise_set_collection_attributes_1_is_primary_producer" - expect(page).to_not have_select "enterprise_set_collection_attributes_1_type" + expect(page).to_not have_select "enterprise_set_collection_attributes_1_sells" end expect(page).to_not have_content "supplier2.name" @@ -312,6 +322,7 @@ feature %q{ click_link 'Enterprises' click_link 'New Enterprise' fill_in 'enterprise_name', with: 'zzz' + fill_in 'enterprise_email', with: 'bob@example.com' fill_in 'enterprise_address_attributes_address1', with: 'z' fill_in 'enterprise_address_attributes_city', with: 'z' fill_in 'enterprise_address_attributes_zipcode', with: 'z' diff --git a/spec/features/admin/image_settings_spec.rb b/spec/features/admin/image_settings_spec.rb new file mode 100644 index 0000000000..a83a072235 --- /dev/null +++ b/spec/features/admin/image_settings_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +feature %q{ + As an admin + I want to manage image formats +} do + include AuthenticationWorkflow + include WebHelper + + before(:all) do + styles = {"mini" => "48x48>", + "small" => "100x100>", + "product" => "240x240>", + "large" => "600x600>"} + + Spree::Config[:attachment_styles] = ActiveSupport::JSON.encode(styles) + Spree::Image.attachment_definitions[:attachment][:styles] = ActiveSupport::JSON.decode(Spree::Config[:attachment_styles]) + Spree::Image.reformat_styles + end + + scenario "setting the image format for a paperclip style" do + # When I go to the image settings page + login_to_admin_section + visit spree.edit_admin_image_settings_path + + # All the styles should default to "Unchanged" + page.should have_select 'attachment_styles_format_mini', selected: 'Unchanged' + page.should have_select 'attachment_styles_format_small', selected: 'Unchanged' + page.should have_select 'attachment_styles_format_product', selected: 'Unchanged' + page.should have_select 'attachment_styles_format_large', selected: 'Unchanged' + + # When I change a style to "PNG" and save + select 'PNG', from: 'attachment_styles_format_mini' + click_button 'Update' + + # Then the change should be saved to the image formats + page.should have_content "Image Settings successfully updated." + page.should have_select 'attachment_styles_format_mini', selected: 'PNG' + + styles = Spree::Image.attachment_definitions[:attachment][:styles] + styles[:mini].should == ['48x48>', :png] + end +end diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index febd966109..be91d442e1 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -471,6 +471,10 @@ feature %q{ # I should see only the order cycle I am coordinating page.should have_content oc_user_coordinating.name page.should_not have_content oc_for_other_user.name + + # The order cycle should show enterprises that I manage + page.should have_selector 'td.suppliers', text: supplier_managed.name + page.should have_selector 'td.distributors', text: distributor_managed.name # The order cycle should not show enterprises that I don't manage page.should_not have_selector 'td.suppliers', text: supplier_unmanaged.name diff --git a/spec/features/admin/orders_spec.rb b/spec/features/admin/orders_spec.rb index dd4a2113e8..908ce4042e 100644 --- a/spec/features/admin/orders_spec.rb +++ b/spec/features/admin/orders_spec.rb @@ -110,10 +110,10 @@ feature %q{ let(:coordinator2) { create(:distributor_enterprise) } let!(:order_cycle1) { create(:order_cycle, coordinator: coordinator1) } let!(:order_cycle2) { create(:simple_order_cycle, coordinator: coordinator2) } - let(:supplier1) { order_cycle1.suppliers.first } - let(:supplier2) { order_cycle1.suppliers.last } - let(:distributor1) { order_cycle1.distributors.first } - let(:distributor2) { order_cycle1.distributors.last } + let!(:supplier1) { order_cycle1.suppliers.first } + let!(:supplier2) { order_cycle1.suppliers.last } + let!(:distributor1) { order_cycle1.distributors.first } + let!(:distributor2) { order_cycle1.distributors.reject{ |d| d == distributor1 }.last } # ensure d1 != d2 let(:product) { order_cycle1.products.first } before(:each) do @@ -131,6 +131,7 @@ feature %q{ expect(page).to have_content 'ADD PRODUCT' targetted_select2_search product.name, from: '#add_variant_id', dropdown_css: '.select2-drop' + click_link 'Add' page.has_selector? "table.index tbody[data-hook='admin_order_form_line_items'] tr" # Wait for JS expect(page).to have_selector 'td', text: product.name diff --git a/spec/features/admin/reports_spec.rb b/spec/features/admin/reports_spec.rb index e880f25d23..5228b4c8a0 100644 --- a/spec/features/admin/reports_spec.rb +++ b/spec/features/admin/reports_spec.rb @@ -10,8 +10,8 @@ feature %q{ context "Permissions for different reports" do context "As an enterprise user" do let(:user) do - create_enterprise_user([ - create(:distributor_enterprise) + create_enterprise_user(enterprises: [ + create(:distributor_enterprise) ]) end it "should not show the Sales Total report" do @@ -99,7 +99,7 @@ feature %q{ let(:shipping_instructions) { "pick up on thursday please!" } let(:order1) { create(:order, :distributor => distributor, :bill_address => bill_address, :special_instructions => shipping_instructions) } let(:order2) { create(:order, :distributor => distributor, :bill_address => bill_address, :special_instructions => shipping_instructions) } - + before do Timecop.travel(Time.zone.local(2013, 4, 25, 14, 0, 0)) { order1.finalize! } Timecop.travel(Time.zone.local(2013, 4, 25, 16, 0, 0)) { order2.finalize! } @@ -144,7 +144,7 @@ feature %q{ variant_2.update_column(:count_on_hand, 20) product_2.master.update_column(:count_on_hand, 9) variant_1.option_values = [create(:option_value, :presentation => "Test")] - + login_to_admin_section click_link 'Reports' @@ -165,4 +165,3 @@ feature %q{ end end end - diff --git a/spec/features/admin/variants_spec.rb b/spec/features/admin/variants_spec.rb index 1904f3d5af..19767d8ea2 100644 --- a/spec/features/admin/variants_spec.rb +++ b/spec/features/admin/variants_spec.rb @@ -1,4 +1,4 @@ -require "spec_helper" +require 'spec_helper' feature %q{ As an admin diff --git a/spec/features/consumer/producers_spec.rb b/spec/features/consumer/producers_spec.rb index 98bef7f084..6e09365195 100644 --- a/spec/features/consumer/producers_spec.rb +++ b/spec/features/consumer/producers_spec.rb @@ -3,14 +3,14 @@ require 'spec_helper' feature %q{ As a consumer I want to see a list of producers - So that I can shop at hubs distributing their products + So that I can shop at hubs distributing their products }, js: true do include UIComponentHelper let!(:producer) { create(:supplier_enterprise) } let!(:invisible_producer) { create(:supplier_enterprise, visible: false) } let(:taxon) { create(:taxon) } let!(:product) { create(:simple_product, supplier: producer, taxons: [taxon]) } - + before do visit producers_path end @@ -20,7 +20,7 @@ feature %q{ expand_active_table_node producer.name page.should have_content producer.supplied_taxons.first.name.split.map(&:capitalize).join(' ') end - + it "doesn't show invisible producers" do page.should_not have_content invisible_producer.name end diff --git a/spec/features/consumer/registration_spec.rb b/spec/features/consumer/registration_spec.rb index 04e244e205..ea09450467 100644 --- a/spec/features/consumer/registration_spec.rb +++ b/spec/features/consumer/registration_spec.rb @@ -11,36 +11,28 @@ feature "Registration", js: true do expect(URI.parse(current_url).path).to eq registration_auth_path - sleep 0.5 # TOTO: DEAL WITH ME + page.has_selector? "dd", text: "Log in" + switch_to_login_tab - # Logging in - click_link "Log in" + # Enter Login details fill_in "Email", with: user.email fill_in "Password", with: user.password - click_button 'Log in' + click_login_and_ensure_content "Hi there!" - # Log in was successful, introduction shown - sleep 0.5 # TOTO: DEAL WITH ME - - expect(page).to have_content "This wizard will step you through creating a profile" expect(URI.parse(current_url).path).to eq registration_path # Done reading introduction - click_button "Let's get started!" + click_button_and_ensure_content "Let's get started!", "Woot! First we need to know a little bit about your enterprise:" # Filling in details - expect(page).to have_content "Woot! First we need to know what sort of enterprise you are:" fill_in 'enterprise_name', with: "My Awesome Enterprise" - click_link 'both-panel' - click_button 'Continue' # Filling in address - expect(page).to have_content 'Greetings My Awesome Enterprise' fill_in 'enterprise_address', with: '123 Abc Street' fill_in 'enterprise_city', with: 'Northcote' fill_in 'enterprise_zipcode', with: '3070' select 'Australia', from: 'enterprise_country' - select 'Vic', from: 'enterprise_state' + select 'VIC', from: 'enterprise_state' click_button 'Continue' # Filling in Contact Details @@ -50,11 +42,16 @@ feature "Registration", js: true do fill_in 'enterprise_phone', with: '12 3456 7890' click_button 'Continue' + # Choosing a type + expect(page).to have_content 'Last step to add My Awesome Enterprise!' + click_link 'producer-panel' + click_button 'Continue' + # Enterprise should be created expect(page).to have_content 'Nice one!' e = Enterprise.find_by_name('My Awesome Enterprise') expect(e.address.address1).to eq "123 Abc Street" - expect(e.is_distributor).to eq true + expect(e.sells).to eq "none" expect(e.is_primary_producer).to eq true expect(e.contact).to eq "Saskia Munroe" @@ -65,15 +62,22 @@ feature "Registration", js: true do fill_in 'enterprise_acn', with: '54321' click_button 'Continue' - # Enterprise should be updated - expect(page).to have_content 'Last step!' + # Enterprise should be update + expect(page).to have_content "Let's upload some pretty pictures so your profile looks great!" e.reload expect(e.description).to eq "Short description" expect(e.long_description).to eq "Long description" expect(e.abn).to eq '12345' expect(e.acn).to eq '54321' + # Images + # Move from logo page + click_button 'Continue' + # Move from promo page + click_button 'Continue' + # Filling in social + expect(page).to have_content 'How can people find My Awesome Enterprise online?' fill_in 'enterprise_website', with: 'www.shop.com' fill_in 'enterprise_facebook', with: 'FaCeBoOk' fill_in 'enterprise_linkedin', with: 'LiNkEdIn' @@ -82,7 +86,7 @@ feature "Registration", js: true do click_button 'Continue' # Done - expect(page).to have_content "You have successfully completed the profile for My Awesome Enterprise" + expect(page).to have_content "Finished!" e.reload expect(e.website).to eq "www.shop.com" expect(e.facebook).to eq "FaCeBoOk" @@ -90,34 +94,36 @@ feature "Registration", js: true do expect(e.twitter).to eq "@TwItTeR" expect(e.instagram).to eq "@InStAgRaM" end + end - it "Allows a logged in user to register a store" do - visit store_registration_path + def switch_to_login_tab + # Link appears to be unresponsive for a while, so keep clicking it until it works + using_wait_time 0.5 do + 10.times do + click_link "Log in" + break if page.has_selector? "dd.active", text: "Log in" + end + end + end - expect(URI.parse(current_url).path).to eq registration_auth_path + def click_login_and_ensure_content(content) + # Buttons appear to be unresponsive for a while, so keep clicking them until content appears + using_wait_time 1 do + 3.times do + click_button "Log in" + break if page.has_selector? "div#loading", text: "Hold on a moment, we're logging you in" + end + end + expect(page).to have_content content + end - sleep 0.5 # TOTO: DEAL WITH ME - - # Logging in - click_link "Log in" - fill_in "Email", with: user.email - fill_in "Password", with: user.password - click_button 'Log in' - - # Log in was successful, introduction shown - sleep 0.5 # TOTO: DEAL WITH ME - - expect(page).to have_content "This wizard will step you through creating a profile" - expect(URI.parse(current_url).path).to eq store_registration_path - - # Done reading introduction - click_button "Let's get started!" - - # Details Page - expect(page).to have_content "Woot! First we need to know the name of your farm:" - expect(page).to_not have_selector '#enterprise-types' - - # Everything from here should be covered in 'profile' spec + def click_button_and_ensure_content(button_text, content) + # Buttons appear to be unresponsive for a while, so keep clicking them until content appears + using_wait_time 0.5 do + 10.times do + click_button button_text + break if page.has_content? content + end end end end diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index 56c578f91b..d4db6bff29 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -167,7 +167,7 @@ feature "As a consumer I want to shop with a distributor", js: true do visit shop_path end - it "should save group buy data to ze cart" do + it "should save group buy data to the cart" do fill_in "variants[#{variant.id}]", with: 6 fill_in "variant_attributes[#{variant.id}][max_quantity]", with: 7 page.should have_in_cart product.name diff --git a/spec/javascripts/application_spec.js b/spec/javascripts/application_spec.js index 8c611081a2..10db2226a4 100644 --- a/spec/javascripts/application_spec.js +++ b/spec/javascripts/application_spec.js @@ -1,13 +1,15 @@ //= require angular //= require angular-resource //= require angular-animate -//= require angular-sanitize //= require angular-mocks //= require angular-cookies //= require angular-backstretch.js +//= require angularjs-file-upload //= require lodash.underscore.js //= require angular-flash.min.js //= require shared/mm-foundation-tpls-0.2.2.min.js +//= require textAngular.min.js +//= require textAngular-sanitize.min.js //= require moment angular.module('templates', []) diff --git a/spec/javascripts/unit/admin/enterprises/controllers/enterprise_controller_spec.js.coffee b/spec/javascripts/unit/admin/enterprises/controllers/enterprise_controller_spec.js.coffee index b1b019276b..f6805fd04d 100644 --- a/spec/javascripts/unit/admin/enterprises/controllers/enterprise_controller_spec.js.coffee +++ b/spec/javascripts/unit/admin/enterprises/controllers/enterprise_controller_spec.js.coffee @@ -4,10 +4,14 @@ describe "enterpriseCtrl", -> Enterprise = null PaymentMethods = null ShippingMethods = null + longDescriptionMock = ["long description text"] beforeEach -> module('admin.enterprises') - Enterprise = + module ($provide)-> + $provide.value "longDescription", longDescriptionMock + null + Enterprise = enterprise: payment_method_ids: [ 1, 3 ] shipping_method_ids: [ 2, 4 ] @@ -15,7 +19,7 @@ describe "enterpriseCtrl", -> paymentMethods: [ { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 } ] ShippingMethods = shippingMethods: [ { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 } ] - + inject ($controller) -> scope = {} ctrl = $controller 'enterpriseCtrl', {$scope: scope, Enterprise: Enterprise, PaymentMethods: PaymentMethods, ShippingMethods: ShippingMethods} @@ -82,4 +86,4 @@ describe "enterpriseCtrl", -> describe "counting selected shipping methods", -> it "counts only shipping methods with selected: true", -> scope.ShippingMethods = [ { selected: true }, { selected: true }, { selected: false }, { selected: true } ] - expect(scope.selectedShippingMethodsCount()).toBe 3 \ No newline at end of file + expect(scope.selectedShippingMethodsCount()).toBe 3 diff --git a/spec/javascripts/unit/darkswarm/filters/filter_hubs_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/filter_hubs_spec.js.coffee deleted file mode 100644 index fc2702a72b..0000000000 --- a/spec/javascripts/unit/darkswarm/filters/filter_hubs_spec.js.coffee +++ /dev/null @@ -1,45 +0,0 @@ -describe 'filtering Hubs', -> - filter = null - filterHubs = null - hubs = [{ - name: "frogs" - other: "roger" - address: - zipcode: "cats" - city: "cambridge" - state: "kansas" - }, { - name: "donkeys" - other: "roger" - address: - zipcode: "" - city: "Wellington" - state: "uzbekistan" - }] - - beforeEach -> - module 'Darkswarm' - inject ($filter) -> - filter = $filter - filterHubs = $filter('hubs') - - it 'has a hub filter', -> - expect(filter('hubs')).not.toBeNull() - - it "filters by name", -> - expect(filterHubs(hubs, 'donkeys').length).toEqual 1 - - it "is case insensitive", -> - expect(filterHubs(hubs, 'DONKEYS').length).toEqual 1 - - it "filters by state", -> - expect(filterHubs(hubs, 'kansas').length).toEqual 1 - - it "filters by zipcode", -> - expect(filterHubs(hubs, 'cats').length).toEqual 1 - - it "gives all hubs when no argument is specified", -> - expect(filterHubs(hubs, '').length).toEqual 2 - - it "does not filter by anything else", -> - expect(filterHubs(hubs, 'roger').length).toEqual 0 diff --git a/spec/javascripts/unit/darkswarm/filters/filter_producers_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/filter_producers_spec.js.coffee deleted file mode 100644 index 29d8986b56..0000000000 --- a/spec/javascripts/unit/darkswarm/filters/filter_producers_spec.js.coffee +++ /dev/null @@ -1,28 +0,0 @@ -describe 'filtering producers', -> - filter = null - filterProducers = null - producers = [{ - name: "frogs" - other: "roger" - address: - zipcode: "cats" - city: "cambridge" - state: "kansas" - }, { - name: "donkeys" - other: "roger" - address: - zipcode: "" - city: "Wellington" - state: "uzbekistan" - }] - - beforeEach -> - module 'Darkswarm' - inject ($filter) -> - filter = $filter - filterProducers = $filter('filterProducers') - - - it 'has a producer filter', -> - expect(filter('filterProducers')).not.toBeNull() diff --git a/spec/javascripts/unit/darkswarm/filters/localize_currency_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/localize_currency_spec.js.coffee new file mode 100644 index 0000000000..0d21c7de6c --- /dev/null +++ b/spec/javascripts/unit/darkswarm/filters/localize_currency_spec.js.coffee @@ -0,0 +1,42 @@ +describe 'convert number to localised currency ', -> + filter = currencyconfig = null + + beforeEach -> + currencyconfig = + symbol: "$" + symbol_position: "before" + currency: "D" + hide_cents: "false" + # Not used yet... + # decimal_mark: "." + # thousands_separator: "," + module 'Darkswarm' + module ($provide)-> + $provide.value "currencyConfig", currencyconfig + null + inject ($filter) -> + filter = $filter('localizeCurrency') + + it "adds decimal fraction to an amount", -> + expect(filter(10)).toEqual "$10.00" + + it "handles an existing fraction", -> + expect(filter(9.9)).toEqual "$9.90" + + it "can use any currency symbol", -> + currencyconfig.symbol = "£" + expect(filter(404.04)).toEqual "£404.04" + + it "can place symbols after the amount", -> + currencyconfig.symbol_position = "after" + expect(filter(333.3)).toEqual "333.30 $" + + it "can add a currency string", -> + currencyconfig.display_currency = "true" + expect(filter(5)).toEqual "$5.00 D" + + it "can hide cents", -> + currencyconfig.hide_cents = "true" + expect(filter(5)).toEqual "$5" + + diff --git a/spec/javascripts/unit/darkswarm/filters/search_enterprises_spec.js.coffee b/spec/javascripts/unit/darkswarm/filters/search_enterprises_spec.js.coffee new file mode 100644 index 0000000000..2717e50cfb --- /dev/null +++ b/spec/javascripts/unit/darkswarm/filters/search_enterprises_spec.js.coffee @@ -0,0 +1,40 @@ +describe 'filtering Enterprises', -> + filter = null + enterprises = [{ + name: "frogs" + other: "roger" + address: + zipcode: "cats" + city: "cambridge" + state: "kansas" + }, { + name: "donkeys" + other: "roger" + address: + zipcode: "" + city: "Wellington" + state: "uzbekistan" + }] + + beforeEach -> + module 'Darkswarm' + inject ($filter) -> + filter = $filter('searchEnterprises') + + it "filters by name", -> + expect(filter(enterprises, 'donkeys').length).toEqual 1 + + it "is case insensitive", -> + expect(filter(enterprises, 'DONKEYS').length).toEqual 1 + + it "filters by state", -> + expect(filter(enterprises, 'kansas').length).toEqual 1 + + it "filters by zipcode", -> + expect(filter(enterprises, 'cats').length).toEqual 1 + + it "gives all enterprises when no argument is specified", -> + expect(filter(enterprises, '').length).toEqual 2 + + it "does not filter by anything else", -> + expect(filter(enterprises, 'roger').length).toEqual 0 diff --git a/spec/javascripts/unit/darkswarm/services/enterprise_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/enterprise_spec.js.coffee index 59d4ae9826..94dd7d39d2 100644 --- a/spec/javascripts/unit/darkswarm/services/enterprise_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/enterprise_spec.js.coffee @@ -1,24 +1,30 @@ describe "Enterprises service", -> Enterprises = null - CurrentHubMock = {} + CurrentHubMock = {} taxons = [ {id: 1, name: "test"} ] enterprises = [ - {id: 1, type: "hub", producers: [{id: 2}], taxons: [{id: 1}]}, - {id: 2, type: "producer", hubs: [{id: 1}]}, - {id: 3, type: "producer", hubs: [{id: 1}]} + {id: 1, visible: true, category: "hub", producers: [{id: 5}], taxons: [{id: 1}]}, + {id: 2, visible: true, category: "hub", producers: [{id: 6}]} + {id: 3, visible: true, category: "hub_profile"} + {id: 4, visible: false, category: "hub", producers: [{id: 7}]} + {id: 5, visible: true, category: "producer_hub", hubs: [{id: 1}]}, + {id: 6, visible: true, category: "producer_shop", hubs: [{id: 2}]}, + {id: 7, visible: true, category: "producer", hubs: [{id: 2}]} + {id: 8, visible: false, category: "producer", hubs: [{id: 2}]} ] + H1: 0 beforeEach -> module 'Darkswarm' module ($provide)-> - $provide.value "CurrentHub", CurrentHubMock + $provide.value "CurrentHub", CurrentHubMock null - angular.module('Darkswarm').value('enterprises', enterprises) - angular.module('Darkswarm').value('taxons', taxons) + angular.module('Darkswarm').value('enterprises', enterprises) + angular.module('Darkswarm').value('taxons', taxons) inject ($injector)-> - Enterprises = $injector.get("Enterprises") + Enterprises = $injector.get("Enterprises") it "stores enterprises as id/object pairs", -> expect(Enterprises.enterprises_by_id["1"]).toBe enterprises[0] @@ -31,8 +37,39 @@ describe "Enterprises service", -> expect(Enterprises.enterprises[0]).toBe Enterprises.enterprises_by_id["1"] it "dereferences references to other enterprises", -> - expect(Enterprises.enterprises_by_id["1"].producers[0]).toBe enterprises[1] - expect(Enterprises.enterprises_by_id["3"].hubs[0]).toBe enterprises[0] + expect(Enterprises.enterprises_by_id["1"].producers[0]).toBe enterprises[4] + expect(Enterprises.enterprises_by_id["5"].hubs[0]).toBe enterprises[0] it "dereferences taxons", -> expect(Enterprises.enterprises[0].taxons[0]).toBe taxons[0] + + it "filters Enterprise.hubs into a new array", -> + expect(Enterprises.hubs[0]).toBe Enterprises.enterprises[0] + # Because the $filter is a new sorted array + # We check to see the objects in both arrays are still the same + Enterprises.enterprises[0].active = false + expect(Enterprises.hubs[0].active).toBe false + + it "filters Enterprises.producers into a new array", -> + expect(Enterprises.producers[0]).toBe Enterprises.enterprises[4] + Enterprises.enterprises[4].active = false + expect(Enterprises.producers[0].active).toBe false + + it "only includes visible enterprises in hubs array", -> + expect(Enterprises.hubs).toContain Enterprises.enterprises[0] + expect(Enterprises.hubs).not.toContain Enterprises.enterprises[3] + + it "only includes visible enterprises in producers array", -> + expect(Enterprises.producers).toContain Enterprises.enterprises[4] + expect(Enterprises.producers).not.toContain Enterprises.enterprises[7] + + it "includes hub, hub_profile, producer_hub and, producer_shop enterprises in hubs array", -> + expect(Enterprises.hubs).toContain Enterprises.enterprises[0] + expect(Enterprises.hubs).toContain Enterprises.enterprises[2] + expect(Enterprises.hubs).toContain Enterprises.enterprises[4] + expect(Enterprises.hubs).toContain Enterprises.enterprises[5] + + it "includes producer_hub, producer_shop and producer enterprises in producers array", -> + expect(Enterprises.producers).toContain Enterprises.enterprises[4] + expect(Enterprises.producers).toContain Enterprises.enterprises[5] + expect(Enterprises.producers).toContain Enterprises.enterprises[6] diff --git a/spec/javascripts/unit/darkswarm/services/hubs_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/hubs_spec.js.coffee deleted file mode 100644 index f8ae8230bc..0000000000 --- a/spec/javascripts/unit/darkswarm/services/hubs_spec.js.coffee +++ /dev/null @@ -1,46 +0,0 @@ -describe "Hubs service", -> - Hubs = null - Enterprises = null - CurrentHubMock = {} - hubs = [ - { - id: 2 - active: false - orders_close_at: new Date() - is_distributor: true - has_shopfront: true - } - { - id: 3 - active: false - orders_close_at: new Date() - is_distributor: true - has_shopfront: true - } - { - id: 1 - active: true - orders_close_at: new Date() - is_distributor: true - has_shopfront: true - } - ] - - - beforeEach -> - module 'Darkswarm' - angular.module('Darkswarm').value('enterprises', hubs) - module ($provide)-> - $provide.value "CurrentHub", CurrentHubMock - null - inject ($injector)-> - Enterprises = $injector.get("Enterprises") - Hubs = $injector.get("Hubs") - - it "filters Enterprise.hubs into a new array", -> - expect(Hubs.hubs[0]).toBe Enterprises.enterprises[2] - # Because the $filter is a new sorted array - # We check to see the objects in both arrays are still the same - Enterprises.enterprises[2].active = false - expect(Hubs.hubs[0].active).toBe false - diff --git a/spec/javascripts/unit/darkswarm/services/producers_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/producers_spec.js.coffee deleted file mode 100644 index dc9ac0fc8f..0000000000 --- a/spec/javascripts/unit/darkswarm/services/producers_spec.js.coffee +++ /dev/null @@ -1,21 +0,0 @@ -describe "Producers service", -> - Producers = null - Enterprises = null - CurrentHubMock = - hub: - id: 1 - enterprises = [ - {is_primary_producer: true} - ] - - beforeEach -> - module 'Darkswarm' - module ($provide)-> - $provide.value "CurrentHub", CurrentHubMock - null - angular.module('Darkswarm').value('enterprises', enterprises) - inject ($injector)-> - Producers = $injector.get("Producers") - - it "delegates producers array to Enterprises", -> - expect(Producers.producers[0]).toBe enterprises[0] diff --git a/spec/javascripts/unit/darkswarm/services/variants_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/variants_spec.js.coffee index ac9865a142..5c6e138e0d 100644 --- a/spec/javascripts/unit/darkswarm/services/variants_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/services/variants_spec.js.coffee @@ -5,8 +5,8 @@ describe 'Variants service', -> beforeEach -> variant = id: 1 - base_price: 80.5 - price: 100 + price: 80.5 + price_with_fees: 100 module 'Darkswarm' inject ($injector)-> Variants = $injector.get("Variants") diff --git a/spec/mailers/enterprise_mailer_spec.rb b/spec/mailers/enterprise_mailer_spec.rb index 412870bad7..dd1e150eb7 100644 --- a/spec/mailers/enterprise_mailer_spec.rb +++ b/spec/mailers/enterprise_mailer_spec.rb @@ -1,13 +1,16 @@ require 'spec_helper' describe EnterpriseMailer do + let!(:enterprise) { create(:enterprise) } + before do - @enterprise = create(:enterprise) ActionMailer::Base.deliveries = [] end - it "should send an email when given an enterprise" do - EnterpriseMailer.creation_confirmation(@enterprise).deliver + it "should send an email confirmation when given an enterprise" do + EnterpriseMailer.confirmation_instructions(enterprise, 'token').deliver ActionMailer::Base.deliveries.count.should == 1 + mail = ActionMailer::Base.deliveries.first + expect(mail.subject).to eq "Please confirm your email for #{enterprise.name}" end end \ No newline at end of file diff --git a/spec/models/enterprise_spec.rb b/spec/models/enterprise_spec.rb index f6b3c13d2d..206be1e131 100644 --- a/spec/models/enterprise_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -3,6 +3,25 @@ require 'spec_helper' describe Enterprise do include AuthenticationWorkflow + describe "sending emails" do + describe "on creation" do + let!(:user) { create_enterprise_user( enterprise_limit: 2 ) } + let!(:enterprise) { create(:enterprise, owner: user) } + + it "when the email address has not already been confirmed" do + mail_message = double "Mail::Message" + EnterpriseMailer.should_receive(:confirmation_instructions).and_return mail_message + mail_message.should_receive :deliver + create(:enterprise, owner: user, email: "unknown@email.com", confirmed_at: nil ) + end + + it "when the email address has already been confirmed" do + EnterpriseMailer.should_not_receive(:confirmation_instructions) + e = create(:enterprise, owner: user, email: enterprise.email, confirmed_at: nil) + end + end + end + describe "associations" do it { should belong_to(:owner) } it { should have_many(:supplied_products) } @@ -85,6 +104,8 @@ describe Enterprise do describe "validations" do subject { FactoryGirl.create(:distributor_enterprise, :address => FactoryGirl.create(:address)) } it { should validate_presence_of(:name) } + it { should validate_presence_of(:email) } + it { should ensure_length_of(:description).is_at_most(255) } it "requires an owner" do expect{ @@ -101,6 +122,7 @@ describe Enterprise do it { should delegate(:city).to(:address) } it { should delegate(:state_name).to(:address) } end + describe "scopes" do describe 'active' do it 'find active enterprises' do @@ -110,6 +132,28 @@ describe Enterprise do end end + describe "confirmed" do + it "find enterprises with a confirmed date" do + s1 = create(:supplier_enterprise) + d1 = create(:distributor_enterprise) + s2 = create(:supplier_enterprise, confirmed_at: nil) + d2 = create(:distributor_enterprise, confirmed_at: nil) + expect(Enterprise.confirmed).to include s1, d1 + expect(Enterprise.confirmed).to_not include s2, d2 + end + end + + describe "unconfirmed" do + it "find enterprises without a confirmed date" do + s1 = create(:supplier_enterprise) + d1 = create(:distributor_enterprise) + s2 = create(:supplier_enterprise, confirmed_at: nil) + d2 = create(:distributor_enterprise, confirmed_at: nil) + expect(Enterprise.unconfirmed).to_not include s1, d1 + expect(Enterprise.unconfirmed).to include s2, d2 + end + end + describe "distributors_with_active_order_cycles" do it "finds active distributors by order cycles" do s = create(:supplier_enterprise) @@ -501,4 +545,25 @@ describe Enterprise do supplier.producer_properties.first.property.presentation.should == 'Organic Certified' end end + + describe "provide enterprise category" do + let(:producer_sell_all) { build(:enterprise, is_primary_producer: true, sells: "any") } + let(:producer_sell_own) { build(:enterprise, is_primary_producer: true, sells: "own") } + let(:producer_sell_none) { build(:enterprise, is_primary_producer: true, sells: "none") } + let(:non_producer_sell_all) { build(:enterprise, is_primary_producer: false, sells: "any") } + let(:non_producer_sell_own) { build(:enterprise, is_primary_producer: false, sells: "own") } + let(:non_producer_sell_none) { build(:enterprise, is_primary_producer: false, sells: "none") } + + it "should output enterprise categories" do + producer_sell_all.is_primary_producer.should == true + producer_sell_all.sells.should == "any" + + producer_sell_all.category.should == :producer_hub + producer_sell_own.category.should == :producer_shop + producer_sell_none.category.should == :producer + non_producer_sell_all.category.should == :hub + non_producer_sell_own.category.should == :hub + non_producer_sell_none.category.should == :hub_profile + end + end end diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index 9785d5ed2d..56aa51a0d0 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -44,8 +44,8 @@ describe OrderCycle do end it "finds order cycles accessible by a user" do - e1 = create(:enterprise, is_primary_producer: true, is_distributor: true) - e2 = create(:enterprise, is_primary_producer: true, is_distributor: true) + e1 = create(:enterprise, is_primary_producer: true, sells: "any") + e2 = create(:enterprise, is_primary_producer: true, sells: "any") user = create(:user, enterprises: [e2], spree_roles: []) user.spree_roles = [] diff --git a/spec/models/spree/ability_spec.rb b/spec/models/spree/ability_spec.rb index cf17d7fe3a..e523cce755 100644 --- a/spec/models/spree/ability_spec.rb +++ b/spec/models/spree/ability_spec.rb @@ -9,39 +9,81 @@ module Spree describe "broad permissions" do subject { AbilityDecorator.new(user) } let(:user) { create(:user) } - let(:enterprise_full) { create(:enterprise, type: 'full') } - let(:enterprise_single) { create(:enterprise, type: 'single') } - let(:enterprise_profile) { create(:enterprise, type: 'profile') } + let(:enterprise_any) { create(:enterprise, sells: 'any') } + let(:enterprise_own) { create(:enterprise, sells: 'own') } + let(:enterprise_none) { create(:enterprise, sells: 'none') } + let(:enterprise_any_producer) { create(:enterprise, sells: 'any', is_primary_producer: true) } + let(:enterprise_own_producer) { create(:enterprise, sells: 'own', is_primary_producer: true) } + let(:enterprise_none_producer) { create(:enterprise, sells: 'none', is_primary_producer: true) } - describe "managing enterprises" do - it "can manage enterprises when the user has at least one enterprise assigned" do - user.enterprise_roles.create! enterprise: enterprise_full - subject.can_manage_enterprises?(user).should be_true + context "as manager of an enterprise who sells 'any'" do + before do + user.enterprise_roles.create! enterprise: enterprise_any end - it "can't otherwise" do - subject.can_manage_enterprises?(user).should be_false - end + it { subject.can_manage_products?(user).should be_false } + it { subject.can_manage_enterprises?(user).should be_true } + it { subject.can_manage_orders?(user).should be_true } end - describe "managing products" do - it "can when a user manages a 'full' type enterprise" do - user.enterprise_roles.create! enterprise: enterprise_full - subject.can_manage_products?(user).should be_true + context "as manager of an enterprise who sell 'own'" do + before do + user.enterprise_roles.create! enterprise: enterprise_own end - it "can when a user manages a 'single' type enterprise" do - user.enterprise_roles.create! enterprise: enterprise_single - subject.can_manage_products?(user).should be_true + it { subject.can_manage_products?(user).should be_false } + it { subject.can_manage_enterprises?(user).should be_true } + it { subject.can_manage_orders?(user).should be_true } + end + + context "as manager of an enterprise who sells 'none'" do + before do + user.enterprise_roles.create! enterprise: enterprise_none end - it "can't when a user manages a 'profile' type enterprise" do - user.enterprise_roles.create! enterprise: enterprise_profile - subject.can_manage_products?(user).should be_false + it { subject.can_manage_products?(user).should be_false } + it { subject.can_manage_enterprises?(user).should be_true } + it { subject.can_manage_orders?(user).should be_false } + end + + context "as manager of a producer enterprise who sells 'any'" do + before do + user.enterprise_roles.create! enterprise: enterprise_any_producer end - it "can't when the user manages no enterprises" do - subject.can_manage_products?(user).should be_false + it { subject.can_manage_products?(user).should be_true } + it { subject.can_manage_enterprises?(user).should be_true } + it { subject.can_manage_orders?(user).should be_true } + end + + context "as manager of a producer enterprise who sell 'own'" do + before do + user.enterprise_roles.create! enterprise: enterprise_own_producer + end + + it { subject.can_manage_products?(user).should be_true } + it { subject.can_manage_enterprises?(user).should be_true } + it { subject.can_manage_orders?(user).should be_true } + end + + context "as manager of a producer enterprise who sells 'none'" do + before do + user.enterprise_roles.create! enterprise: enterprise_none_producer + end + + it { subject.can_manage_products?(user).should be_true } + it { subject.can_manage_enterprises?(user).should be_true } + it { subject.can_manage_orders?(user).should be_false } + end + + context "as a new user with no enterprises" do + it { subject.can_manage_products?(user).should be_false } + it { subject.can_manage_enterprises?(user).should be_false } + it { subject.can_manage_orders?(user).should be_false } + + it "can create enterprises straight off the bat" do + subject.is_new_user?(user).should be_true + expect(user).to have_ability :create, for: Enterprise end end end @@ -142,6 +184,14 @@ module Spree should_not have_ability(:destroy, for: er2) end + it "should be able to read some reports" do + should have_ability([:admin, :index, :customers, :bulk_coop, :orders_and_fulfillment, :products_and_inventory], for: :report) + end + + it "should not be able to read other reports" do + should_not have_ability([:sales_total, :group_buys, :payments, :orders_and_distributors], for: :report) + end + end context "when is a distributor enterprise user" do @@ -228,17 +278,26 @@ module Spree it "should not be able to destroy enterprise relationships for other enterprises" do should_not have_ability(:destroy, for: er1) end + + it "should be able to read some reports" do + should have_ability([:admin, :index, :customers, :group_buys, :bulk_coop, :payments, :orders_and_distributors, :orders_and_fulfillment, :products_and_inventory], for: :report) + end + + it "should not be able to read other reports" do + should_not have_ability([:sales_total], for: :report) + end + end - context 'Order Cycle co-ordinator' do - + context 'Order Cycle co-ordinator, distributor enterprise manager' do let (:user) do user = create(:user) user.spree_roles = [] - s1.enterprise_roles.build(user: user).save + d1.enterprise_roles.build(user: user).save user end - let(:oc1) { create(:simple_order_cycle, {coordinator: s1}) } + + let(:oc1) { create(:simple_order_cycle, {coordinator: d1}) } let(:oc2) { create(:simple_order_cycle) } it "should be able to read/write OrderCycles they are the co-ordinator of" do @@ -256,6 +315,10 @@ module Spree it "should be able to read/write EnterpriseFees" do should have_ability([:admin, :index, :read, :create, :edit, :bulk_update, :destroy], for: EnterpriseFee) end + + it "should be able to add enterprises to order cycles" do + should have_ability([:admin, :index, :for_order_cycle, :create], for: Enterprise) + end end context 'enterprise manager' do @@ -275,7 +338,7 @@ module Spree end it 'should have the ability administrate and create enterpises' do - should have_ability([:admin, :index, :for_order_cycle, :create], for: Enterprise) + should have_ability([:admin, :index, :create], for: Enterprise) end end end diff --git a/spec/models/spree/image_spec.rb b/spec/models/spree/image_spec.rb new file mode 100644 index 0000000000..56665fa641 --- /dev/null +++ b/spec/models/spree/image_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +module Spree + describe Image do + describe "attachment definitions" do + let(:name_str) { {"mini" => "48x48>"} } + let(:formatted) { {mini: ["48x48>", "png"]} } + + it "converts style names to symbols" do + Image.format_styles(name_str).should == {:mini => "48x48>"} + end + + it "converts formats to symbols" do + Image.format_styles(formatted).should == {:mini => ["48x48>", :png]} + end + end + end +end diff --git a/spec/serializers/enterprise_serializer_spec.rb b/spec/serializers/enterprise_serializer_spec.rb index 5fe57a99e2..f3409f2d57 100644 --- a/spec/serializers/enterprise_serializer_spec.rb +++ b/spec/serializers/enterprise_serializer_spec.rb @@ -13,9 +13,27 @@ describe Api::EnterpriseSerializer do serializer = Api::EnterpriseSerializer.new enterprise serializer.to_json.should match taxon.id.to_s end - + it "will render urls" do serializer = Api::EnterpriseSerializer.new enterprise serializer.to_json.should match "map_005-hub.svg" end + + describe "visibility" do + before do + enterprise.stub(:visible).and_return true + end + + it "is visible when confirmed" do + enterprise.stub(:confirmed?).and_return true + serializer = Api::EnterpriseSerializer.new enterprise + expect(serializer.to_json).to match "\"visible\":true" + end + + it "is not visible when unconfirmed" do + enterprise.stub(:confirmed?).and_return false + serializer = Api::EnterpriseSerializer.new enterprise + expect(serializer.to_json).to match "\"visible\":false" + end + end end diff --git a/spec/support/matchers/table_matchers.rb b/spec/support/matchers/table_matchers.rb index 146002b751..053562b9e4 100644 --- a/spec/support/matchers/table_matchers.rb +++ b/spec/support/matchers/table_matchers.rb @@ -3,20 +3,15 @@ RSpec::Matchers.define :have_table_row do |row| match_for_should do |node| @row = row - false_on_timeout_error do - wait_until { rows_under(node).include? row } - end + node.has_selector? "tr", text: row.join(" ").strip # Check for appearance + rows_under(node).include? row # Robust check of columns end match_for_should_not do |node| @row = row - false_on_timeout_error do - # Without this sleep, we trigger capybara's wait when looking up the table, for the full - # period of default_wait_time. - sleep 0.1 - wait_until { !rows_under(node).include? row } - end + node.has_no_selector? "tr", text: row.join(" ").strip # Check for appearance + !rows_under(node).include? row # Robust check of columns end failure_message_for_should do |text| @@ -27,17 +22,7 @@ RSpec::Matchers.define :have_table_row do |row| "expected not to find table row #{@row}" end - def rows_under(node) node.all('tr').map { |tr| tr.all('th, td').map(&:text) } end - - def false_on_timeout_error - yield - rescue TimeoutError - false - else - true - end - end diff --git a/spec/support/request/authentication_workflow.rb b/spec/support/request/authentication_workflow.rb index a2ee597f27..39f78223d7 100644 --- a/spec/support/request/authentication_workflow.rb +++ b/spec/support/request/authentication_workflow.rb @@ -37,12 +37,9 @@ module AuthenticationWorkflow visit spree.admin_path end - def create_enterprise_user(enterprises = []) - new_user = create(:user, password: 'blahblah', :password_confirmation => 'blahblah') + def create_enterprise_user( attrs = {} ) + new_user = create(:user, attrs) new_user.spree_roles = [] # for some reason unbeknown to me, this new user gets admin permissions by default. - for enterprise in enterprises do - new_user.enterprise_roles.build(enterprise: enterprise).save - end new_user.save new_user end diff --git a/vendor/assets/javascripts/textAngular-sanitize.min.js b/vendor/assets/javascripts/textAngular-sanitize.min.js new file mode 100644 index 0000000000..75534aaa01 --- /dev/null +++ b/vendor/assets/javascripts/textAngular-sanitize.min.js @@ -0,0 +1 @@ +!function(a,b){b["true"]=a,function(a,b){"use strict";function c(){this.$get=["$$sanitizeUri",function(a){return function(b){var c=[];return f(b,k(c,function(b,c){return!/^unsafe/.test(a(b,c))})),c.join("")}}]}function d(a){var c=[],d=k(c,b.noop);return d.chars(a),c.join("")}function e(a){var b,c={},d=a.split(",");for(b=0;b")},activeState:function(){return this.$editor().queryFormatBlockState("p")}}),a("pre",{buttontext:"pre",tooltiptext:c.pre.tooltip,action:function(){return this.$editor().wrapSelection("formatBlock","
")},activeState:function(){return this.$editor().queryFormatBlockState("pre")}}),a("ul",{iconclass:"fa fa-list-ul",tooltiptext:c.ul.tooltip,action:function(){return this.$editor().wrapSelection("insertUnorderedList",null)},activeState:function(){return this.$editor().queryCommandState("insertUnorderedList")}}),a("ol",{iconclass:"fa fa-list-ol",tooltiptext:c.ol.tooltip,action:function(){return this.$editor().wrapSelection("insertOrderedList",null)},activeState:function(){return this.$editor().queryCommandState("insertOrderedList")}}),a("quote",{iconclass:"fa fa-quote-right",tooltiptext:c.quote.tooltip,action:function(){return this.$editor().wrapSelection("formatBlock","")},activeState:function(){return this.$editor().queryFormatBlockState("blockquote")}}),a("undo",{iconclass:"fa fa-undo",tooltiptext:c.undo.tooltip,action:function(){return this.$editor().wrapSelection("undo",null)}}),a("redo",{iconclass:"fa fa-repeat",tooltiptext:c.redo.tooltip,action:function(){return this.$editor().wrapSelection("redo",null)}}),a("bold",{iconclass:"fa fa-bold",tooltiptext:c.bold.tooltip,action:function(){return this.$editor().wrapSelection("bold",null)},activeState:function(){return this.$editor().queryCommandState("bold")},commandKeyCode:98}),a("justifyLeft",{iconclass:"fa fa-align-left",tooltiptext:c.justifyLeft.tooltip,action:function(){return this.$editor().wrapSelection("justifyLeft",null)},activeState:function(a){var b=!1;return a&&(b="left"===a.css("text-align")||"left"===a.attr("align")||"right"!==a.css("text-align")&&"center"!==a.css("text-align")&&!this.$editor().queryCommandState("justifyRight")&&!this.$editor().queryCommandState("justifyCenter")),b=b||this.$editor().queryCommandState("justifyLeft")}}),a("justifyRight",{iconclass:"fa fa-align-right",tooltiptext:c.justifyRight.tooltip,action:function(){return this.$editor().wrapSelection("justifyRight",null)},activeState:function(a){var b=!1;return a&&(b="right"===a.css("text-align")),b=b||this.$editor().queryCommandState("justifyRight")}}),a("justifyCenter",{iconclass:"fa fa-align-center",tooltiptext:c.justifyCenter.tooltip,action:function(){return this.$editor().wrapSelection("justifyCenter",null)},activeState:function(a){var b=!1;return a&&(b="center"===a.css("text-align")),b=b||this.$editor().queryCommandState("justifyCenter")}}),a("indent",{iconclass:"fa fa-indent",tooltiptext:c.indent.tooltip,action:function(){return this.$editor().wrapSelection("indent",null)},activeState:function(){return this.$editor().queryFormatBlockState("blockquote")}}),a("outdent",{iconclass:"fa fa-outdent",tooltiptext:c.outdent.tooltip,action:function(){return this.$editor().wrapSelection("outdent",null)},activeState:function(){return!1}}),a("italics",{iconclass:"fa fa-italic",tooltiptext:c.italic.tooltip,action:function(){return this.$editor().wrapSelection("italic",null)},activeState:function(){return this.$editor().queryCommandState("italic")},commandKeyCode:105}),a("underline",{iconclass:"fa fa-underline",tooltiptext:c.underline.tooltip,action:function(){return this.$editor().wrapSelection("underline",null)},activeState:function(){return this.$editor().queryCommandState("underline")},commandKeyCode:117}),a("clear",{iconclass:"fa fa-ban",tooltiptext:c.clear.tooltip,action:function(a,b){this.$editor().wrapSelection("removeFormat",null);var c=angular.element(d.getSelectionElement()),e=function(a){a=angular.element(a);var b=a;angular.forEach(a.children(),function(a){var c=angular.element("");c.html(angular.element(a).html()),b.after(c),b=c}),a.remove()};angular.forEach(c.find("ul"),e),angular.forEach(c.find("ol"),e);var f=this.$editor(),g=function(a){a=angular.element(a),a[0]!==f.displayElements.text[0]&&a.removeAttr("class"),angular.forEach(a.children(),g)};angular.forEach(c,g),"li"!==c[0].tagName.toLowerCase()&&"ol"!==c[0].tagName.toLowerCase()&&"ul"!==c[0].tagName.toLowerCase()&&this.$editor().wrapSelection("formatBlock",""),b()}});var g=function(a,b,c){var d=function(){c.updateTaBindtaTextElement(),c.hidePopover()};a.preventDefault(),c.displayElements.popover.css("width","375px");var e=c.displayElements.popoverContainer;e.empty();var f=angular.element('
'),g=angular.element('');g.on("click",function(a){a.preventDefault(),b.css({width:"100%",height:""}),d()});var h=angular.element('');h.on("click",function(a){a.preventDefault(),b.css({width:"50%",height:""}),d()});var i=angular.element('');i.on("click",function(a){a.preventDefault(),b.css({width:"25%",height:""}),d()});var j=angular.element('');j.on("click",function(a){a.preventDefault(),b.css({width:"",height:""}),d()}),f.append(g),f.append(h),f.append(i),f.append(j),e.append(f),f=angular.element('');var k=angular.element('');k.on("click",function(a){a.preventDefault(),b.css("float","left"),d()});var l=angular.element('');l.on("click",function(a){a.preventDefault(),b.css("float","right"),d()});var m=angular.element('');m.on("click",function(a){a.preventDefault(),b.css("float",""),d()}),f.append(k),f.append(m),f.append(l),e.append(f),f=angular.element('');var n=angular.element('');n.on("click",function(a){a.preventDefault(),b.remove(),d()}),f.append(n),e.append(f),c.showPopover(b),c.showResizeOverlay(b)};a("insertImage",{iconclass:"fa fa-picture-o",tooltiptext:c.insertImage.tooltip,action:function(){var a;return a=b.prompt(c.insertImage.dialogPrompt,"http://"),a&&""!==a&&"http://"!==a?this.$editor().wrapSelection("insertImage",a,!0):void 0},onElementSelect:{element:"img",action:g}}),a("insertVideo",{iconclass:"fa fa-youtube-play",tooltiptext:c.insertVideo.tooltip,action:function(){var a;if(a=b.prompt(c.insertVideo.dialogPrompt,"http://"),a&&""!==a&&"http://"!==a){var d=a.match(/(\?|&)v=[^&]*/);if(d.length>0){var e="http://www.youtube.com/embed/"+d[0].substring(3),f='
';return this.$editor().wrapSelection("insertHTML",f,!0)}}},onElementSelect:{element:"img",onlyWithAttrs:["ta-insert-video"],action:g}}),a("insertLink",{tooltiptext:c.insertLink.tooltip,iconclass:"fa fa-link",action:function(){var a;return a=b.prompt(c.insertLink.dialogPrompt,"http://"),a&&""!==a&&"http://"!==a?this.$editor().wrapSelection("createLink",a,!0):void 0},activeState:function(a){return a?"A"===a[0].tagName:!1},onElementSelect:{element:"a",action:function(a,d,e){a.preventDefault(),e.displayElements.popover.css("width","435px");var f=e.displayElements.popoverContainer;f.empty(),f.css("line-height","28px");var g=angular.element(''+d.attr("href")+"");g.css({display:"inline-block","max-width":"200px",overflow:"hidden","text-overflow":"ellipsis","white-space":"nowrap","vertical-align":"middle"}),f.append(g);var h=angular.element(''),i=angular.element('');i.on("click",function(a){a.preventDefault();var f=b.prompt(c.insertLink.dialogPrompt,d.attr("href"));f&&""!==f&&"http://"!==f&&(d.attr("href",f),e.updateTaBindtaTextElement()),e.hidePopover()}),h.append(i);var j=angular.element('');j.on("click",function(a){a.preventDefault(),d.replaceWith(d.contents()),e.updateTaBindtaTextElement(),e.hidePopover()}),h.append(j);var k=angular.element('');"_blank"===d.attr("target")&&k.addClass("active"),k.on("click",function(a){a.preventDefault(),d.attr("target","_blank"===d.attr("target")?"":"_blank"),k.toggleClass("active"),e.updateTaBindtaTextElement()}),h.append(k),f.append(h),e.showPopover(d)}}})}]),function(){"Use Strict";function a(a){try{return 0!==angular.element(a).length}catch(b){return!1}}function b(a,c){var d=[],e=a.children();return e.length&&angular.forEach(e,function(a){d=d.concat(b(angular.element(a),c))}),void 0!==a.attr(c)&&d.push(a),d}function c(b,c){if(!b||""===b||n.hasOwnProperty(b))throw"textAngular Error: A unique name is required for a Tool Definition";if(c.display&&(""===c.display||!a(c.display))||!c.display&&!c.buttontext&&!c.iconclass)throw'textAngular Error: Tool Definition for "'+b+'" does not have a valid display/iconclass/buttontext value';n[b]=c}var d=!1;/AppleWebKit\/([\d.]+)/.exec(navigator.userAgent)&&(document.addEventListener("click",function(){var a=window.event.target;if(d&&null!==a){for(var b=!1,c=a;null!==c&&"html"!==c.tagName.toLowerCase()&&!b;)b="true"===c.contentEditable,c=c.parentNode;b||(document.getElementById("textAngular-editableFix-010203040506070809").setSelectionRange(0,0),a.focus())}d=!1},!1),angular.element(document).ready(function(){angular.element(document.body).append(angular.element(''))}));var e=function(){var a,b=-1,c=window.navigator.userAgent,d=c.indexOf("MSIE "),e=c.indexOf("Trident/");if(d>0)b=parseInt(c.substring(d+5,c.indexOf(".",d)),10);else if(e>0){var f=c.indexOf("rv:");b=parseInt(c.substring(f+3,c.indexOf(".",f)),10)}return b>-1?b:a}();"function"!=typeof String.prototype.trim&&(String.prototype.trim=function(){return this.replace(/^\s\s*/,"").replace(/\s\s*$/,"")});var f,g,h,i,j;if(e>8||void 0===e){var k=function(){var a=document.createElement("style");return/AppleWebKit\/([\d.]+)/.exec(navigator.userAgent)&&a.appendChild(document.createTextNode("")),document.head.insertBefore(a,document.head.firstChild),a.sheet}();f=function(){var a=document.createElement("style");return/AppleWebKit\/([\d.]+)/.exec(navigator.userAgent)&&a.appendChild(document.createTextNode("")),document.head.appendChild(a),a.sheet}(),g=function(a,b){i(f,a,b)},i=function(a,b,c){var d;return a.rules?d=Math.max(a.rules.length-1,0):a.cssRules&&(d=Math.max(a.cssRules.length-1,0)),a.insertRule?a.insertRule(b+"{"+c+"}",d):a.addRule(b,c,d),d},h=function(a){j(f,a)},j=function(a,b){a.removeRule?a.removeRule(b):a.deleteRule(b)},i(k,".ta-scroll-window.form-control","height: auto; min-height: 300px; overflow: auto; font-family: inherit; font-size: 100%; position: relative; padding: 0;"),i(k,".ta-root.focussed .ta-scroll-window.form-control","border-color: #66afe9; outline: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);"),i(k,".ta-editor.ta-html","min-height: 300px; height: auto; overflow: auto; font-family: inherit; font-size: 100%;"),i(k,".ta-scroll-window > .ta-bind","height: auto; min-height: 300px; padding: 6px 12px;"),i(k,".ta-root .ta-resizer-handle-overlay","z-index: 100; position: absolute; display: none;"),i(k,".ta-root .ta-resizer-handle-overlay > .ta-resizer-handle-info","position: absolute; bottom: 16px; right: 16px; border: 1px solid black; background-color: #FFF; padding: 0 4px; opacity: 0.7;"),i(k,".ta-root .ta-resizer-handle-overlay > .ta-resizer-handle-background","position: absolute; bottom: 5px; right: 5px; left: 5px; top: 5px; border: 1px solid black; background-color: rgba(0, 0, 0, 0.2);"),i(k,".ta-root .ta-resizer-handle-overlay > .ta-resizer-handle-corner","width: 10px; height: 10px; position: absolute;"),i(k,".ta-root .ta-resizer-handle-overlay > .ta-resizer-handle-corner-tl","top: 0; left: 0; border-left: 1px solid black; border-top: 1px solid black;"),i(k,".ta-root .ta-resizer-handle-overlay > .ta-resizer-handle-corner-tr","top: 0; right: 0; border-right: 1px solid black; border-top: 1px solid black;"),i(k,".ta-root .ta-resizer-handle-overlay > .ta-resizer-handle-corner-bl","bottom: 0; left: 0; border-left: 1px solid black; border-bottom: 1px solid black;"),i(k,".ta-root .ta-resizer-handle-overlay > .ta-resizer-handle-corner-br","bottom: 0; right: 0; border: 1px solid black; cursor: se-resize; background-color: white;")}var l=!1,m=angular.module("textAngular",["ngSanitize","textAngularSetup"]),n={};m.constant("taRegisterTool",c),m.value("taTools",n),m.config([function(){angular.forEach(n,function(a,b){delete n[b]})}]),m.directive("textAngular",["$compile","$timeout","taOptions","taSelection","taExecCommand","textAngularManager","$window","$document","$animate","$log",function(a,b,c,d,e,f,g,h,i,j){return{require:"?ngModel",scope:{},restrict:"EA",link:function(k,l,m,n){var o,p,q,r,s,t,u,v,w,x=m.serial?m.serial:Math.floor(1e16*Math.random()),y=m.name?m.name:"textAngularEditor"+x,z=function(a,c,d){b(function(){var b=function(){a.off(c,b),d()};a.on(c,b)},100)};w=e(m.taDefaultWrap),angular.extend(k,angular.copy(c),{wrapSelection:function(a,b,c){w(a,!1,b),c&&k["reApplyOnSelectorHandlerstaTextElement"+x](),k.displayElements.text[0].focus()},showHtml:!1}),m.taFocussedClass&&(k.classes.focussed=m.taFocussedClass),m.taTextEditorClass&&(k.classes.textEditor=m.taTextEditorClass),m.taHtmlEditorClass&&(k.classes.htmlEditor=m.taHtmlEditorClass),m.taTextEditorSetup&&(k.setup.textEditorSetup=k.$parent.$eval(m.taTextEditorSetup)),m.taHtmlEditorSetup&&(k.setup.htmlEditorSetup=k.$parent.$eval(m.taHtmlEditorSetup)),k.fileDropHandler=m.taFileDrop?k.$parent.$eval(m.taFileDrop):k.defaultFileDropHandler,u=l[0].innerHTML,l[0].innerHTML="",k.displayElements={forminput:angular.element(""),html:angular.element(""),text:angular.element(""),scrollWindow:angular.element(""),popover:angular.element(''),popoverArrow:angular.element(''),popoverContainer:angular.element(''),resize:{overlay:angular.element(''),background:angular.element(''),anchors:[angular.element(''),angular.element(''),angular.element(''),angular.element('')],info:angular.element('')}},k.displayElements.popover.append(k.displayElements.popoverArrow),k.displayElements.popover.append(k.displayElements.popoverContainer),k.displayElements.scrollWindow.append(k.displayElements.popover),k.displayElements.popover.on("mousedown",function(a,b){return b&&angular.extend(a,b),a.preventDefault(),!1}),k.showPopover=function(a){k.displayElements.popover.css("display","block"),k.reflowPopover(a),i.addClass(k.displayElements.popover,"in"),z(l,"click keyup",function(){k.hidePopover()})},k.reflowPopover=function(a){k.displayElements.text[0].offsetHeight-51>a[0].offsetTop?(k.displayElements.popover.css("top",a[0].offsetTop+a[0].offsetHeight+"px"),k.displayElements.popover.removeClass("top").addClass("bottom")):(k.displayElements.popover.css("top",a[0].offsetTop-54+"px"),k.displayElements.popover.removeClass("bottom").addClass("top"));var b=k.displayElements.text[0].offsetWidth-k.displayElements.popover[0].offsetWidth,c=a[0].offsetLeft+a[0].offsetWidth/2-k.displayElements.popover[0].offsetWidth/2;k.displayElements.popover.css("left",Math.max(0,Math.min(b,c))+"px"),k.displayElements.popoverArrow.css("margin-left",Math.min(c,Math.max(0,c-b))-11+"px")},k.hidePopover=function(){i.removeClass(k.displayElements.popover,"in",function(){k.displayElements.popover.css("display",""),k.displayElements.popoverContainer.attr("style",""),k.displayElements.popoverContainer.attr("class","popover-content")})},k.displayElements.resize.overlay.append(k.displayElements.resize.background),angular.forEach(k.displayElements.resize.anchors,function(a){k.displayElements.resize.overlay.append(a)}),k.displayElements.resize.overlay.append(k.displayElements.resize.info),k.displayElements.scrollWindow.append(k.displayElements.resize.overlay),k.reflowResizeOverlay=function(a){a=angular.element(a)[0],k.displayElements.resize.overlay.css({display:"block",left:a.offsetLeft-5+"px",top:a.offsetTop-5+"px",width:a.offsetWidth+10+"px",height:a.offsetHeight+10+"px"}),k.displayElements.resize.info.text(a.offsetWidth+" x "+a.offsetHeight)},k.showResizeOverlay=function(a){var b=function(b){var c={width:parseInt(a.attr("width")),height:parseInt(a.attr("height")),x:b.clientX,y:b.clientY};void 0===c.width&&(c.width=a[0].offsetWidth),void 0===c.height&&(c.height=a[0].offsetHeight),k.hidePopover();var d=c.height/c.width,e=function(b){var e={x:Math.max(0,c.width+(b.clientX-c.x)),y:Math.max(0,c.height+(b.clientY-c.y))},f=function(a,b){a=angular.element(a),"img"===a[0].tagName.toLowerCase()&&(b.height&&(a.attr("height",b.height),delete b.height),b.width&&(a.attr("width",b.width),delete b.width)),a.css(b)};if(b.shiftKey){var g=e.y/e.x;f(a,{width:d>g?e.x:e.y/d,height:d>g?e.x*d:e.y})}else f(a,{width:e.x,height:e.y});k.reflowResizeOverlay(a)};h.find("body").on("mousemove",e),z(k.displayElements.resize.overlay,"mouseup",function(){h.find("body").off("mousemove",e),k.showPopover(a)}),b.stopPropagation(),b.preventDefault()};k.displayElements.resize.anchors[3].on("mousedown",b),k.reflowResizeOverlay(a),z(l,"click",function(){k.hideResizeOverlay()})},k.hideResizeOverlay=function(){k.displayElements.resize.overlay.css("display","")},k.setup.htmlEditorSetup(k.displayElements.html),k.setup.textEditorSetup(k.displayElements.text),k.displayElements.html.attr({id:"taHtmlElement"+x,"ng-show":"showHtml","ta-bind":"ta-bind","ng-model":"html"}),k.displayElements.text.attr({id:"taTextElement"+x,contentEditable:"true","ta-bind":"ta-bind","ng-model":"html"}),k.displayElements.scrollWindow.attr({"ng-hide":"showHtml"}),m.taDefaultWrap&&k.displayElements.text.attr("ta-default-wrap",m.taDefaultWrap),m.taUnsafeSanitizer&&(k.displayElements.text.attr("ta-unsafe-sanitizer",m.taUnsafeSanitizer),k.displayElements.html.attr("ta-unsafe-sanitizer",m.taUnsafeSanitizer)),k.displayElements.scrollWindow.append(k.displayElements.text),l.append(k.displayElements.scrollWindow),l.append(k.displayElements.html),k.displayElements.forminput.attr("name",y),l.append(k.displayElements.forminput),m.tabindex&&(l.removeAttr("tabindex"),k.displayElements.text.attr("tabindex",m.tabindex),k.displayElements.html.attr("tabindex",m.tabindex)),m.placeholder&&(k.displayElements.text.attr("placeholder",m.placeholder),k.displayElements.html.attr("placeholder",m.placeholder)),m.taDisabled&&(k.displayElements.text.attr("ta-readonly","disabled"),k.displayElements.html.attr("ta-readonly","disabled"),k.disabled=k.$parent.$eval(m.taDisabled),k.$parent.$watch(m.taDisabled,function(a){k.disabled=a,k.disabled?l.addClass(k.classes.disabled):l.removeClass(k.classes.disabled)})),a(k.displayElements.scrollWindow)(k),a(k.displayElements.html)(k),k.updateTaBindtaTextElement=k["updateTaBindtaTextElement"+x],k.updateTaBindtaHtmlElement=k["updateTaBindtaHtmlElement"+x],l.addClass("ta-root"),k.displayElements.scrollWindow.addClass("ta-text ta-editor "+k.classes.textEditor),k.displayElements.html.addClass("ta-html ta-editor "+k.classes.htmlEditor),k._actionRunning=!1;var A=!1;if(k.startAction=function(){return k._actionRunning=!0,g.rangy&&g.rangy.saveSelection?(A=g.rangy.saveSelection(),function(){A&&g.rangy.restoreSelection(A)}):void 0},k.endAction=function(){k._actionRunning=!1,A&&g.rangy.removeMarkers(A),A=!1,k.updateSelectedStyles(),k.showHtml||k["updateTaBindtaTextElement"+x]()},s=function(){l.addClass(k.classes.focussed),v.focus()},k.displayElements.html.on("focus",s),k.displayElements.text.on("focus",s),t=function(a){return k._actionRunning||h[0].activeElement===k.displayElements.html[0]||h[0].activeElement===k.displayElements.text[0]||(l.removeClass(k.classes.focussed),v.unfocus(),b(function(){l.triggerHandler("blur")},0)),a.preventDefault(),!1},k.displayElements.html.on("blur",t),k.displayElements.text.on("blur",t),k.queryFormatBlockState=function(a){return!k.showHtml&&a.toLowerCase()===h[0].queryCommandValue("formatBlock").toLowerCase()},k.queryCommandState=function(a){return k.showHtml?"":h[0].queryCommandState(a)},k.switchView=function(){k.showHtml=!k.showHtml,k.showHtml?b(function(){return k.displayElements.html[0].focus()},100):b(function(){return k.displayElements.text[0].focus()},100)},m.ngModel){var B=!0;n.$render=function(){if(B){B=!1;var a=k.$parent.$eval(m.ngModel);void 0!==a&&null!==a||!u||""===u||n.$setViewValue(u)}k.displayElements.forminput.val(n.$viewValue),k._elementSelectTriggered||h[0].activeElement===k.displayElements.html[0]||h[0].activeElement===k.displayElements.text[0]||(k.html=n.$viewValue||"")};var C=function(a){return m.required&&n.$setValidity("required",!(!a||""===a.trim())),a};n.$parsers.push(C),n.$formatters.push(C)}else k.displayElements.forminput.val(u),k.html=u;if(k.$watch("html",function(a,b){a!==b&&(m.ngModel&&n.$viewValue!==a&&n.$setViewValue(a),k.displayElements.forminput.val(a))}),m.taTargetToolbars)v=f.registerEditor(y,k,m.taTargetToolbars.split(","));else{var D=angular.element('');m.taToolbar&&D.attr("ta-toolbar",m.taToolbar),m.taToolbarClass&&D.attr("ta-toolbar-class",m.taToolbarClass),m.taToolbarGroupClass&&D.attr("ta-toolbar-group-class",m.taToolbarGroupClass),m.taToolbarButtonClass&&D.attr("ta-toolbar-button-class",m.taToolbarButtonClass),m.taToolbarActiveButtonClass&&D.attr("ta-toolbar-active-button-class",m.taToolbarActiveButtonClass),m.taFocussedClass&&D.attr("ta-focussed-class",m.taFocussedClass),l.prepend(D),a(D)(k.$parent),v=f.registerEditor(y,k,["textAngularToolbar"+x])}k.$on("$destroy",function(){f.unregisterEditor(y)}),k.$on("ta-element-select",function(a,b){v.triggerElementSelect(a,b)}),k.$on("ta-drop-event",function(a,b,c,d){k.displayElements.text[0].focus(),d&&d.files&&d.files.length>0&&(angular.forEach(d.files,function(a){try{return k.fileDropHandler(a,k.wrapSelection)||k.fileDropHandler!==k.defaultFileDropHandler&&k.defaultFileDropHandler(a,k.wrapSelection)}catch(b){j.error(b)}}),c.preventDefault(),c.stopPropagation())}),k._bUpdateSelectedStyles=!1,k.updateSelectedStyles=function(){var a;void 0!==(a=d.getSelectionElement())&&a.parentNode!==k.displayElements.text[0]?v.updateSelectedStyles(angular.element(a)):v.updateSelectedStyles(),k._bUpdateSelectedStyles&&b(k.updateSelectedStyles,200)},o=function(){k._bUpdateSelectedStyles||(k._bUpdateSelectedStyles=!0,k.$apply(function(){k.updateSelectedStyles()}))},k.displayElements.html.on("keydown",o),k.displayElements.text.on("keydown",o),p=function(){k._bUpdateSelectedStyles=!1},k.displayElements.html.on("keyup",p),k.displayElements.text.on("keyup",p),q=function(a,b){b&&angular.extend(a,b),k.$apply(function(){return v.sendKeyCommand(a)?(k._bUpdateSelectedStyles||k.updateSelectedStyles(),a.preventDefault(),!1):void 0})},k.displayElements.html.on("keypress",q),k.displayElements.text.on("keypress",q),r=function(){k._bUpdateSelectedStyles=!1,k.$apply(function(){k.updateSelectedStyles()})},k.displayElements.html.on("mouseup",r),k.displayElements.text.on("mouseup",r)}}}]).factory("taBrowserTag",[function(){return function(a){return a?""===a?void 0===e?"div":8>=e?"P":"p":8>=e?a.toUpperCase():a:8>=e?"P":"p"}}]).factory("taExecCommand",["taSelection","taBrowserTag","$document",function(a,b,c){var d=/^(address|article|aside|audio|blockquote|canvas|dd|div|dl|fieldset|figcaption|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|noscript|ol|output|p|pre|section|table|tfoot|ul|video)$/gi,e=/^(ul|li|ol)$/gi,f=function(b,c){var d,e,f=b.find("li");for(e=f.length-1;e>=0;e--)d=angular.element("<"+c+">"+f[e].innerHTML+""+c+">"),b.after(d);b.remove(),a.setSelectionToElementEnd(d[0])},g=function(b,c){var d=angular.element("<"+c+">"+b[0].innerHTML+""+c+">");b.after(d),b.remove(),a.setSelectionToElementEnd(d.find("li")[0])},h=function(c,d,e){for(var f="",g=0;g"+c[g].innerHTML+""+b("li")+">";var h=angular.element("<"+e+">"+f+""+e+">");d.after(h),d.remove(),a.setSelectionToElementEnd(h.find("li")[0])};return function(i){return i=b(i),function(j,k,l){var m,n,o,p,q,r=angular.element("<"+i+">"),s=a.getSelectionElement(),t=angular.element(s);if(void 0!==s){var u=s.tagName.toLowerCase();if("insertorderedlist"===j.toLowerCase()||"insertunorderedlist"===j.toLowerCase()){var v=b("insertorderedlist"===j.toLowerCase()?"ol":"ul");if(u===v)return f(t,i);if("li"===u&&t.parent()[0].tagName.toLowerCase()===v&&1===t.parent().children().length)return f(t.parent(),i);if("li"===u&&t.parent()[0].tagName.toLowerCase()!==v&&1===t.parent().children().length)return g(t.parent(),v);if(u.match(d)&&!t.hasClass("ta-bind")){if("ol"===u||"ul"===u)return g(t,v);var w=!1;return angular.forEach(t.children(),function(a){a.tagName.match(d)&&(w=!0)}),w?h(t.children(),t,v):h([angular.element(""+s.innerHTML+"")[0]],t,v)}if(u.match(d)){if(p=a.getOnlySelectedElements(),1===p.length&&("ol"===p[0].tagName.toLowerCase()||"ul"===p[0].tagName.toLowerCase()))return p[0].tagName.toLowerCase()===v?f(angular.element(p[0]),i):g(angular.element(p[0]),v);o="";var x=[];for(m=0;m"+y[0].innerHTML+""+b("li")+">",x.unshift(y)}return n=angular.element("<"+v+">"+o+""+v+">"),x.pop().replaceWith(n),angular.forEach(x,function(a){a.remove()}),void a.setSelectionToElementEnd(n[0])}}else if("formatblock"===j.toLowerCase()){var z=l.toLowerCase().replace(/[<>]/gi,"");for(n="li"===u?t.parent():t;!n[0].tagName.match(d);)n=n.parent(),u=n[0].tagName.toLowerCase();if(u===z){p=n.children();var A=!1;for(m=0;m"),r[0].innerHTML=D[m].outerHTML,D[m]=r[0]),C.parent()[0].insertBefore(D[m],C[0]);C.remove()}return void a.setSelectionToElementEnd(n[0])}}try{c[0].execCommand(j,k,l)}catch(E){}}}}]).directive("taBind",["taSanitize","$timeout","$window","$document","taFixChrome","taBrowserTag","taSelection","taSelectableElements","taApplyCustomRenderers","taOptions",function(a,b,c,f,i,j,k,m,n,o){return{require:"ngModel",scope:{},link:function(j,p,q,r){var s,t,u=void 0!==p.attr("contenteditable")&&p.attr("contenteditable"),v=u||"textarea"===p[0].tagName.toLowerCase()||"input"===p[0].tagName.toLowerCase(),w=!1,x=!1,y=q.taUnsafeSanitizer||o.disableSanitizer;void 0===q.taDefaultWrap&&(q.taDefaultWrap="p"),""===q.taDefaultWrap?(s="",t=void 0===e?"
":e>=11?"
":8>=e?"
":"
"):(s=void 0===e||e>=11?"<"+q.taDefaultWrap+">
"+q.taDefaultWrap+">":8>=e?"<"+q.taDefaultWrap.toUpperCase()+">"+q.taDefaultWrap.toUpperCase()+">":"<"+q.taDefaultWrap+">"+q.taDefaultWrap+">",t=void 0===e||e>=11?"<"+q.taDefaultWrap+">
"+q.taDefaultWrap+">":8>=e?"<"+q.taDefaultWrap.toUpperCase()+"> "+q.taDefaultWrap.toUpperCase()+">":"<"+q.taDefaultWrap+"> "+q.taDefaultWrap+">"),p.addClass("ta-bind");
+var z=function(){if(u)return p[0].innerHTML;if(v)return p.val();throw"textAngular Error: attempting to update non-editable taBind"},A=function(a){a||(a=z()),a===t?""!==r.$viewValue&&r.$setViewValue(""):r.$viewValue!==a&&r.$setViewValue(a)};if(j.$parent["updateTaBind"+(q.id||"")]=function(){w||A()},v)if(u){if(p.on("cut",function(a){w?a.preventDefault():b(function(){A()},0)}),p.on("paste",function(a,b){b&&angular.extend(a,b);var d;if(a.clipboardData||a.originalEvent&&a.originalEvent.clipboardData?d=(a.originalEvent||a).clipboardData.getData("text/plain"):c.clipboardData&&(d=c.clipboardData.getData("Text")),!d&&!w)return!0;if(a.preventDefault(),!w){var e=angular.element("");if(e[0].innerHTML=d,d=e.text(),f[0].selection){var g=f[0].selection.createRange();g.pasteHTML(d)}else f[0].execCommand("insertText",!1,d);A()}}),p.on("keyup",function(a,b){if(b&&angular.extend(a,b),!w){if(""!==s&&13===a.keyCode&&!a.shiftKey){var c=k.getSelectionElement();if(c.tagName.toLowerCase()!==q.taDefaultWrap&&"li"!==c.tagName.toLowerCase()&&(""===c.innerHTML.trim()||"
"===c.innerHTML.trim())){var d=angular.element(s);angular.element(c).replaceWith(d),k.setSelectionToElementStart(d[0])}}var e=z();""!==s&&""===e.trim()&&(p[0].innerHTML=s,k.setSelectionToElementStart(p.children()[0])),A(e)}}),p.on("blur",function(){x=!1,w||A(),r.$render()}),q.placeholder&&(e>8||void 0===e)){var B;if(!q.id)throw"textAngular Error: An unique ID is required for placeholders to work";B=g("#"+q.id+".placeholder-text:before",'content: "'+q.placeholder+'"'),j.$on("$destroy",function(){h(B)})}p.on("focus",function(){x=!0,r.$render()}),p.on("mousedown",function(a,b){b&&angular.extend(a,b),a.stopPropagation()})}else p.on("paste cut",function(){w||b(function(){r.$setViewValue(z())},0)}),p.on("change blur",function(){w||r.$setViewValue(z())});var C=function(b){return r.$oldViewValue=a(i(b),r.$oldViewValue,y)},D=function(a){return q.required&&r.$setValidity("required",!(!a||a.trim()===t||""===a.trim())),a};r.$parsers.push(C),r.$parsers.push(D),r.$formatters.push(C),r.$formatters.push(D);var E=function(a){return j.$emit("ta-element-select",this),a.preventDefault(),!1},F=function(a,c){if(c&&angular.extend(a,c),!l&&!w){l=!0;var d;d=a.originalEvent?a.originalEvent.dataTransfer:a.dataTransfer,j.$emit("ta-drop-event",this,a,d),b(function(){l=!1},100)}};j.$parent["reApplyOnSelectorHandlers"+(q.id||"")]=function(){w||angular.forEach(m,function(a){p.find(a).off("click",E).on("click",E)})};var G=function(a){p[0].innerHTML=a};r.$render=function(){var a=r.$viewValue||"";f[0].activeElement!==p[0]?u?(q.placeholder?""===a?(x?p.removeClass("placeholder-text"):p.addClass("placeholder-text"),G(s)):(p.removeClass("placeholder-text"),G(a)):G(""===a?s:a),w?p.off("drop",F):(angular.forEach(m,function(a){p.find(a).on("click",E)}),p.on("drop",F))):"textarea"!==p[0].tagName.toLowerCase()&&"input"!==p[0].tagName.toLowerCase()?G(n(a)):p.val(a):u&&p.removeClass("placeholder-text")},q.taReadonly&&(w=j.$parent.$eval(q.taReadonly),w?(p.addClass("ta-readonly"),("textarea"===p[0].tagName.toLowerCase()||"input"===p[0].tagName.toLowerCase())&&p.attr("disabled","disabled"),void 0!==p.attr("contenteditable")&&p.attr("contenteditable")&&p.removeAttr("contenteditable")):(p.removeClass("ta-readonly"),"textarea"===p[0].tagName.toLowerCase()||"input"===p[0].tagName.toLowerCase()?p.removeAttr("disabled"):u&&p.attr("contenteditable","true")),j.$parent.$watch(q.taReadonly,function(a,b){b!==a&&(a?(p.addClass("ta-readonly"),("textarea"===p[0].tagName.toLowerCase()||"input"===p[0].tagName.toLowerCase())&&p.attr("disabled","disabled"),void 0!==p.attr("contenteditable")&&p.attr("contenteditable")&&p.removeAttr("contenteditable"),angular.forEach(m,function(a){p.find(a).on("click",E)}),p.off("drop",F)):(p.removeClass("ta-readonly"),"textarea"===p[0].tagName.toLowerCase()||"input"===p[0].tagName.toLowerCase()?p.removeAttr("disabled"):u&&p.attr("contenteditable","true"),angular.forEach(m,function(a){p.find(a).off("click",E)}),p.on("drop",F)),w=a)})),u&&!w&&(angular.forEach(m,function(a){p.find(a).on("click",E)}),p.on("drop",F),p.on("blur",function(){/AppleWebKit\/([\d.]+)/.exec(navigator.userAgent)&&(d=!0)}))}}}]).factory("taApplyCustomRenderers",["taCustomRenderers",function(a){return function(c){var d=angular.element("");return d[0].innerHTML=c,angular.forEach(a,function(a){var c=[];a.selector&&""!==a.selector?c=d.find(a.selector):a.customAttribute&&""!==a.customAttribute&&(c=b(d,a.customAttribute)),angular.forEach(c,function(b){b=angular.element(b),a.selector&&""!==a.selector&&a.customAttribute&&""!==a.customAttribute?void 0!==b.attr(a.customAttribute)&&a.renderLogic(b):a.renderLogic(b)})}),d[0].innerHTML}}]).directive("taMaxText",function(){return{restrict:"A",require:"ngModel",link:function(a,b,c,d){function e(a){var b=angular.element("");b.html(a);var c=b.text().length;return f>=c?(d.$setValidity("taMaxText",!0),a):void d.$setValidity("taMaxText",!1)}var f=parseInt(a.$eval(c.taMaxText));if(isNaN(f))throw"Max text must be an integer";c.$observe("taMaxText",function(a){if(f=parseInt(a),isNaN(f))throw"Max text must be an integer";d.$dirty&&d.$setViewValue(d.$viewValue)}),d.$parsers.unshift(e)}}}).directive("taMinText",function(){return{restrict:"A",require:"ngModel",link:function(a,b,c,d){function e(a){var b=angular.element("");b.html(a);var c=b.text().length;return!c||c>=f?(d.$setValidity("taMinText",!0),a):void d.$setValidity("taMinText",!1)}var f=parseInt(a.$eval(c.taMinText));if(isNaN(f))throw"Min text must be an integer";c.$observe("taMinText",function(a){if(f=parseInt(a),isNaN(f))throw"Min text must be an integer";d.$dirty&&d.$setViewValue(d.$viewValue)}),d.$parsers.unshift(e)}}}).factory("taFixChrome",function(){var a=function(a){for(var b=angular.element(""+a+""),c=angular.element(b).find("span"),d=0;d0&&"BR"===e.next()[0].tagName&&e.next().remove(),e.replaceWith(e[0].innerHTML)))}var f=b[0].innerHTML.replace(/style="[^"]*?(line-height: 1.428571429;|color: inherit; line-height: 1.1;)[^"]*"/gi,"");return f!==b[0].innerHTML&&(b[0].innerHTML=f),b[0].innerHTML};return a}).factory("taSanitize",["$sanitize",function(a){return function(c,d,e){var f=angular.element(""+c+"");angular.forEach(b(f,"align"),function(a){a.css("text-align",a.attr("align")),a.removeAttr("align")});var g;c=f[0].innerHTML;try{g=a(c),e&&(g=c)}catch(h){g=d||""}return g}}]).directive("textAngularToolbar",["$compile","textAngularManager","taOptions","taTools","taToolExecuteAction","$window",function(a,b,c,d,e,f){return{scope:{name:"@"},restrict:"EA",link:function(g,h,i){if(!g.name||""===g.name)throw"textAngular Error: A toolbar requires a name";angular.extend(g,angular.copy(c)),i.taToolbar&&(g.toolbar=g.$parent.$eval(i.taToolbar)),i.taToolbarClass&&(g.classes.toolbar=i.taToolbarClass),i.taToolbarGroupClass&&(g.classes.toolbarGroup=i.taToolbarGroupClass),i.taToolbarButtonClass&&(g.classes.toolbarButton=i.taToolbarButtonClass),i.taToolbarActiveButtonClass&&(g.classes.toolbarButtonActive=i.taToolbarActiveButtonClass),i.taFocussedClass&&(g.classes.focussed=i.taFocussedClass),g.disabled=!0,g.focussed=!1,g._$element=h,h[0].innerHTML="",h.addClass("ta-toolbar "+g.classes.toolbar),g.$watch("focussed",function(){g.focussed?h.addClass(g.classes.focussed):h.removeClass(g.classes.focussed)});var j=function(b,c){var d;if(d=angular.element(b&&b.display?b.display:"