diff --git a/Gemfile b/Gemfile
index 87566eebfa..e958a9389e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -40,6 +40,8 @@ gem 'custom_error_message', :github => 'jeremydurham/custom-err-msg'
gem 'foreigner'
gem 'immigrant'
+gem 'whenever', require: false
+
# Gems used only for assets and not required
# in production environments by default.
group :assets do
diff --git a/Gemfile.lock b/Gemfile.lock
index 733ef43572..dd09513503 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -175,6 +175,7 @@ GEM
xpath (~> 2.0)
celluloid (0.15.2)
timers (~> 1.1.0)
+ chronic (0.10.2)
chunky_png (1.3.0)
climate_control (0.0.3)
activesupport (>= 3.0)
@@ -479,6 +480,9 @@ GEM
addressable (>= 2.2.7)
crack (>= 0.3.2)
websocket-driver (0.3.2)
+ whenever (0.9.2)
+ activesupport (>= 2.3.4)
+ chronic (>= 0.6.3)
xpath (2.0.0)
nokogiri (~> 1.3)
zeus (0.13.3)
@@ -552,3 +556,4 @@ DEPENDENCIES
unicorn
unicorn-rails
webmock
+ whenever
diff --git a/app/assets/images/loading.gif b/app/assets/images/loading.gif
new file mode 100644
index 0000000000..31b6c2c752
Binary files /dev/null and b/app/assets/images/loading.gif differ
diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee
index 93cf4b430b..ba9c1fa18d 100644
--- a/app/assets/javascripts/admin/bulk_product_update.js.coffee
+++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee
@@ -213,7 +213,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", [
if confirm("Are you sure?")
$http(
method: "DELETE"
- url: "/api/products/" + product.id
+ url: "/api/products/" + product.id + "/soft_delete"
).success (data) ->
$scope.products.splice $scope.products.indexOf(product), 1
DirtyProducts.deleteProduct product.id
diff --git a/app/assets/javascripts/darkswarm/all.js.coffee b/app/assets/javascripts/darkswarm/all.js.coffee
index 4727f7282a..a19bd1294f 100644
--- a/app/assets/javascripts/darkswarm/all.js.coffee
+++ b/app/assets/javascripts/darkswarm/all.js.coffee
@@ -8,6 +8,7 @@
#= require angular-sanitize
#= require angular-resource
#= require lodash.underscore.js
+#= require angular-scroll.min.js
#= require angular-google-maps.min.js
#= require ../shared/mm-foundation-tpls-0.2.0-SNAPSHOT
#= require ../shared/bindonce.min.js
diff --git a/app/assets/javascripts/darkswarm/controllers/authentication/login_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/authentication/login_controller.js.coffee
index da625e3d34..c24f7a70f2 100644
--- a/app/assets/javascripts/darkswarm/controllers/authentication/login_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/authentication/login_controller.js.coffee
@@ -1,11 +1,13 @@
-Darkswarm.controller "LoginCtrl", ($scope, $http, AuthenticationService, Redirections) ->
+Darkswarm.controller "LoginCtrl", ($scope, $http, AuthenticationService, Redirections, Loading) ->
$scope.path = "/login"
$scope.submit = ->
+ Loading.message = "Hold on a moment, we're logging you in"
$http.post("/user/spree_user/sign_in", {spree_user: $scope.spree_user}).success (data)->
if Redirections.after_login
location.href = location.origin + Redirections.after_login
else
location.href = location.origin + location.pathname # Strips out hash fragments
.error (data) ->
+ Loading.clear()
$scope.errors = data.message
diff --git a/app/assets/javascripts/darkswarm/controllers/hub_node_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/hub_node_controller.js.coffee
index c53f760e7d..b43b97e560 100644
--- a/app/assets/javascripts/darkswarm/controllers/hub_node_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/hub_node_controller.js.coffee
@@ -1,4 +1,4 @@
-Darkswarm.controller "HubNodeCtrl", ($scope, HashNavigation, Navigation, $location, $anchorScroll, $templateCache, CurrentHub) ->
+Darkswarm.controller "HubNodeCtrl", ($scope, HashNavigation, Navigation, $location, $templateCache, CurrentHub) ->
$scope.toggle = ->
HashNavigation.toggle $scope.hub.hash
@@ -7,6 +7,3 @@ Darkswarm.controller "HubNodeCtrl", ($scope, HashNavigation, Navigation, $locati
$scope.current = ->
$scope.hub.id is CurrentHub.id
-
- if $scope.open()
- $anchorScroll()
diff --git a/app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee
index bd57ee083c..ca7d86f5cf 100644
--- a/app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/hubs_controller.js.coffee
@@ -1,7 +1,7 @@
-Darkswarm.controller "HubsCtrl", ($scope, Hubs, $anchorScroll, $rootScope, HashNavigation) ->
+Darkswarm.controller "HubsCtrl", ($scope, Hubs, $document, $rootScope, HashNavigation) ->
$scope.Hubs = Hubs
$scope.hubs = Hubs.hubs
$rootScope.$on "$locationChangeSuccess", (newRoute, oldRoute) ->
if HashNavigation.active "hubs"
- $anchorScroll()
+ $document.scrollTo $("#hubs"), 100, 200
diff --git a/app/assets/javascripts/darkswarm/controllers/map_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/map_controller.js.coffee
index f6c7fb341a..7b0cd3607b 100644
--- a/app/assets/javascripts/darkswarm/controllers/map_controller.js.coffee
+++ b/app/assets/javascripts/darkswarm/controllers/map_controller.js.coffee
@@ -1,8 +1,3 @@
Darkswarm.controller "MapCtrl", ($scope, MapConfiguration, OfnMap)->
$scope.OfnMap = OfnMap
- $scope.map =
- center:
- latitude: -37.4713077
- longitude: 144.7851531
- zoom: 12
- styles: MapConfiguration.options
+ $scope.map = MapConfiguration.options
diff --git a/app/assets/javascripts/darkswarm/darkswarm.js.coffee b/app/assets/javascripts/darkswarm/darkswarm.js.coffee
index 2f07a327cb..b04487b330 100644
--- a/app/assets/javascripts/darkswarm/darkswarm.js.coffee
+++ b/app/assets/javascripts/darkswarm/darkswarm.js.coffee
@@ -7,6 +7,7 @@ window.Darkswarm = angular.module("Darkswarm", ["ngResource",
'templates',
'ngSanitize',
'google-maps',
+ 'duScroll',
'backstretch']).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')
diff --git a/app/assets/javascripts/darkswarm/directives/empties_cart.js.coffee b/app/assets/javascripts/darkswarm/directives/empties_cart.js.coffee
index 0b29180b57..4691d9b830 100644
--- a/app/assets/javascripts/darkswarm/directives/empties_cart.js.coffee
+++ b/app/assets/javascripts/darkswarm/directives/empties_cart.js.coffee
@@ -5,12 +5,13 @@ Darkswarm.directive "ofnEmptiesCart", (CurrentHub, CurrentOrder, Navigation, sto
template: "{{action}} {{hub.name}}"
link: (scope, elm, attr)->
# A hub is selected, we're changing to a different hub, and the cart isn't empty
- if CurrentHub.id and CurrentHub.id isnt scope.hub.id and not CurrentOrder.empty()
+ if CurrentHub.id and CurrentHub.id isnt scope.hub.id
scope.action = attr.change
- elm.bind 'click', (ev)->
- ev.preventDefault()
- if confirm "Are you sure? This will change your selected Hub and remove any items in you shopping cart."
- storage.clearAll() # One day this will have to be moar GRANULAR
- Navigation.go scope.hub.path
+ unless CurrentOrder.empty()
+ elm.bind 'click', (ev)->
+ ev.preventDefault()
+ if confirm "Are you sure? This will change your selected Hub and remove any items in you shopping cart."
+ storage.clearAll() # One day this will have to be moar GRANULAR
+ Navigation.go scope.hub.path
else
scope.action = attr.shop
diff --git a/app/assets/javascripts/darkswarm/directives/flash.js.coffee b/app/assets/javascripts/darkswarm/directives/flash.js.coffee
index a302a2a891..b5c9aaddd8 100644
--- a/app/assets/javascripts/darkswarm/directives/flash.js.coffee
+++ b/app/assets/javascripts/darkswarm/directives/flash.js.coffee
@@ -16,7 +16,7 @@ Darkswarm.directive "ofnFlash", (flash, $timeout, RailsFlashLoader)->
show = (message, type)=>
if message
$scope.flashes.push({message: message, type: typePairings[type]})
- $timeout($scope.delete, 5000)
+ $timeout($scope.delete, 10000)
$scope.delete = ->
$scope.flashes.shift()
diff --git a/app/assets/javascripts/darkswarm/directives/loading.js.coffee b/app/assets/javascripts/darkswarm/directives/loading.js.coffee
new file mode 100644
index 0000000000..febe56de9b
--- /dev/null
+++ b/app/assets/javascripts/darkswarm/directives/loading.js.coffee
@@ -0,0 +1,10 @@
+Darkswarm.directive "loading", (Loading)->
+ scope: {}
+ restrict: 'E'
+ templateUrl: 'loading.html'
+ controller: ($scope)->
+ $scope.Loading = Loading
+ $scope.show = ->
+ $scope.Loading.message?
+
+ link: ($scope, element, attr)->
diff --git a/app/assets/javascripts/darkswarm/directives/modal.js.coffee b/app/assets/javascripts/darkswarm/directives/modal.js.coffee
index c8ed5df4af..d727fa64c9 100644
--- a/app/assets/javascripts/darkswarm/directives/modal.js.coffee
+++ b/app/assets/javascripts/darkswarm/directives/modal.js.coffee
@@ -8,10 +8,9 @@ Darkswarm.directive "ofnModal", ($modal)->
link: (scope, elem, attrs, ctrl, transclude)->
scope.title = attrs.title
contents = null
- # We're using an isolate scope, which is a child of the original scope
- # We have to compile the transclude against the original scope, not the isolate
- transclude scope.$parent, (clone)->
- contents = clone
-
elem.on "click", =>
+ # We're using an isolate scope, which is a child of the original scope
+ # We have to compile the transclude against the original scope, not the isolate
+ transclude scope.$parent, (clone)->
+ contents = clone
scope.modalInstance = $modal.open(controller: ctrl, template: contents, scope: scope.$parent)
diff --git a/app/assets/javascripts/darkswarm/directives/render_svg.js.coffee b/app/assets/javascripts/darkswarm/directives/render_svg.js.coffee
new file mode 100644
index 0000000000..86ffea8c5b
--- /dev/null
+++ b/app/assets/javascripts/darkswarm/directives/render_svg.js.coffee
@@ -0,0 +1,10 @@
+Darkswarm.directive "renderSvg", ()->
+ restrict: 'E'
+ priority: 99
+ template: ""
+ link: (scope, elem, attr)->
+ if /.svg/.test attr.path # Only do this if we've got an svg
+ $.ajax
+ url: attr.path
+ success: (html)->
+ elem.html($(html).find("svg"))
diff --git a/app/assets/javascripts/darkswarm/directives/scroll_after_load.js.coffee b/app/assets/javascripts/darkswarm/directives/scroll_after_load.js.coffee
new file mode 100644
index 0000000000..ab2e0a6e3d
--- /dev/null
+++ b/app/assets/javascripts/darkswarm/directives/scroll_after_load.js.coffee
@@ -0,0 +1,10 @@
+Darkswarm.directive 'scrollAfterLoad', ($timeout, $location, $document)->
+ restrict: "A"
+ link: (scope, element, attr) ->
+ if scope.$last is true
+ $(window).load ->
+ $timeout ->
+ elem = $("##{$location.hash()}")
+ if elem.length > 0
+ $document.scrollTo elem , 100, 200, (x)->
+ x * (2 - x)
diff --git a/app/assets/javascripts/darkswarm/services/loading.js.coffee b/app/assets/javascripts/darkswarm/services/loading.js.coffee
new file mode 100644
index 0000000000..af46487cd3
--- /dev/null
+++ b/app/assets/javascripts/darkswarm/services/loading.js.coffee
@@ -0,0 +1,5 @@
+Darkswarm.factory "Loading", ->
+ new class Loading
+ message: null
+ clear: =>
+ @message = null
diff --git a/app/assets/javascripts/darkswarm/services/map_configuration.js.coffee b/app/assets/javascripts/darkswarm/services/map_configuration.js.coffee
index bdcd6c8346..8f1d735357 100644
--- a/app/assets/javascripts/darkswarm/services/map_configuration.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/map_configuration.js.coffee
@@ -1,5 +1,11 @@
Darkswarm.factory "MapConfiguration", ->
new class MapConfiguration
- # From http://snazzymaps.com/style/15/subtle-grayscale
- options: [{"featureType":"landscape","stylers":[{"saturation":-100},{"lightness":65},{"visibility":"on"}]},{"featureType":"poi","stylers":[{"saturation":-100},{"lightness":51},{"visibility":"simplified"}]},{"featureType":"road.highway","stylers":[{"saturation":-100},{"visibility":"simplified"}]},{"featureType":"road.arterial","stylers":[{"saturation":-100},{"lightness":30},{"visibility":"on"}]},{"featureType":"road.local","stylers":[{"saturation":-100},{"lightness":40},{"visibility":"on"}]},{"featureType":"transit","stylers":[{"saturation":-100},{"visibility":"simplified"}]},{"featureType":"administrative.province","stylers":[{"visibility":"off"}]},{"featureType":"water","elementType":"labels","stylers":[{"visibility":"on"},{"lightness":-25},{"saturation":-100}]},{"featureType":"water","elementType":"geometry","stylers":[{"hue":"#ffff00"},{"lightness":-25},{"saturation":-97}]}]
+ options:
+ center:
+ latitude: -37.4713077
+ longitude: 144.7851531
+ zoom: 12
+ additional_options: {}
+ #mapTypeId: 'satellite'
+ styles: [{"featureType":"landscape","stylers":[{"saturation":-100},{"lightness":65},{"visibility":"on"}]},{"featureType":"poi","stylers":[{"saturation":-100},{"lightness":51},{"visibility":"simplified"}]},{"featureType":"road.highway","stylers":[{"saturation":-100},{"visibility":"simplified"}]},{"featureType":"road.arterial","stylers":[{"saturation":-100},{"lightness":30},{"visibility":"on"}]},{"featureType":"road.local","stylers":[{"saturation":-100},{"lightness":40},{"visibility":"on"}]},{"featureType":"transit","stylers":[{"saturation":-100},{"visibility":"simplified"}]},{"featureType":"administrative.province","stylers":[{"visibility":"off"}]},{"featureType":"water","elementType":"labels","stylers":[{"visibility":"on"},{"lightness":-25},{"saturation":-100}]},{"featureType":"water","elementType":"geometry","stylers":[{"hue":"#ffff00"},{"lightness":-25},{"saturation":-97}]}]
diff --git a/app/assets/javascripts/darkswarm/services/order.js.coffee b/app/assets/javascripts/darkswarm/services/order.js.coffee
index a90cec9e34..e119365685 100644
--- a/app/assets/javascripts/darkswarm/services/order.js.coffee
+++ b/app/assets/javascripts/darkswarm/services/order.js.coffee
@@ -1,4 +1,4 @@
-Darkswarm.factory 'Order', ($resource, order, $http, Navigation, storage, CurrentHub, RailsFlashLoader)->
+Darkswarm.factory 'Order', ($resource, order, $http, Navigation, storage, CurrentHub, RailsFlashLoader, Loading)->
new class Order
errors: {}
secrets: {}
@@ -20,9 +20,11 @@ Darkswarm.factory 'Order', ($resource, order, $http, Navigation, storage, Curren
defaultValue: true
submit: ->
+ Loading.message = "Submitting your order: please wait"
$http.put('/checkout', {order: @preprocess()}).success (data, status)=>
Navigation.go data.path
.error (response, status)=>
+ Loading.clear()
@errors = response.errors
RailsFlashLoader.loadFlash(response.flash)
diff --git a/app/assets/javascripts/templates/loading.html.haml b/app/assets/javascripts/templates/loading.html.haml
new file mode 100644
index 0000000000..85a4359760
--- /dev/null
+++ b/app/assets/javascripts/templates/loading.html.haml
@@ -0,0 +1,8 @@
+#loading{"ng-show" => "show()"}
+ %modal-backdrop
+ #message
+ %ul.loader
+ %li
+ %li
+ %li
+ %h1 {{ Loading.message }}
diff --git a/app/assets/stylesheets/darkswarm/active_table.css.sass b/app/assets/stylesheets/darkswarm/active_table.css.sass
index 5843c0f547..b56ac17590 100644
--- a/app/assets/stylesheets/darkswarm/active_table.css.sass
+++ b/app/assets/stylesheets/darkswarm/active_table.css.sass
@@ -11,7 +11,6 @@
&:first-child
cursor: pointer
-
.active_table .active_table_node
@include csstrans
display: block
diff --git a/app/assets/stylesheets/darkswarm/active_table_search.css.sass b/app/assets/stylesheets/darkswarm/active_table_search.css.sass
index e558c41511..e862f4322b 100644
--- a/app/assets/stylesheets/darkswarm/active_table_search.css.sass
+++ b/app/assets/stylesheets/darkswarm/active_table_search.css.sass
@@ -8,7 +8,7 @@
left: 26px
top: 12px
font-size: 1.6em
- z-index: 999
+ z-index: 2
color: #b2b2b2
input[type="text"]
diff --git a/app/assets/stylesheets/darkswarm/hub_node.css.sass b/app/assets/stylesheets/darkswarm/hub_node.css.sass
index d74eb35774..7dd6686d09 100644
--- a/app/assets/stylesheets/darkswarm/hub_node.css.sass
+++ b/app/assets/stylesheets/darkswarm/hub_node.css.sass
@@ -1,4 +1,5 @@
@import branding
+@import mixins
.hubs
.active_table .active_table_node
@@ -36,7 +37,21 @@
//Open row
&.open
.fat-taxons
+ @include csstrans
margin-right: 0.5rem
+ text-transform: uppercase
+ font-size: 1rem
+ line-height: 1
+ color: $dark-grey
+ // border: 1px solid $disabled-bright
+ display: inline-block
+ padding: 0.2rem 0.5rem 0.35rem 0.35rem
+
+ object.taxon
+ height: 20px
+ // &:hover, &.hover, &:active
+ // background: rgba(255,255,255,0.5)
+
.active_table_row:first-child
border-top: 1px solid $clr-brick
border-left: 1px solid $clr-brick
diff --git a/app/assets/stylesheets/darkswarm/loading.sass b/app/assets/stylesheets/darkswarm/loading.sass
new file mode 100644
index 0000000000..cebb7edfd3
--- /dev/null
+++ b/app/assets/stylesheets/darkswarm/loading.sass
@@ -0,0 +1,68 @@
+@import "compass/css3/user-interface"
+
+#loading
+ .reveal-modal-bg
+ z-index: 101
+ background: rgba(0, 0, 0, 0.85)
+ #message
+ width: 100%
+ text-align: center
+ position: absolute
+ z-index: 102
+ margin: auto
+ top: 0
+ left: 0
+ bottom: 0
+ right: 0
+ @include user-select(none)
+ h1
+ color: white
+ position: fixed
+ text-align: center
+ left: 0
+ right: 0
+ margin: 0 auto
+ top: 55%
+ width: 100%
+ height: 400px
+ overflow: visible
+
+ .loader
+ position: fixed
+ margin: 0 auto
+ left: 0
+ right: 0
+ top: 50%
+ margin-top: -30px
+ width: 60px
+ height: 60px
+ list-style: none
+
+ li
+ background-color: #FFFFFF
+ width: 10px
+ height: 10px
+ float: right
+ margin-right: 5px
+ box-shadow: 0px 100px 20px rgba(0, 0, 0, 0.2)
+
+ li:first-child
+ -webkit-animation: loadbars 0.6s cubic-bezier(0.645, 0.045, 0.355, 1) infinite 0s
+
+ li:nth-child(2)
+ -webkit-animation: loadbars 0.6s ease-in-out infinite -0.2s
+
+ li:nth-child(3)
+ -webkit-animation: loadbars 0.6s ease-in-out infinite -0.4s
+
+@-webkit-keyframes 'loadbars'
+ 0%
+ height: 10px
+ margin-top: 25px
+ 50%
+ height: 50px
+ margin-top: 0px
+ 100%
+ height: 10px
+ margin-top: 25px
+
diff --git a/app/assets/stylesheets/darkswarm/map.css.sass b/app/assets/stylesheets/darkswarm/map.css.sass
index ba824b3cd0..a8028c24d1 100644
--- a/app/assets/stylesheets/darkswarm/map.css.sass
+++ b/app/assets/stylesheets/darkswarm/map.css.sass
@@ -1,7 +1,12 @@
// Place all the styles related to the map controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
-.ofn-map-container
+.map-container
+ width: 100%
map, .angular-google-map-container, google-map, .angular-google-map
display: block
height: 100%
+
+ img // https://github.com/zurb/foundation/issues/112
+ max-width: none
+ height: auto
diff --git a/app/assets/stylesheets/darkswarm/shop.css.sass b/app/assets/stylesheets/darkswarm/shop.css.sass
index a23458b941..b37c985884 100644
--- a/app/assets/stylesheets/darkswarm/shop.css.sass
+++ b/app/assets/stylesheets/darkswarm/shop.css.sass
@@ -31,35 +31,31 @@
location, location + small
display: block
- #distributor_title
- float: left
- display: block
- min-width: 350px
+ #distributor_title h3
+ margin-top: 0
+ @media all and (max-width: 768px)
+ margin-bottom: 8px
+
ordercycle
- @media all and (max-width: 768px)
+ @media all and (max-width: 640px)
float: left
clear: left
- padding-bottom: 12px
- display: block
+ padding: 12px 10px
+ width: 100%
+ margin-top: 10px
+ background: #e5e5e5
float: right
form.custom
- //width: 400px
text-align: right
- margin-right: 1em
- @media all and (max-width: 768px)
- padding-left: 1em
- padding-top: 1em
& > strong
line-height: 2.5
font-size: 1.29em
padding-right: 14px
@media all and (max-width: 768px)
- font-size: 1.2em
- select
+ select
width: inherit
display: inline-block
- vackground: transparent
border-width: 1px
border-color: #999
color: #666
@@ -67,13 +63,18 @@
margin-bottom: 0
padding: 8px 20px 8px 12px
@media all and (max-width: 768px)
- font-size: 1.2em
- // width: 180px
+ font-size: 0.875em
closing
+ @media all and (max-width: 768px)
+ font-size: 1.2em
+ padding-bottom: 10px
color: black
font-size: 1.5em
display: block
- padding-bottom: 20px
+ padding-bottom: 12px
+ span
+ @media all and (max-width: 768px)
+ font-size: 0.875em
products
display: block
@@ -101,10 +102,12 @@
input
margin: 0
width: 8em
+ display: inline
.columns
padding-top: 1em
padding-bottom: 1em
+ line-height: 2.4em
.row.summary, .row.variants
@include csstrans
@@ -116,8 +119,15 @@
.row.summary
@include csstrans
background: #fff
+ line-height: 1
.summary-header
+ @include csstrans
+ font-size: 1.15rem
+
+ object.taxon
+ height: 18px
+
&, & *
@include avenir
color: $clr-brick
diff --git a/app/assets/stylesheets/darkswarm/style.css b/app/assets/stylesheets/darkswarm/style.css
index 94980106af..66e1269c3a 100755
--- a/app/assets/stylesheets/darkswarm/style.css
+++ b/app/assets/stylesheets/darkswarm/style.css
@@ -1,10 +1,10 @@
@font-face {
font-family: 'OFN';
- src:url('/OFN.eot?-zgemuq');
- src:url('/OFN.eot?#iefix-zgemuq') format('embedded-opentype'),
- url('/OFN.woff?-zgemuq') format('woff'),
- url('/OFN.ttf?-zgemuq') format('truetype'),
- url('/OFN.svg?-zgemuq#OFN') format('svg');
+ src:url('/OFN.eot?-g90m7b');
+ src:url('/OFN.eot?#iefix-g90m7b') format('embedded-opentype'),
+ url('/OFN.woff?-g90m7b') format('woff'),
+ url('/OFN.ttf?-g90m7b') format('truetype'),
+ url('/OFN.svg?-g90m7b#OFN') format('svg');
font-weight: normal;
font-style: normal;
}
@@ -23,168 +23,174 @@
-moz-osx-font-smoothing: grayscale;
}
-.ofn-i_055-point-left:before {
+.ofn-i_057-expand:before {
content: "\e600";
}
-.ofn-i_054-point-right:before {
+.ofn-i_056-bulk:before {
content: "\e601";
}
-.ofn-i_053-point-up:before {
+.ofn-i_055-point-left:before {
content: "\e602";
}
-.ofn-i_052-point-down:before {
+.ofn-i_054-point-right:before {
content: "\e603";
}
-.ofn-i_051-check-big:before {
+.ofn-i_053-point-up:before {
content: "\e604";
}
-.ofn-i_050-mail-circle:before {
+.ofn-i_052-point-down:before {
content: "\e605";
}
-.ofn-i_049-web:before {
+.ofn-i_051-check-big:before {
content: "\e606";
}
-.ofn-i_048-play-video:before {
+.ofn-i_050-mail-circle:before {
content: "\e607";
}
-.ofn-i_047-youtube:before {
+.ofn-i_049-web:before {
content: "\e608";
}
-.ofn-i_046-g:before {
+.ofn-i_048-play-video:before {
content: "\e609";
}
-.ofn-i_045-pintrest:before {
+.ofn-i_047-youtube:before {
content: "\e60a";
}
-.ofn-i_044-facebook:before {
+.ofn-i_046-g:before {
content: "\e60b";
}
-.ofn-i_043-instagram:before {
+.ofn-i_045-pintrest:before {
content: "\e60c";
}
-.ofn-i_042-linkedin:before {
+.ofn-i_044-facebook:before {
content: "\e60d";
}
-.ofn-i_041-twitter:before {
+.ofn-i_043-instagram:before {
content: "\e60e";
}
-.ofn-i_040-hub:before {
+.ofn-i_042-linkedin:before {
content: "\e60f";
}
-.ofn-i_039-delivery:before {
+.ofn-i_041-twitter:before {
content: "\e610";
}
-.ofn-i_038-takeaway:before {
+.ofn-i_040-hub:before {
content: "\e611";
}
-.ofn-i_037-map:before {
+.ofn-i_039-delivery:before {
content: "\e612";
}
-.ofn-i_036-producers:before {
+.ofn-i_038-takeaway:before {
content: "\e613";
}
-.ofn-i_035-groups:before {
+.ofn-i_037-map:before {
content: "\e614";
}
-.ofn-i_034-timer:before {
+.ofn-i_036-producers:before {
content: "\e615";
}
-.ofn-i_033-open-sign:before {
+.ofn-i_035-groups:before {
content: "\e616";
}
-.ofn-i_032-closed-sign:before {
+.ofn-i_034-timer:before {
content: "\e617";
}
-.ofn-i_031-alarm-clock:before {
+.ofn-i_033-open-sign:before {
content: "\e618";
}
-.ofn-i_030-money:before {
+.ofn-i_032-closed-sign:before {
content: "\e619";
}
-.ofn-i_029-shopping-basket:before {
+.ofn-i_031-alarm-clock:before {
content: "\e61a";
}
-.ofn-i_028-barcode:before {
+.ofn-i_030-money:before {
content: "\e61b";
}
-.ofn-i_027-shopping-cart:before {
+.ofn-i_029-shopping-basket:before {
content: "\e61c";
}
-.ofn-i_026-trash:before {
+.ofn-i_028-barcode:before {
content: "\e61d";
}
-.ofn-i_025-notepad:before {
+.ofn-i_027-shopping-cart:before {
content: "\e61e";
}
-.ofn-i_024-mail:before {
+.ofn-i_026-trash:before {
content: "\e61f";
}
-.ofn-i_023-refresh:before {
+.ofn-i_025-notepad:before {
content: "\e620";
}
-.ofn-i_022-cog:before {
+.ofn-i_024-mail:before {
content: "\e621";
}
-.ofn-i_021-tools:before {
+.ofn-i_023-refresh:before {
content: "\e622";
}
-.ofn-i_020-search:before {
+.ofn-i_022-cog:before {
content: "\e623";
}
-.ofn-i_019-map-pin:before {
+.ofn-i_021-tools:before {
content: "\e624";
}
-.ofn-i_018-unlocked:before {
+.ofn-i_020-search:before {
content: "\e625";
}
-.ofn-i_017-locked:before {
+.ofn-i_019-map-pin:before {
content: "\e626";
}
-.ofn-i_016-group:before {
+.ofn-i_018-unlocked:before {
content: "\e627";
}
-.ofn-i_015-user:before {
+.ofn-i_017-locked:before {
content: "\e628";
}
-.ofn-i_014-menu:before {
+.ofn-i_016-group:before {
content: "\e629";
}
-.ofn-i_013-help:before {
+.ofn-i_015-user:before {
content: "\e62a";
}
-.ofn-i_012-warning:before {
+.ofn-i_014-menu:before {
content: "\e62b";
}
-.ofn-i_011-spinner:before {
+.ofn-i_013-help:before {
content: "\e62c";
}
-.ofn-i_010-bullet:before {
+.ofn-i_012-warning:before {
content: "\e62d";
}
-.ofn-i_009-close:before {
+.ofn-i_011-spinner:before {
content: "\e62e";
}
-.ofn-i_008-caret-left:before {
+.ofn-i_010-bullet:before {
content: "\e62f";
}
-.ofn-i_007-caret-right:before {
+.ofn-i_009-close:before {
content: "\e630";
}
-.ofn-i_006-caret-up:before {
+.ofn-i_008-caret-left:before {
content: "\e631";
}
-.ofn-i_005-caret-down:before {
+.ofn-i_007-caret-right:before {
content: "\e632";
}
-.ofn-i_004-x:before {
+.ofn-i_006-caret-up:before {
content: "\e633";
}
-.ofn-i_003-check:before {
+.ofn-i_005-caret-down:before {
content: "\e634";
}
-.ofn-i_002-arrow-right:before {
+.ofn-i_004-x:before {
content: "\e635";
}
-.ofn-i_001-arrow-left:before {
+.ofn-i_003-check:before {
content: "\e636";
}
+.ofn-i_002-arrow-right:before {
+ content: "\e637";
+}
+.ofn-i_001-arrow-left:before {
+ content: "\e638";
+}
diff --git a/app/assets/stylesheets/darkswarm/tabs.css.sass b/app/assets/stylesheets/darkswarm/tabs.css.sass
index 4bfd2935f4..ccf1743b9f 100644
--- a/app/assets/stylesheets/darkswarm/tabs.css.sass
+++ b/app/assets/stylesheets/darkswarm/tabs.css.sass
@@ -33,7 +33,7 @@
border-top: 4px solid transparent
a:after
padding-left: 8px
- content: "\e632"
+ content: "\e634"
visibility: hidden
@include icon-font
dd:hover
@@ -48,7 +48,7 @@
a
color: $clr-brick-bright
a:after
- content: "\e631"
+ content: "\e633"
visibility: visible
@include icon-font
diff --git a/app/assets/stylesheets/darkswarm/typography.css.sass b/app/assets/stylesheets/darkswarm/typography.css.sass
index cedd6a1130..fac62dfcb6 100644
--- a/app/assets/stylesheets/darkswarm/typography.css.sass
+++ b/app/assets/stylesheets/darkswarm/typography.css.sass
@@ -36,14 +36,14 @@ h1, h2, h3, h4, h5, h6, .avenir
@include avenir
padding: 0px
-ul.ofn-list
+ul.bullet-list
margin: 0
li
list-style: none
line-height: 1.5
height: inherit
li:before
- content: "\e62d"
+ content: "\e62f"
font-family: "OFN"
display: inline-block
font-weight: normal
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 07e1029991..decc48ac7e 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,6 +1,5 @@
class ApplicationController < ActionController::Base
protect_from_forgery
- before_filter :require_certified_hostname
include EnterprisesHelper
@@ -17,6 +16,7 @@ class ApplicationController < ActionController::Base
end
end
+
private
def require_distributor_chosen
@@ -42,17 +42,6 @@ class ApplicationController < ActionController::Base
end
end
- # There are several domains that point to the production server, but only one
- # (vic.openfoodnetwork.org) that has the SSL certificate. Redirect all requests to this
- # domain to avoid showing customers a scary invalid certificate error.
- def require_certified_hostname
- certified_host = "openfoodnetwork.org.au"
-
- if Rails.env.production? && request.host != certified_host
- redirect_to "http://#{certified_host}#{request.fullpath}"
- end
- end
-
# All render calls within the block will be performed with the specified format
# Useful for rendering html within a JSON response, particularly if the specified
diff --git a/app/controllers/spree/admin/variants_controller_decorator.rb b/app/controllers/spree/admin/variants_controller_decorator.rb
index 16a37d5286..7c66f1f08a 100644
--- a/app/controllers/spree/admin/variants_controller_decorator.rb
+++ b/app/controllers/spree/admin/variants_controller_decorator.rb
@@ -1,6 +1,17 @@
Spree::Admin::VariantsController.class_eval do
helper 'spree/products'
+ def destroy
+ @variant = Spree::Variant.find(params[:id])
+ @variant.delete # This line changed, as well as removal of following conditional
+ flash[:success] = I18n.t('notice_messages.variant_deleted')
+
+ respond_with(@variant) do |format|
+ format.html { redirect_to admin_product_variants_url(params[:product_id]) }
+ format.js { render_js_for_destroy }
+ end
+ end
+
protected
diff --git a/app/controllers/spree/api/products_controller_decorator.rb b/app/controllers/spree/api/products_controller_decorator.rb
index 0dbecaa9fd..77c1aa6632 100644
--- a/app/controllers/spree/api/products_controller_decorator.rb
+++ b/app/controllers/spree/api/products_controller_decorator.rb
@@ -8,6 +8,15 @@ Spree::Api::ProductsController.class_eval do
end
+ def soft_delete
+ authorize! :delete, Spree::Product
+ @product = find_product(params[:product_id])
+ authorize! :delete, @product
+ @product.delete
+ respond_with(@product, :status => 204)
+ end
+
+
private
# Copied and modified from Spree::Api::BaseController to allow
diff --git a/app/controllers/spree/api/variants_controller_decorator.rb b/app/controllers/spree/api/variants_controller_decorator.rb
index a225c4c401..3bc8b1c511 100644
--- a/app/controllers/spree/api/variants_controller_decorator.rb
+++ b/app/controllers/spree/api/variants_controller_decorator.rb
@@ -3,11 +3,7 @@ Spree::Api::VariantsController.class_eval do
@variant = scope.find(params[:variant_id])
authorize! :delete, @variant
- @variant.deleted_at = Time.now()
- if @variant.save
- respond_with(@variant, :status => 204)
- else
- invalid_resource!(@variant)
- end
+ @variant.delete
+ respond_with @variant, status: 204
end
end
diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb
index f257e55c1b..37aeb5e3f1 100644
--- a/app/models/spree/product_decorator.rb
+++ b/app/models/spree/product_decorator.rb
@@ -147,6 +147,15 @@ Spree::Product.class_eval do
end
end
+ def delete_with_delete_from_order_cycles
+ transaction do
+ delete_without_delete_from_order_cycles
+
+ ExchangeVariant.where('exchange_variants.variant_id IN (?)', self.variants_including_master_and_deleted).destroy_all
+ end
+ end
+ alias_method_chain :delete, :delete_from_order_cycles
+
private
diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb
index 25d09ae097..9410c14902 100644
--- a/app/models/spree/variant_decorator.rb
+++ b/app/models/spree/variant_decorator.rb
@@ -40,6 +40,20 @@ Spree::Variant.class_eval do
values.to_sentence({ :words_connector => ", ", :two_words_connector => ", " })
end
+ def delete_unit_option_values
+ ovs = self.option_values.where(option_type_id: Spree::Product.all_variant_unit_option_types)
+ self.option_values.destroy ovs
+ end
+
+ def name_to_display
+ display_name || product.name
+ end
+
+ def unit_to_display
+ display_as || options_text
+ end
+
+
def update_units
delete_unit_option_values
@@ -51,17 +65,20 @@ Spree::Variant.class_eval do
end
end
+ def delete
+ transaction do
+ self.update_column(:deleted_at, Time.now)
+ ExchangeVariant.where(variant_id: self).destroy_all
+ end
+ end
+
+
private
def update_weight_from_unit_value
self.weight = unit_value / 1000 if self.product.variant_unit == 'weight' && unit_value.present?
end
- def delete_unit_option_values
- ovs = self.option_values.where(option_type_id: Spree::Product.all_variant_unit_option_types)
- self.option_values.destroy ovs
- end
-
def option_value_name
if display_as.present?
display_as
diff --git a/app/views/admin/enterprises/_supplied_product.rabl b/app/views/admin/enterprises/_supplied_product.rabl
new file mode 100644
index 0000000000..268ff302d5
--- /dev/null
+++ b/app/views/admin/enterprises/_supplied_product.rabl
@@ -0,0 +1,10 @@
+object @product
+
+attributes :name
+node(:supplier_name) { |p| p.supplier.andand.name }
+node(:image_url) { |p| p.images.present? ? p.images.first.attachment.url(:mini) : nil }
+node(:master_id) { |p| p.master.id }
+child variants: :variants do |variant|
+ attributes :id
+ node(:label) { |v| v.options_text }
+end
diff --git a/app/views/admin/enterprises/index.rabl b/app/views/admin/enterprises/index.rabl
index 9e1ee893d1..1342d6eb1a 100644
--- a/app/views/admin/enterprises/index.rabl
+++ b/app/views/admin/enterprises/index.rabl
@@ -2,13 +2,8 @@ collection @collection
attributes :id, :name
-child supplied_products: :supplied_products do |product|
- attributes :name
- node(:supplier_name) { |p| p.supplier.andand.name }
- node(:image_url) { |p| p.images.present? ? p.images.first.attachment.url(:mini) : nil }
- node(:master_id) { |p| p.master.id }
- child variants: :variants do |variant|
- attributes :id
- node(:label) { |v| v.options_text }
+node(:supplied_products) do |enterprise|
+ enterprise.supplied_products.not_deleted.map do |product|
+ partial 'admin/enterprises/supplied_product', object: product
end
end
diff --git a/app/views/home/_fat.html.haml b/app/views/home/_fat.html.haml
index 30a9dd719a..25c48006e8 100644
--- a/app/views/home/_fat.html.haml
+++ b/app/views/home/_fat.html.haml
@@ -5,8 +5,7 @@
Shop for
%p.trans-sentence
%span.fat-taxons{"ng-repeat" => "taxon in hub.taxons"}
- %img{"bo-src" => "taxon.icon",
- name: "{{taxon.name}}", alt: "{{taxon.name}}"}
+ %render-svg{path: "{{taxon.icon}}"}
{{taxon.name}}
.columns.small-4
%h5 Delivery options
@@ -21,7 +20,7 @@
%h5
%i.ofn-i_036-producers
Our producers
- %ul.ofn-list
+ %ul.bullet-list
%li{"ng-repeat" => "producer in hub.producers"}
= render partial: "modals/producer"
diff --git a/app/views/home/_hubs.html.haml b/app/views/home/_hubs.html.haml
index 7d28d28508..c96ec6698c 100644
--- a/app/views/home/_hubs.html.haml
+++ b/app/views/home/_hubs.html.haml
@@ -25,6 +25,7 @@
.active_table
%hub.active_table_node.row{"ng-repeat" => "hub in filteredHubs = (hubs | hubs:query)",
"ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !hub.active, 'current' : current()}",
+ "scroll-after-load" => true,
"ng-controller" => "HubNodeCtrl",
id: "{{hub.hash}}"}
.small-12.columns
diff --git a/app/views/home/_map.html.haml b/app/views/home/_map.html.haml
index 8d83e58c40..5a89dd181e 100644
--- a/app/views/home/_map.html.haml
+++ b/app/views/home/_map.html.haml
@@ -5,6 +5,6 @@
%h2 Map
%h5 of all our food hubs and producers
%p
- %button.neutral-btn.light
+ %a.neutral-btn.light{href: "/map"}
%i.ofn-i_037-map
View map
\ No newline at end of file
diff --git a/app/views/json/_producer.rabl b/app/views/json/_producer.rabl
index 1f4a99cdfc..467011bb7a 100644
--- a/app/views/json/_producer.rabl
+++ b/app/views/json/_producer.rabl
@@ -1,5 +1,16 @@
attributes :id, :name, :description, :long_description, :website, :instagram, :facebook, :linkedin, :twitter
node :promo_image do |producer|
- producer.promo_image.url
+ producer.promo_image(:large)
+end
+node :logo do |producer|
+ producer.logo(:medium)
+end
+
+node :path do |producer|
+ main_app.producer_path(producer)
+end
+
+node :hash do |producer|
+ producer.to_param
end
diff --git a/app/views/json/_taxon.rabl b/app/views/json/_taxon.rabl
index 916abeff78..265ace60d7 100644
--- a/app/views/json/_taxon.rabl
+++ b/app/views/json/_taxon.rabl
@@ -1,5 +1,5 @@
attributes :name, :id, :permalink
node :icon do |taxon|
- taxon.icon.url
+ taxon.icon(:original)
end
diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml
index 1b1d97f85d..da314d514c 100644
--- a/app/views/layouts/darkswarm.html.haml
+++ b/app/views/layouts/darkswarm.html.haml
@@ -27,9 +27,8 @@
.inner-wrap
= render partial: "shared/menu/menu"
- %ofn-flash
-
%section{ role: "main" }
= yield
#footer
+ %loading
diff --git a/app/views/map/index.html.haml b/app/views/map/index.html.haml
index 9fec2290d8..277178bfdc 100644
--- a/app/views/map/index.html.haml
+++ b/app/views/map/index.html.haml
@@ -1,9 +1,7 @@
= inject_json "enterprisesForMap" , "enterprises_for_map", collection: @enterprises
-.ofn-map-container{"fill-vertical" => true}
+.map-container{"fill-vertical" => true}
%map{"ng-controller" => "MapCtrl"}
- %google-map{center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"}
+ %google-map{options: "map.additional_options", center: "map.center", zoom: "map.zoom", styles: "map.styles", draggable: "true"}
%markers{models: "OfnMap.enterprises", fit: "true",
coords: "'self'", icon: "'icon'", click: "'reveal'"}
-
-
diff --git a/app/views/modals/_producer.html.haml b/app/views/modals/_producer.html.haml
index e9808b09d7..51dc79ed72 100644
--- a/app/views/modals/_producer.html.haml
+++ b/app/views/modals/_producer.html.haml
@@ -9,41 +9,35 @@
.row
.small-12.large-6.columns
%p{"ng-bind-html" => "producer.long_description"}
- .small-12.large-6.columns
+ .small-8.large-4.columns
+ %a{"ng-href" => '/producers##{{producer.hash}}'}
+ Find my products
%img.producer-logo{"ng-src" => "{{producer.logo}}", "ng-if" => "producer.logo"}
- %h5 Stay in touch with {{ producer.name }}
-
+ .small-4.large-2.columns
%ul.small-block-grid-1{bindonce: true}
-
%li{"ng-if" => "producer.website"}
%a{"ng-href" => "http://{{producer.website | stripUrl}}", target: "_blank" }
%i.ofn-i_049-web
- {{ producer.website | stripUrl }}
%li{"ng-if" => "producer.email"}
%a{"ng-href" => "mailto:{{producer.email | stripUrl}}", target: "_blank" }
%i.ofn-i_050-mail-circle
- {{ producer.email | stripUrl }}
%li{"ng-if" => "producer.twitter"}
%a{"ng-href" => "http://twitter.com/{{producer.twitter}}", target: "_blank"}
%i.ofn-i_041-twitter
- {{ producer.twitter }}
%li{"ng-if" => "producer.facebook"}
%a{"ng-href" => "http://{{producer.facebook | stripUrl}}", target: "_blank"}
%i.ofn-i_044-facebook
- {{ producer.facebook | stripUrl }}
%li{"ng-if" => "producer.linkedin"}
%a{"ng-href" => "http://{{producer.linkedin | stripUrl}}", target: "_blank"}
%i.ofn-i_042-linkedin
- {{ producer.linkedin | stripUrl }}
%li{"ng-if" => "producer.instagram"}
%a{"ng-href" => "http://instagram.com/{{producer.instagram}}", target: "_blank"}
%i.ofn-i_043-instagram
- {{ producer.instagram }}
%a.close-reveal-modal{"ng-click" => "$close()"}
%i.ofn-i_009-close
diff --git a/app/views/producers/index.haml b/app/views/producers/index.haml
index a5b1601ce3..7c4b6a5b62 100644
--- a/app/views/producers/index.haml
+++ b/app/views/producers/index.haml
@@ -26,6 +26,7 @@
.small-12.columns
.active_table
%producer.active_table_node.row{id: "{{producer.path}}",
+ "scroll-after-load" => true,
"ng-repeat" => "producer in filteredProducers = (Producers.producers | filterProducers:query)",
"ng-controller" => "ProducerNodeCtrl",
"ng-class" => "{'closed' : !open(), 'open' : open(), 'inactive' : !producer.active}",
diff --git a/app/views/shared/_footer.html.haml b/app/views/shared/_footer.html.haml
index bdd51259c4..afe27bc02f 100644
--- a/app/views/shared/_footer.html.haml
+++ b/app/views/shared/_footer.html.haml
@@ -19,7 +19,7 @@
LinkedIn
.small-12.medium-4.columns.text-left
%h4 Getting around
- %ul.ofn-list
+ %ul.bullet-list
%li
%a{href: "/shop"} Shop
%li
diff --git a/app/views/shared/menu/_large_menu.html.haml b/app/views/shared/menu/_large_menu.html.haml
index c85516d8c3..49e82d2632 100644
--- a/app/views/shared/menu/_large_menu.html.haml
+++ b/app/views/shared/menu/_large_menu.html.haml
@@ -38,4 +38,4 @@
%span.nav-primary.nav-branded {{ CurrentHub.name }}
%li.divider
%li.cart
- = render partial: "shared/menu/cart"
\ No newline at end of file
+ = render partial: "shared/menu/cart"
diff --git a/app/views/shared/menu/_menu.html.haml b/app/views/shared/menu/_menu.html.haml
index d673a962db..616b128d78 100644
--- a/app/views/shared/menu/_menu.html.haml
+++ b/app/views/shared/menu/_menu.html.haml
@@ -1,3 +1,4 @@
.fixed
= render partial: "shared/menu/large_menu"
+ %ofn-flash
= render partial: "shared/menu/mobile_menu"
diff --git a/app/views/shop/products.rabl b/app/views/shop/products.rabl
index b1567a311a..eb5d62644a 100644
--- a/app/views/shop/products.rabl
+++ b/app/views/shop/products.rabl
@@ -25,7 +25,7 @@ child :primary_taxon => :primary_taxon do
end
child :master => :master do
- attributes :id, :is_master, :count_on_hand, :options_text, :count_on_hand, :on_demand
+ attributes :id, :is_master, :count_on_hand, :name_to_display, :unit_to_display, :count_on_hand, :on_demand
child :images => :images do
attributes :id, :alt
node do |img|
@@ -40,7 +40,8 @@ node :variants do |product|
{id: v.id,
is_master: v.is_master,
count_on_hand: v.count_on_hand,
- options_text: v.options_text,
+ name_to_display: v.name_to_display,
+ unit_to_display: v.unit_to_display,
on_demand: v.on_demand,
price: v.price_with_fees(current_distributor, current_order_cycle),
images: v.images.map { |i| {id: i.id, alt: i.alt, small_url: i.attachment.url(:small, false)} }
diff --git a/app/views/shop/products/_master.html.haml b/app/views/shop/products/_master.html.haml
index e8972082dd..59c0911cfd 100644
--- a/app/views/shop/products/_master.html.haml
+++ b/app/views/shop/products/_master.html.haml
@@ -3,37 +3,40 @@
.small-4.columns
- {{ product.master.options_text }}
+ {{ product.master.name_to_display }}
-# WITHOUT GROUP BUY
.small-5.columns{"bo-if" => "!product.group_buy"}
%input{type: :number,
min: 0,
+ placeholder: "0",
"ofn-disable-scroll" => true,
max: "{{product.on_demand && 9999 || product.count_on_hand }}",
name: "variants[{{product.master.id}}]",
id: "variants_{{product.master.id}}",
"ng-model" => "product.quantity"}
+ {{ product.master.unit_to_display }}
-# WITH GROUP BUY
.small-2.columns{"bo-if" => "product.group_buy"}
%input{type: :number,
min: 0,
+ placeholder: "min",
"ofn-disable-scroll" => true,
max: "{{product.on_demand && 9999 || product.count_on_hand }}",
name: "variants[{{product.master.id}}]",
id: "variants_{{product.master.id}}",
"ng-model" => "product.quantity"}
- (min)
.small-3.columns{"bo-if" => "product.group_buy"}
%input{type: :number,
min: 0,
+ placeholder: "max",
"ofn-disable-scroll" => true,
max: "{{product.on_demand && 9999 || product.count_on_hand }}",
name: "variant_attributes[{{product.master.id}}][max_quantity]",
"ng-model" => "product.max_quantity"}
- (max)
+ {{ product.master.unit_to_display }}
.small-2.columns.text-right
{{ product.price | currency }}
diff --git a/app/views/shop/products/_summary.html.haml b/app/views/shop/products/_summary.html.haml
index ca9557d42a..ec018065c8 100644
--- a/app/views/shop/products/_summary.html.haml
+++ b/app/views/shop/products/_summary.html.haml
@@ -3,9 +3,7 @@
%img{"bo-src" => "product.master.images[0].small_url"}
.small-4.columns.summary-header
- %img{"bo-src" => "product.primary_taxon.icon",
- "ng-click" => "ordering.order = 'primary_taxon.name'",
- name: "{{product.primary_taxon.name}}"}
+ %render-svg{path: "{{product.primary_taxon.icon}}"}
= render partial: "modals/product"
.small-5.columns
diff --git a/app/views/shop/products/_variants.html.haml b/app/views/shop/products/_variants.html.haml
index 6751f8c62c..1863bf7c1c 100644
--- a/app/views/shop/products/_variants.html.haml
+++ b/app/views/shop/products/_variants.html.haml
@@ -6,37 +6,40 @@
.small-4.columns
- {{ variant.options_text }}
+ {{ variant.name_to_display }}
-# WITHOUT GROUP BUY
.small-5.columns{"bo-if" => "!product.group_buy"}
%input{type: :number,
value: nil,
min: 0,
+ placeholder: "0",
"ofn-disable-scroll" => true,
max: "{{variant.on_demand && 9999 || variant.count_on_hand }}",
name: "variants[{{variant.id}}]", id: "variants_{{variant.id}}",
"bo-model" => "variant.quantity"}
+ {{ variant.unit_to_display }}
-# WITH GROUP BUY
.small-2.columns{"bo-if" => "product.group_buy"}
%input{type: :number,
value: nil,
min: 0,
+ placeholder: "min",
"ofn-disable-scroll" => true,
max: "{{variant.on_demand && 9999 || variant.count_on_hand }}",
name: "variants[{{variant.id}}]", id: "variants_{{variant.id}}",
"bo-model" => "variant.quantity"}
- (min)
.small-3.columns{"bo-if" => "product.group_buy"}
%input{type: :number,
min: 0,
+ placeholder: "max",
"ofn-disable-scroll" => true,
max: "{{variant.on_demand && 9999 || variant.count_on_hand }}",
name: "variant_attributes[{{variant.id}}][max_quantity]",
"ng-model" => "variant.max_quantity"}
- (max)
+ {{ variant.unit_to_display }}
.small-2.columns.text-right.price
{{ variant.price | currency }}
diff --git a/app/views/shopping_shared/_details.html.haml b/app/views/shopping_shared/_details.html.haml
index 1b30627533..247f1142b9 100644
--- a/app/views/shopping_shared/_details.html.haml
+++ b/app/views/shopping_shared/_details.html.haml
@@ -1,13 +1,14 @@
%navigation
%distributor.details.row
- #distributor_title
- - if current_distributor.logo.exists?
- %img.left{src: current_distributor.logo.url(:thumb)}
- %h3
- = current_distributor.name
- %location= current_distributor.address.city
- / Will this needs to be a drop-down to choose either pick-up point or delivery once shipping methods are implemented
-
- = render partial: "shopping_shared/order_cycles"
+ .small-12.medium-6.large-6.columns
+ #distributor_title
+ - if current_distributor.logo.exists?
+ %img.left{src: current_distributor.logo.url(:thumb)}
+ %h3
+ = current_distributor.name
+ %location= current_distributor.address.city
+ / Will this needs to be a drop-down to choose either pick-up point or delivery once shipping methods are implemented
+ .small-12.medium-6.large-6.columns
+ = render partial: "shopping_shared/order_cycles"
= render partial: "shopping_shared/tabs"
diff --git a/app/views/shopping_shared/_groups.html.haml b/app/views/shopping_shared/_groups.html.haml
index 194434dcb6..5fd1a1f9b3 100644
--- a/app/views/shopping_shared/_groups.html.haml
+++ b/app/views/shopping_shared/_groups.html.haml
@@ -4,7 +4,7 @@
%h5
=current_distributor.name
belongs to:
- %ul.ofn-list
+ %ul.bullet-list
- for group in current_distributor.groups
%li
%a{href: main_app.groups_path(anchor: "#/#group#{group.id}")}= group.name
diff --git a/app/views/shopping_shared/_producers.html.haml b/app/views/shopping_shared/_producers.html.haml
index 2173b3b53f..5244032214 100644
--- a/app/views/shopping_shared/_producers.html.haml
+++ b/app/views/shopping_shared/_producers.html.haml
@@ -3,6 +3,6 @@
.small-12.columns
%h5
= "#{current_distributor.name}'s producers:"
- %ul.ofn-list
+ %ul.bullet-list
%li{"ng-repeat" => "producer in CurrentHub.producers"}
= render partial: "modals/producer"
diff --git a/config/routes.rb b/config/routes.rb
index be74e71469..0622acb050 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -3,7 +3,6 @@ Openfoodnetwork::Application.routes.draw do
get "/#/login", to: "home#index", as: :spree_login
-
if Rails.env.production?
get "/map", to: "home#index", as: :map
else
@@ -120,6 +119,7 @@ Spree::Core::Engine.routes.prepend do
resources :products do
get :managed, on: :collection
+ delete :soft_delete
resources :variants do
delete :soft_delete
diff --git a/config/schedule.rb b/config/schedule.rb
new file mode 100644
index 0000000000..36cec1f172
--- /dev/null
+++ b/config/schedule.rb
@@ -0,0 +1,12 @@
+require 'whenever'
+
+# Learn more: http://github.com/javan/whenever
+
+env "MAILTO", "rohan@rohanmitchell.com"
+
+# If we use -e with a file containing specs, rspec interprets it and filters out our examples
+job_type :run_file, "cd :path; :environment_variable=:environment bundle exec script/rails runner :task :output"
+
+every 1.day, at: '12:05am' do
+ run_file "lib/open_food_network/integrity_checker.rb"
+end
diff --git a/db/migrate/20140612020206_remove_deleted_variants_from_order_cycles.rb b/db/migrate/20140612020206_remove_deleted_variants_from_order_cycles.rb
new file mode 100644
index 0000000000..cb03abe09a
--- /dev/null
+++ b/db/migrate/20140612020206_remove_deleted_variants_from_order_cycles.rb
@@ -0,0 +1,10 @@
+class RemoveDeletedVariantsFromOrderCycles < ActiveRecord::Migration
+ def up
+ evs = ExchangeVariant.joins(:variant).where('spree_variants.deleted_at IS NOT NULL')
+ say "Removing #{evs.count} deleted variants from order cycles..."
+ evs.destroy_all
+ end
+
+ def down
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 2b3c2cbf62..a40a1414e1 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20140604051248) do
+ActiveRecord::Schema.define(:version => 20140612020206) do
create_table "adjustment_metadata", :force => true do |t|
t.integer "adjustment_id"
@@ -681,7 +681,7 @@ ActiveRecord::Schema.define(:version => 20140604051248) do
t.float "variant_unit_scale"
t.string "variant_unit_name"
t.text "notes"
- t.integer "primary_taxon_id"
+ t.integer "primary_taxon_id", :null => false
end
add_index "spree_products", ["available_on"], :name => "index_products_on_available_on"
diff --git a/lib/open_food_network/integrity_checker.rb b/lib/open_food_network/integrity_checker.rb
new file mode 100644
index 0000000000..ff34f661dc
--- /dev/null
+++ b/lib/open_food_network/integrity_checker.rb
@@ -0,0 +1,23 @@
+require 'rspec/rails'
+require 'rspec/autorun'
+
+# This spec file is one part of a two-part strategy to maintain data integrity. The first part
+# is to proactively protect data integrity using database constraints (not null, foreign keys,
+# etc) and ActiveRecord validations. As a backup to those two techniques, and particularly in
+# the cases where it's not possible to model an integrity concern with database constraints,
+# we can add a reactive integrity test here.
+
+# These tests are run nightly and the results are emailed to the MAILTO address in
+# config/schedule.rb if any failures occur.
+
+# Ref: http://pluralsight.com/training/Courses/TableOfContents/database-your-friend
+
+
+describe "data integrity" do
+ it "has no deleted variants in order cycles" do
+ # When a variant is soft deleted, it should be removed from all order cycles
+ # via Spree::Product#delete or Spree::Variant#delete.
+ evs = ExchangeVariant.joins(:variant).where('spree_variants.deleted_at IS NOT NULL')
+ evs.count.should == 0
+ end
+end
diff --git a/public/OFN.eot b/public/OFN.eot
index d748b3b44b..29cead43c7 100755
Binary files a/public/OFN.eot and b/public/OFN.eot differ
diff --git a/public/OFN.svg b/public/OFN.svg
index 8190e20cbf..0e2eea3b28 100755
--- a/public/OFN.svg
+++ b/public/OFN.svg
@@ -7,59 +7,61 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/OFN.ttf b/public/OFN.ttf
index a40786bc41..4b9b2e5cdc 100755
Binary files a/public/OFN.ttf and b/public/OFN.ttf differ
diff --git a/public/OFN.woff b/public/OFN.woff
index 1b1d24bd45..30d229ffd0 100755
Binary files a/public/OFN.woff and b/public/OFN.woff differ
diff --git a/spec/controllers/spree/api/products_controller_spec.rb b/spec/controllers/spree/api/products_controller_spec.rb
index c64b95853f..eb56859365 100644
--- a/spec/controllers/spree/api/products_controller_spec.rb
+++ b/spec/controllers/spree/api/products_controller_spec.rb
@@ -7,9 +7,11 @@ module Spree
render_views
let(:supplier) { FactoryGirl.create(:supplier_enterprise) }
+ let(:supplier2) { FactoryGirl.create(:supplier_enterprise) }
let!(:product1) { FactoryGirl.create(:product, supplier: supplier) }
let!(:product2) { FactoryGirl.create(:product, supplier: supplier) }
let!(:product3) { FactoryGirl.create(:product, supplier: supplier) }
+ let(:product_other_supplier) { FactoryGirl.create(:product, supplier: supplier2) }
let(:attributes) { [:id, :name, :supplier, :price, :on_hand, :available_on, :permalink_live] }
let(:unit_attributes) { [:id, :name, :group_buy_unit_size, :variant_unit] }
@@ -39,6 +41,20 @@ module Spree
keys = json_response.first.keys.map{ |key| key.to_sym }
attributes.all?{ |attr| keys.include? attr }.should == true
end
+
+ it "soft deletes my products" do
+ spree_delete :soft_delete, {product_id: product1.to_param, format: :json}
+ response.status.should == 204
+ lambda { product1.reload }.should_not raise_error
+ product1.deleted_at.should_not be_nil
+ end
+
+ it "is denied access to soft deleting another enterprises' product" do
+ spree_delete :soft_delete, {product_id: product_other_supplier.to_param, format: :json}
+ assert_unauthorized!
+ lambda { product_other_supplier.reload }.should_not raise_error
+ product_other_supplier.deleted_at.should be_nil
+ end
end
context "as an administrator" do
@@ -80,17 +96,23 @@ module Spree
end
it "should allow available_on to be nil" do
-
spree_get :index, { :template => 'bulk_index', :format => :json }
json_response.size.should == 3
- product4 = FactoryGirl.create(:product)
- product4.available_on = nil
- product4.save!
+ product5 = FactoryGirl.create(:product)
+ product5.available_on = nil
+ product5.save!
spree_get :index, { :template => 'bulk_index', :format => :json }
json_response.size.should == 4
end
+
+ it "soft deletes a product" do
+ spree_delete :soft_delete, {product_id: product1.to_param, format: :json}
+ response.status.should == 204
+ lambda { product1.reload }.should_not raise_error
+ product1.deleted_at.should_not be_nil
+ end
end
end
end
diff --git a/spec/features/admin/variants_spec.rb b/spec/features/admin/variants_spec.rb
index d05aa75ef8..07a8580022 100644
--- a/spec/features/admin/variants_spec.rb
+++ b/spec/features/admin/variants_spec.rb
@@ -83,4 +83,18 @@ feature %q{
page.should_not have_field "variant_unit_value"
page.should_not have_field "variant_unit_description"
end
+
+ it "soft-deletes variants", js: true do
+ p = create(:simple_product)
+ v = create(:variant, product: p)
+
+ login_to_admin_section
+ visit spree.admin_product_variants_path p
+
+ page.find('a.delete-resource').click
+ page.should_not have_content v.options_text
+
+ v.reload
+ v.deleted_at.should_not be_nil
+ end
end
diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb
index 78937ecb47..304eff7d23 100644
--- a/spec/features/consumer/shopping/shopping_spec.rb
+++ b/spec/features/consumer/shopping/shopping_spec.rb
@@ -36,6 +36,7 @@ feature "As a consumer I want to shop with a distributor", js: true do
exchange.variants << product.master
visit shop_path
+ save_screenshot "/users/willmarshall/Desktop/wtsvg.png"
find("#tab_producers a").click
page.should have_content supplier.name
end
@@ -64,6 +65,8 @@ feature "As a consumer I want to shop with a distributor", js: true do
end
it "shows products after selecting an order cycle" do
+ product.master.update_attribute(:display_name, "kitten")
+ product.master.update_attribute(:display_as, "rabbit")
exchange1.variants << product.master ## add product to exchange
visit shop_path
page.should_not have_content product.name
@@ -74,6 +77,9 @@ feature "As a consumer I want to shop with a distributor", js: true do
page.should have_content "Next order closing in 2 days"
Spree::Order.last.order_cycle.should == oc1
page.should have_content product.name
+ save_screenshot "/Users/willmarshall/Desktop/shop.png"
+ page.should have_content product.master.display_name
+ page.should have_content product.master.display_as
end
end
end
diff --git a/spec/javascripts/unit/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/bulk_product_update_spec.js.coffee
index a1ca2d4b83..44466b5d10 100644
--- a/spec/javascripts/unit/bulk_product_update_spec.js.coffee
+++ b/spec/javascripts/unit/bulk_product_update_spec.js.coffee
@@ -973,7 +973,7 @@ describe "AdminProductEditCtrl", ->
describe "deleting products", ->
- it "deletes products with a http delete request to /api/products/id", ->
+ it "deletes products with a http delete request to /api/products/id/soft_delete", ->
spyOn(window, "confirm").andReturn true
$scope.products = [
{
@@ -986,7 +986,7 @@ describe "AdminProductEditCtrl", ->
}
]
$scope.dirtyProducts = {}
- $httpBackend.expectDELETE("/api/products/13").respond 200, "data"
+ $httpBackend.expectDELETE("/api/products/13/soft_delete").respond 200, "data"
$scope.deleteProduct $scope.products[1]
$httpBackend.flush()
@@ -1005,7 +1005,7 @@ describe "AdminProductEditCtrl", ->
DirtyProducts.addProductProperty 9, "someProperty", "something"
DirtyProducts.addProductProperty 13, "name", "P1"
- $httpBackend.expectDELETE("/api/products/13").respond 200, "data"
+ $httpBackend.expectDELETE("/api/products/13/soft_delete").respond 200, "data"
$scope.deleteProduct $scope.products[1]
$httpBackend.flush()
expect($scope.products).toEqual [
@@ -1036,7 +1036,7 @@ describe "AdminProductEditCtrl", ->
describe "when the variant has been saved", ->
- it "deletes variants with a http delete request to /api/products/product_permalink/variants/(variant_id)", ->
+ it "deletes variants with a http delete request to /api/products/product_permalink/variants/(variant_id)/soft_delete", ->
spyOn(window, "confirm").andReturn true
$scope.products = [
{
diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb
index 3b21d425eb..594dfa5d29 100644
--- a/spec/models/spree/product_spec.rb
+++ b/spec/models/spree/product_spec.rb
@@ -580,7 +580,7 @@ module Spree
end
end
- describe "Taxons" do
+ describe "taxons" do
let(:taxon1) { create(:taxon) }
let(:taxon2) { create(:taxon) }
let(:product) { create(:simple_product) }
@@ -589,5 +589,25 @@ module Spree
product.taxons.should == [product.primary_taxon]
end
end
+
+ describe "deletion" do
+ let(:p) { create(:simple_product) }
+ let(:v) { create(:variant, product: p) }
+ let(:oc) { create(:simple_order_cycle) }
+ let(:s) { create(:supplier_enterprise) }
+ let(:e) { create(:exchange, order_cycle: oc, incoming: true, sender: s, receiver: oc.coordinator) }
+
+ it "removes the master variant from all order cycles" do
+ e.variants << p.master
+ p.delete
+ e.variants(true).should be_empty
+ end
+
+ it "removes all other variants from order cycles" do
+ e.variants << v
+ p.delete
+ e.variants(true).should be_empty
+ end
+ end
end
end
diff --git a/spec/models/spree/variant_spec.rb b/spec/models/spree/variant_spec.rb
index f5a3dd146d..bf13cf978c 100644
--- a/spec/models/spree/variant_spec.rb
+++ b/spec/models/spree/variant_spec.rb
@@ -124,6 +124,31 @@ module Spree
end
describe "unit value/description" do
+ describe "getting name for display" do
+ it "returns display_name if present" do
+ v = create(:variant, display_name: "foo")
+ v.name_to_display.should == "foo"
+ end
+
+ it "returns product name if display_name is empty" do
+ v = create(:variant, product: create(:product))
+ v.name_to_display.should == v.product.name
+ end
+ end
+
+ describe "getting unit for display" do
+ it "returns display_as if present" do
+ v = create(:variant, display_as: "foo")
+ v.unit_to_display.should == "foo"
+ end
+
+ it "returns options_text if display_as is empty" do
+ v = create(:variant)
+ v.stub(:options_text).and_return "ponies"
+ v.unit_to_display.should == "ponies"
+ end
+ end
+
describe "setting the variant's weight from the unit value" do
it "sets the variant's weight when unit is weight" do
p = create(:simple_product, variant_unit: nil, variant_unit_scale: nil)
@@ -209,6 +234,25 @@ module Spree
end
end
end
+ context "when the variant already has a value set (and all required option values exist)" do
+ let!(:p0) { create(:simple_product, variant_unit: 'weight', variant_unit_scale: 1) }
+ let!(:v0) { create(:variant, product: p0, unit_value: 10, unit_description: 'foo') }
+
+ let!(:p) { create(:simple_product, variant_unit: 'weight', variant_unit_scale: 1) }
+ let!(:v) { create(:variant, product: p, unit_value: 5, unit_description: 'bar') }
+
+ it "removes the old option value and assigns the new one" do
+ ov_orig = v.option_values.last
+ ov_new = v0.option_values.last
+
+ expect {
+ v.update_attributes!(unit_value: 10, unit_description: 'foo')
+ }.to change(Spree::OptionValue, :count).by(0)
+
+ v.option_values.should_not include ov_orig
+ v.option_values.should include ov_new
+ end
+ end
context "when the variant already has a value set (and all required option values exist)" do
let!(:p0) { create(:simple_product, variant_unit: 'weight', variant_unit_scale: 1) }
@@ -246,7 +290,7 @@ module Spree
let!(:p) { create(:simple_product, variant_unit: 'weight', variant_unit_scale: 1) }
let!(:v) { create(:variant, product: p, unit_value: 5, unit_description: 'bar', display_as: 'FOOS!') }
- it "requests the name of the new option_value from OptionValueName" do
+ it "does not request the name of the new option_value from OptionValueName" do
OpenFoodNetwork::OptionValueNamer.any_instance.should_not_receive(:name)
v.update_attributes!(unit_value: 10, unit_description: 'foo')
ov = v.option_values.last
@@ -264,13 +308,13 @@ module Spree
it "removes option value associations for unit option types" do
expect {
- @v.send(:delete_unit_option_values)
+ @v.delete_unit_option_values
}.to change(@v.option_values, :count).by(-1)
end
it "does not delete option values" do
expect {
- @v.send(:delete_unit_option_values)
+ @v.delete_unit_option_values
}.to change(Spree::OptionValue, :count).by(0)
end
end
diff --git a/spec/views/admin/enterprises/index.rabl_spec.rb b/spec/views/admin/enterprises/index.rabl_spec.rb
new file mode 100644
index 0000000000..91463c2212
--- /dev/null
+++ b/spec/views/admin/enterprises/index.rabl_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe "admin/enterprises/index.rabl" do
+ let(:enterprise) { create(:distributor_enterprise) }
+ let!(:product) { create(:simple_product, supplier: enterprise) }
+ let!(:deleted_product) { create(:simple_product, supplier: enterprise, deleted_at: 1.day.ago) }
+ let(:render) { Rabl.render([enterprise], 'admin/enterprises/index', view_path: 'app/views', scope: RablHelper::FakeContext.instance) }
+
+ describe "supplied products" do
+ it "does not render deleted products" do
+ render.should have_json_size(1).at_path '0/supplied_products'
+ render.should be_json_eql(product.master.id).at_path '0/supplied_products/0/master_id'
+ end
+ end
+end
diff --git a/vendor/assets/javascripts/angular-scroll.min.js b/vendor/assets/javascripts/angular-scroll.min.js
new file mode 100644
index 0000000000..d979dfe0ee
--- /dev/null
+++ b/vendor/assets/javascripts/angular-scroll.min.js
@@ -0,0 +1 @@
+var duScrollDefaultEasing=function(e){return.5>e?Math.pow(2*e,2)/2:1-Math.pow(2*(1-e),2)/2};angular.module("duScroll",["duScroll.scrollspy","duScroll.requestAnimation","duScroll.smoothScroll","duScroll.scrollContainer","duScroll.scrollHelpers"]).value("duScrollDuration",350).value("duScrollGreedy",!1).value("duScrollEasing",duScrollDefaultEasing),angular.module("duScroll.scrollHelpers",[]).run(["$window","$q","cancelAnimation","requestAnimation","duScrollEasing",function(e,t,n,r,o){var l=angular.element.prototype;this.$get=function(){return l};var i=function(e){return"undefined"!=typeof HTMLDocument&&e instanceof HTMLDocument||e.nodeType&&e.nodeType===e.DOCUMENT_NODE},u=function(e){return"undefined"!=typeof HTMLElement&&e instanceof HTMLElement||e.nodeType&&e.nodeType===e.ELEMENT_NODE},c=function(e){return u(e)||i(e)?e:e[0]};l.scrollTo=function(t,n,r){var o;if(angular.isElement(t)?o=this.scrollToElement:r&&(o=this.scrollToAnimated),o)return o.apply(this,arguments);var l=c(this);return i(l)?e.scrollTo(t,n):(l.scrollLeft=t,void(l.scrollTop=n))};var a,s;l.scrollToAnimated=function(e,l,i,u){i&&!u&&(u=o);var c=this.scrollLeft(),d=this.scrollTop(),f=Math.round(e-c),p=Math.round(l-d),m=null;a&&(n(a),s.reject());var g=this;if(s=t.defer(),!f&&!p)return s.resolve(),s.promise;var v=function(e){null===m&&(m=e);var t=e-m,n=t>=i?1:u(t/i);g.scrollTo(c+Math.ceil(f*n),d+Math.ceil(p*n)),1>n?a=r(v):(a=null,s.resolve())};return g.scrollTo(c,d),a=r(v),s.promise},l.scrollToElement=function(e,t,n,r){var o=c(this),l=this.scrollTop()+c(e).getBoundingClientRect().top-t;return u(o)&&(l-=o.getBoundingClientRect().top),this.scrollTo(0,l,n,r)};var d={scrollLeft:function(t,n,r){if(angular.isNumber(t))return this.scrollTo(t,this.scrollTop(),n,r);var o=c(this);return i(o)?e.scrollX||document.documentElement.scrollLeft||document.body.scrollLeft:o.scrollLeft},scrollTop:function(t,n,r){if(angular.isNumber(t))return this.scrollTo(this.scrollTop(),t,n,r);var o=c(this);return i(o)?e.scrollY||document.documentElement.scrollTop||document.body.scrollTop:o.scrollTop}},f=function(e,t){return function(n,r){return r?t.apply(this,arguments):e.apply(this,arguments)}};for(var p in d)l[p]=l[p]?f(l[p],d[p]):d[p]}]),angular.module("duScroll.polyfill",[]).factory("polyfill",["$window",function(e){var t=["webkit","moz","o","ms"];return function(n,r){if(e[n])return e[n];for(var o,l=n.substr(0,1).toUpperCase()+n.substr(1),i=0;i