diff --git a/Gemfile b/Gemfile index 6965ae1a43..6d9b351cca 100644 --- a/Gemfile +++ b/Gemfile @@ -20,7 +20,7 @@ gem 'angularjs-rails' gem 'bugsnag' gem 'newrelic_rpm' gem 'haml' -gem 'sass' +gem 'sass', "~> 3.2" gem 'aws-sdk' gem 'db2fog' gem 'andand' @@ -49,8 +49,9 @@ group :assets do gem 'uglifier', '>= 1.0.3' gem 'turbo-sprockets-rails3' - gem 'zurb-foundation', :github => 'zurb/foundation' + end +gem "foundation-rails" gem 'foundation_rails_helper', github: 'willrjmarshall/foundation_rails_helper', branch: "rails3" gem 'jquery-rails' diff --git a/Gemfile.lock b/Gemfile.lock index b498268a74..bdd90bf6b8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -103,13 +103,6 @@ GIT activemodel (>= 3.0) railties (>= 3.0) -GIT - remote: git://github.com/zurb/foundation.git - revision: a81d639847ba5fc2e324b749b8e409e31e8d83c9 - specs: - zurb-foundation (4.3.1) - sass (>= 3.2.0) - PATH remote: lib/chili/eaterprises_feature specs: @@ -186,20 +179,18 @@ GEM multi_json (~> 1.0) builder (3.0.4) cancan (1.6.8) - capybara (2.0.2) + capybara (2.2.1) mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) rack-test (>= 0.5.4) - selenium-webdriver (~> 2.0) - xpath (~> 1.0.0) + xpath (~> 2.0) celluloid (0.15.2) timers (~> 1.1.0) - childprocess (0.3.9) - ffi (~> 1.0, >= 1.0.11) - chunky_png (1.2.8) + chunky_png (1.3.0) climate_control (0.0.3) activesupport (>= 3.0) + cliver (0.3.2) cocaine (0.5.1) climate_control (>= 0.0.3, < 1.0) coderay (1.0.9) @@ -216,10 +207,10 @@ GEM active_link_to (~> 1.0.0) paperclip (>= 2.3.0) rails (>= 3.0.0) - compass (0.12.2) + compass (0.12.4) chunky_png (~> 1.2) fssm (>= 0.2.7) - sass (~> 3.1) + sass (~> 3.2.17) compass-rails (1.0.3) compass (>= 0.12.2, < 0.14) crack (0.4.1) @@ -258,10 +249,8 @@ GEM railties (>= 3.0.0) faker (1.0.1) i18n (~> 0.4) - faye-websocket (0.4.7) - eventmachine (>= 0.12.0) ffaker (1.15.0) - ffi (1.9.0) + ffi (1.9.3) fog (1.14.0) builder excon (~> 0.25.0) @@ -273,6 +262,9 @@ GEM nokogiri (~> 1.5) ruby-hmac formatador (0.2.4) + foundation-rails (5.2.1.0) + railties (>= 3.1.0) + sass (>= 3.2.0) fssm (0.2.10) geocoder (1.1.8) gmaps4rails (1.5.6) @@ -335,13 +327,13 @@ GEM money (5.0.0) i18n (~> 0.4) json - multi_json (1.8.4) + multi_json (1.9.2) multi_xml (0.5.5) net-scp (1.1.2) net-ssh (>= 2.6.5) net-ssh (2.6.8) newrelic_rpm (3.6.7.152) - nokogiri (1.6.0) + nokogiri (1.6.1) mini_portile (~> 0.5.0) oj (2.1.2) orm_adapter (0.4.0) @@ -351,10 +343,11 @@ GEM cocaine (>= 0.0.2) mime-types pg (0.13.2) - poltergeist (1.1.2) - capybara (~> 2.0.1) - faye-websocket (~> 0.4.4) - http_parser.rb (~> 0.5.3) + poltergeist (1.5.0) + capybara (~> 2.1) + cliver (~> 0.3.1) + multi_json (~> 1.0) + websocket-driver (>= 0.2.0) polyamorous (0.5.0) activerecord (~> 3.0) polyglot (0.3.4) @@ -373,7 +366,7 @@ GEM rack (>= 0.4) rack-livereload (0.3.15) rack - rack-ssl (1.3.3) + rack-ssl (1.3.4) rack rack-test (0.6.2) rack (>= 1.0) @@ -428,20 +421,14 @@ GEM rspec-expectations (~> 2.14.0) rspec-mocks (~> 2.14.0) ruby-hmac (0.4.0) - rubyzip (0.9.9) safe_yaml (0.9.5) - sass (3.2.12) + sass (3.2.18) sass-rails (3.2.6) railties (~> 3.2.0) sass (>= 3.1.10) tilt (~> 1.3) select2-rails (3.2.1) thor (~> 0.14) - selenium-webdriver (2.35.0) - childprocess (>= 0.2.5) - multi_json (~> 1.0) - rubyzip - websocket (~> 1.0.4) shoulda-matchers (1.1.0) activesupport (>= 3.0.0) simplecov (0.7.1) @@ -461,7 +448,7 @@ GEM therubyracer (0.12.0) libv8 (~> 3.16.14.0) ref - thor (0.18.1) + thor (0.19.0) tilt (1.4.1) timecop (0.6.2.2) timers (1.1.0) @@ -495,8 +482,8 @@ GEM webmock (1.13.0) addressable (>= 2.2.7) crack (>= 0.3.2) - websocket (1.0.7) - xpath (1.0.0) + websocket-driver (0.3.2) + xpath (2.0.0) nokogiri (~> 1.3) zeus (0.13.3) method_source (>= 0.6.7) @@ -522,6 +509,7 @@ DEPENDENCIES eaterprises_feature! factory_girl_rails faker + foundation-rails foundation_rails_helper! geocoder gmaps4rails @@ -547,7 +535,7 @@ DEPENDENCIES rails (= 3.2.17) representative_view rspec-rails - sass + sass (~> 3.2) sass-rails (~> 3.2.3) shoulda-matchers simple_form! @@ -566,4 +554,3 @@ DEPENDENCIES unicorn unicorn-rails webmock - zurb-foundation! diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 7d5ab0a45f..30d3a18f09 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -237,6 +237,8 @@ productEditModule.controller "AdminProductEditCtrl", [ else if confirm("'#{filter.predicate.name}' filter already exists on column '#{filter.property.name}'. Replace it?") $scope.currentFilters[existingfilterIndex] = filter $scope.fetchProducts() + else + alert("Please ensure all filter fields are filled in before adding a filter.") $scope.removeFilter = (filter) -> index = $scope.currentFilters.indexOf(filter) diff --git a/app/assets/javascripts/admin/order_cycle.js.erb.coffee b/app/assets/javascripts/admin/order_cycle.js.erb.coffee index 60395fe474..9589b790b9 100644 --- a/app/assets/javascripts/admin/order_cycle.js.erb.coffee +++ b/app/assets/javascripts/admin/order_cycle.js.erb.coffee @@ -176,10 +176,10 @@ angular.module('order_cycle', ['ngResource']) exchange.showProducts = !exchange.showProducts addSupplier: (new_supplier_id) -> - this.order_cycle.incoming_exchanges.push({enterprise_id: new_supplier_id, active: true, variants: {}, enterprise_fees: []}) + this.order_cycle.incoming_exchanges.push({enterprise_id: new_supplier_id, incoming: true, active: true, variants: {}, enterprise_fees: []}) addDistributor: (new_distributor_id) -> - this.order_cycle.outgoing_exchanges.push({enterprise_id: new_distributor_id, active: true, variants: {}, enterprise_fees: []}) + this.order_cycle.outgoing_exchanges.push({enterprise_id: new_distributor_id, incoming: false, active: true, variants: {}, enterprise_fees: []}) removeExchange: (exchange) -> incoming_index = this.order_cycle.incoming_exchanges.indexOf exchange @@ -239,18 +239,15 @@ angular.module('order_cycle', ['ngResource']) service.order_cycle.incoming_exchanges = [] service.order_cycle.outgoing_exchanges = [] for exchange in service.order_cycle.exchanges - if exchange.sender_id == service.order_cycle.coordinator_id - angular.extend(exchange, {enterprise_id: exchange.receiver_id, active: true}) - delete(exchange.sender_id) - service.order_cycle.outgoing_exchanges.push(exchange) - - else if exchange.receiver_id == service.order_cycle.coordinator_id + if exchange.incoming angular.extend(exchange, {enterprise_id: exchange.sender_id, active: true}) delete(exchange.receiver_id) service.order_cycle.incoming_exchanges.push(exchange) else - console.log('Exchange between two enterprises, neither of which is coordinator!') + angular.extend(exchange, {enterprise_id: exchange.receiver_id, active: true}) + delete(exchange.sender_id) + service.order_cycle.outgoing_exchanges.push(exchange) delete(service.order_cycle.exchanges) service.loaded = true diff --git a/app/assets/javascripts/darkswarm/all.js.coffee b/app/assets/javascripts/darkswarm/all.js.coffee index ead3240bf3..ea8c47eed8 100644 --- a/app/assets/javascripts/darkswarm/all.js.coffee +++ b/app/assets/javascripts/darkswarm/all.js.coffee @@ -5,6 +5,7 @@ # #= require angular #= require angular-resource +#= require ../shared/mm-foundation-tpls-0.2.0-SNAPSHOT # #= require ../shared/jquery.timeago #= require foundation @@ -12,5 +13,8 @@ #= require_tree . $ -> + # Hacky fix for issue - http://foundation.zurb.com/forum/posts/2112-foundation-5100-syntax-error-in-js + Foundation.set_namespace = -> + null $(document).foundation() - $(document).foundation('reveal', {animation: 'fade'}) + $(document).foundation({reveal: {animation: 'fade'}}) diff --git a/app/assets/javascripts/darkswarm/controllers/forgot_sidebar_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/forgot_sidebar_controller.js.coffee new file mode 100644 index 0000000000..075b4f0912 --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/forgot_sidebar_controller.js.coffee @@ -0,0 +1,6 @@ +window.ForgotSidebarCtrl = Darkswarm.controller "ForgotSidebarCtrl", ($scope, $http, $location) -> + $scope.active = -> + $location.path() == '/forgot' + + $scope.select = -> + $location.path("/forgot") diff --git a/app/assets/javascripts/darkswarm/controllers/login_sidebar_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/login_sidebar_controller.js.coffee index 5e6d051e93..a514348aba 100644 --- a/app/assets/javascripts/darkswarm/controllers/login_sidebar_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/login_sidebar_controller.js.coffee @@ -1,14 +1,16 @@ -window.LoginSidebarCtrl = Darkswarm.controller "LoginSidebarCtrl", ($scope, $http) -> +window.LoginSidebarCtrl = Darkswarm.controller "LoginSidebarCtrl", ($scope, $http, $location) -> $scope.spree_user = { remember_me: 0 } $scope.active = -> - $scope.active_sidebar == '/login' + $location.path() == '/login' + + $scope.select = -> + $location.path("/login") $scope.submit = -> $http.post("/user/spree_user/sign_in", {spree_user: $scope.spree_user}).success (data)-> location.href = location.origin + location.pathname # Strips out hash fragments .error (data) -> $scope.errors = data.message - diff --git a/app/assets/javascripts/darkswarm/controllers/menu_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/menu_controller.js.coffee new file mode 100644 index 0000000000..cd5a4ce5fc --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/menu_controller.js.coffee @@ -0,0 +1,12 @@ +window.MenuCtrl = Darkswarm.controller "MenuCtrl", ($scope, $location) -> + $scope.toggleLogin = -> + if $location.path() == "/login" + $location.path("/") + else + $location.path("login") + + $scope.toggleSignup = -> + if $location.path() == "/signup" + $location.path("/") + else + $location.path("signup") diff --git a/app/assets/javascripts/darkswarm/controllers/order_cycle_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/order_cycle_controller.js.coffee index 0f7f3cd17f..fc6413705a 100644 --- a/app/assets/javascripts/darkswarm/controllers/order_cycle_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/order_cycle_controller.js.coffee @@ -1,4 +1,5 @@ Darkswarm.controller "OrderCycleCtrl", ($scope, $rootScope, OrderCycle) -> $scope.order_cycle = OrderCycle.order_cycle + $scope.OrderCycle = OrderCycle $scope.changeOrderCycle = -> OrderCycle.push_order_cycle() diff --git a/app/assets/javascripts/darkswarm/controllers/sidebar_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/sidebar_controller.js.coffee index 0aa5f11683..6f0b954116 100644 --- a/app/assets/javascripts/darkswarm/controllers/sidebar_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/sidebar_controller.js.coffee @@ -1,8 +1,3 @@ window.SidebarCtrl = Darkswarm.controller "SidebarCtrl", ($scope, $location) -> - $scope.$watch -> - $location.path() - , -> - $scope.active_sidebar = $location.path() - $scope.active = -> - return "active" if $scope.active_sidebar != null and $scope.active_sidebar != "" + $location.path() in ["/login", "/signup", "/forgot"] diff --git a/app/assets/javascripts/darkswarm/controllers/signup_sidebar_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/signup_sidebar_controller.js.coffee index a577fc6b51..5e4c3327e1 100644 --- a/app/assets/javascripts/darkswarm/controllers/signup_sidebar_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/signup_sidebar_controller.js.coffee @@ -1,3 +1,17 @@ -window.SignupSidebarCtrl = Darkswarm.controller "SignupSidebarCtrl", ($scope) -> +window.SignupSidebarCtrl = Darkswarm.controller "SignupSidebarCtrl", ($scope, $http, $location) -> + $scope.spree_user = {} + $scope.errors = + email: null + password: null + $scope.active = -> - $scope.active_sidebar == '/signup' + $location.path() == '/signup' + + $scope.select = -> + $location.path("/signup") + + $scope.submit = -> + $http.post("/user/spree_user", {spree_user: $scope.spree_user}).success (data)-> + location.href = location.origin + location.pathname # Strips out hash fragments + .error (data) -> + $scope.errors = data diff --git a/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee new file mode 100644 index 0000000000..f6e6d15f89 --- /dev/null +++ b/app/assets/javascripts/darkswarm/controllers/tabs_controller.js.coffee @@ -0,0 +1,18 @@ +Darkswarm.controller "TabsCtrl", ($scope, $rootScope, $location, OrderCycle) -> + $scope.active = (path)-> + if !OrderCycle.selected() and $location.hash() == "" and path == "/about" + true + else + $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) + $location.hash "/" + else + $location.hash tab.path diff --git a/app/assets/javascripts/darkswarm/darkswarm.js.coffee b/app/assets/javascripts/darkswarm/darkswarm.js.coffee index de8517124f..bffcc780e3 100644 --- a/app/assets/javascripts/darkswarm/darkswarm.js.coffee +++ b/app/assets/javascripts/darkswarm/darkswarm.js.coffee @@ -1,5 +1,4 @@ -window.Darkswarm = angular.module("Darkswarm", ["ngResource", "filters"]).config ($httpProvider) -> +window.Darkswarm = angular.module("Darkswarm", ["ngResource", "filters", 'mm.foundation']).config ($httpProvider) -> $httpProvider.defaults.headers.post['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, */*" - diff --git a/app/assets/javascripts/darkswarm/overrides.js.coffee b/app/assets/javascripts/darkswarm/overrides.js.coffee index 6db1441f08..4d678c77a1 100644 --- a/app/assets/javascripts/darkswarm/overrides.js.coffee +++ b/app/assets/javascripts/darkswarm/overrides.js.coffee @@ -1,20 +1,20 @@ -Foundation.libs.section.toggle_active = (e)-> - $this = $(this) - self = Foundation.libs.section - region = $this.parent() - content = $this.siblings(self.settings.content_selector) - section = region.parent() - settings = $.extend({}, self.settings, self.data_options(section)) - prev_active_region = section.children(self.settings.region_selector).filter("." + self.settings.active_class) +#Foundation.libs.section.toggle_active = (e)-> + #$this = $(this) + #self = Foundation.libs.section + #region = $this.parent() + #content = $this.siblings(self.settings.content_selector) + #section = region.parent() + #settings = $.extend({}, self.settings, self.data_options(section)) + #prev_active_region = section.children(self.settings.region_selector).filter("." + self.settings.active_class) - #for anchors inside [data-section-title] - e.preventDefault() if not settings.deep_linking and content.length > 0 - e.stopPropagation() #do not catch same click again on parent - unless region.hasClass(self.settings.active_class) - prev_active_region.removeClass self.settings.active_class - region.addClass self.settings.active_class - #force resize for better performance (do not wait timer) - self.resize region.find(self.settings.section_selector).not("[" + self.settings.resized_data_attr + "]"), true - else if not settings.one_up# and (self.small(section) or self.is_vertical_nav(section) or self.is_horizontal_nav(section) or self.is_accordion(section)) - region.removeClass self.settings.active_class - settings.callback section + ##for anchors inside [data-section-title] + #e.preventDefault() if not settings.deep_linking and content.length > 0 + #e.stopPropagation() #do not catch same click again on parent + #unless region.hasClass(self.settings.active_class) + #prev_active_region.removeClass self.settings.active_class + #region.addClass self.settings.active_class + ##force resize for better performance (do not wait timer) + #self.resize region.find(self.settings.section_selector).not("[" + self.settings.resized_data_attr + "]"), true + #else if not settings.one_up# and (self.small(section) or self.is_vertical_nav(section) or self.is_horizontal_nav(section) or self.is_accordion(section)) + #region.removeClass self.settings.active_class + #settings.callback section diff --git a/app/assets/javascripts/darkswarm/services/order_cycle.js.coffee b/app/assets/javascripts/darkswarm/services/order_cycle.js.coffee index 84d09a9a13..58d4d11946 100644 --- a/app/assets/javascripts/darkswarm/services/order_cycle.js.coffee +++ b/app/assets/javascripts/darkswarm/services/order_cycle.js.coffee @@ -1,7 +1,15 @@ Darkswarm.factory 'OrderCycle', ($resource, Product, orderCycleData) -> class OrderCycle - @order_cycle = orderCycleData || {orders_close_at: ""} + @order_cycle = orderCycleData || null @push_order_cycle: -> new $resource("/shop/order_cycle").save {order_cycle_id: @order_cycle.order_cycle_id}, (order_data)-> OrderCycle.order_cycle.orders_close_at = order_data.orders_close_at Product.update() + + @orders_close_at: -> + if @order_cycle + @order_cycle.orders_close_at + else + "" + @selected: -> + @order_cycle != null diff --git a/app/assets/javascripts/foundation4/foundation.scss b/app/assets/javascripts/foundation4/foundation.scss new file mode 100755 index 0000000000..d9a6e98582 --- /dev/null +++ b/app/assets/javascripts/foundation4/foundation.scss @@ -0,0 +1,43 @@ +// Make sure the charset is set appropriately +@charset "UTF-8"; + +// This includes all of the foundation global elements that are needed to work with any of the other files. +@import "foundation4/foundation/variables"; + +// Foundation Components +@import + "foundation4/foundation/components/global", + "foundation4/foundation/components/grid", + "foundation4/foundation/components/visibility", + "foundation4/foundation/components/block-grid", + "foundation4/foundation/components/type", + "foundation4/foundation/components/buttons", + "foundation4/foundation/components/forms", + "foundation4/foundation/components/button-groups", + "foundation4/foundation/components/dropdown-buttons", + "foundation4/foundation/components/split-buttons", + "foundation4/foundation/components/flex-video", + "foundation4/foundation/components/section", + "foundation4/foundation/components/top-bar", + "foundation4/foundation/components/orbit", + "foundation4/foundation/components/reveal", + "foundation4/foundation/components/joyride", + "foundation4/foundation/components/clearing", + "foundation4/foundation/components/alert-boxes", + "foundation4/foundation/components/breadcrumbs", + "foundation4/foundation/components/custom-forms", + "foundation4/foundation/components/keystrokes", + "foundation4/foundation/components/labels", + "foundation4/foundation/components/inline-lists", + "foundation4/foundation/components/pagination", + "foundation4/foundation/components/panels", + "foundation4/foundation/components/pricing-tables", + "foundation4/foundation/components/progress-bars", + "foundation4/foundation/components/side-nav", + "foundation4/foundation/components/sub-nav", + "foundation4/foundation/components/switch", + "foundation4/foundation/components/magellan", + "foundation4/foundation/components/tables", + "foundation4/foundation/components/thumbs", + "foundation4/foundation/components/tooltips", + "foundation4/foundation/components/dropdown"; diff --git a/app/assets/javascripts/foundation4/foundation/_variables.scss b/app/assets/javascripts/foundation4/foundation/_variables.scss new file mode 100755 index 0000000000..797474c676 --- /dev/null +++ b/app/assets/javascripts/foundation4/foundation/_variables.scss @@ -0,0 +1,1325 @@ +// +// Foundation Variables +// + +// The default font-size is set to 100% of the browser style sheet (usually 16px) +// for compatibility with browser-based text zoom or user-set defaults. +$base-font-size: 100% !default; + +// $base-line-height is 24px while $base-font-size is 16px +// $base-line-height: 150%; + +// This is the default html and body font-size for the base em value. + +// Since the typical default browser font-size is 16px, that makes the calculation for grid size. +// If you want your base font-size to be a different size and not have it effect grid size too, +// set the value of $em-base to $base-font-size ($em-base: $base-font-size;) +$em-base: 16px !default; + +// It strips the unit of measure and returns it +@function strip-unit($num) { + @return $num / ($num * 0 + 1); +} + +// Converts "px" to "em" using the ($)em-base +@function convert-to-em($value, $base-value: $em-base) { + $value: strip-unit($value) / strip-unit($base-value) * 1em; + @if ($value == 0em) { $value: 0; } // Turn 0em into 0 + @return $value; +} + +// Working in ems is annoying. Think in pixels by using this handy function, em-calc(#) +// Just enter the number, no need to mention "px" +@function em-calc($values, $base-value: $em-base) { + $max: length($values); // Get the total number of parameters passed + + // If there is only 1 parameter, then return it as an integer. + // This is done because a list can't be multiplied or divided even if it contains a single value + @if $max == 1 { @return convert-to-em(nth($values, 1), $base-value); } + + $emValues: (); // This will eventually store the converted $values in a list + @for $i from 1 through $max { + $emValues: append($emValues, convert-to-em(nth($values, $i), $base-value)); + } + @return $emValues; +} + +//Retaining this for backward compatability + +@function emCalc($pxWidth) { + @return $pxWidth / $em-base * 1em; +} + +// Maybe you want to create rems with pixels +// $rem-base: 0.625 !default; //Set the value corresponding to body font size. In this case, you should set as: body {font-size: 62.5%;} +// @function rem-calc($pxWidth) { +// @return $pxWidth / $rem-base * 1rem; +// } + +// Change whether or not you include browser prefixes +// $experimental: true; + +// Various global styles + +$default-float: left; + +// $body-bg: #fff; +// $body-font-color: #222; +// $body-font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; +// $body-font-weight: normal; +// $body-font-style: normal; + +// Font-smoothing + +// $font-smoothing: antialiased; + +// Text direction settings + +// $text-direction: ltr; + +// Colors + +// $primary-color: #2ba6cb; +// $secondary-color: #e9e9e9; +// $alert-color: #c60f13; +// $success-color: #5da423; + +// Make sure border radius matches unless we want it different. + +// $global-radius: 3px; +// $global-rounded: 1000px; + +// Inset shadow shiny edges and depressions. + +// $shiny-edge-size: 0 1px 0; +// $shiny-edge-color: rgba(#fff, .5); +// $shiny-edge-active-color: rgba(#000, .2); + +// Control whether or not CSS classes come through in the CSS files. + +// $include-html-classes: true; +// $include-print-styles: true; +// $include-html-global-classes: $include-html-classes; +// $include-html-inline-list-classes: $include-html-classes; +// $include-html-type-classes: $include-html-classes; +// $include-html-grid-classes: $include-html-classes; +// $include-html-visibility-classes: $include-html-classes; +// $include-html-button-classes: $include-html-classes; +// $include-html-form-classes: $include-html-classes; +// $include-html-custom-form-classes: $include-html-classes; +// $include-html-media-classes: $include-html-classes; +// $include-html-section-classes: $include-html-classes; +// $include-html-orbit-classes: $include-html-classes; +// $include-html-reveal-classes: $include-html-classes; +// $include-html-joyride-classes: $include-html-classes; +// $include-html-clearing-classes: $include-html-classes; +// $include-html-alert-classes: $include-html-classes; +// $include-html-nav-classes: $include-html-classes; +// $include-html-top-bar-classes: $include-html-classes; +// $include-html-label-classes: $include-html-classes; +// $include-html-panel-classes: $include-html-classes; +// $include-html-pricing-classes: $include-html-classes; +// $include-html-progress-classes: $include-html-classes; +// $include-html-magellan-classes: $include-html-classes; +// $include-html-tooltip-classes: $include-html-classes; + +// Media Queries + +// $small-screen: 768px; +// $medium-screen: 1280px; +// $large-screen: 1440px; + +// $screen: "only screen"; +// $small: "only screen and (min-width: #{$small-screen})"; +// $medium: "only screen and (min-width: #{$medium-screen})"; +// $large: "only screen and (min-width: #{$large-screen})"; +// $landscape: "only screen and (orientation: landscape)"; +// $portrait: "only screen and (orientation: portrait)"; + +//// Cursors + +//Custom use example -> $cursor-default-value: url(http://cursors-site.net/path/to/custom/cursor/default.cur),progress; + +// $cursor-crosshair-value: "crosshair"; +// $cursor-default-value: "default"; +// $cursor-pointer-value: "pointer"; +// $cursor-help-value: "help"; + +// +// Grid Variables +// + +// $row-width: em-calc(1000); +// $column-gutter: em-calc(30); +// $total-columns: 12; + +// +// Block Grid Variables +// + +// We use this to control the maximum number of block grid elements per row + +// $block-grid-elements: 12; +// $block-grid-default-spacing: em-calc(20); + +// Enables media queries for block-grid classes. Set to false if writing semantic HTML. + +// $block-grid-media-queries: true; + +// +// Typography Variables +// + +// Control header font styles + +// $header-font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; +// $header-font-weight: bold; +// $header-font-style: normal; +// $header-font-color: #222; +// $header-line-height: 1.4; +// $header-top-margin: .2em; +// $header-bottom-margin: .5em; +// $header-text-rendering: optimizeLegibility; + +// Control header font sizes + +// $h1-font-size: em-calc(44); +// $h2-font-size: em-calc(37); +// $h3-font-size: em-calc(27); +// $h4-font-size: em-calc(23); +// $h5-font-size: em-calc(18); +// $h6-font-size: 1em; + +// Control how subheaders are styled. + +// $subheader-line-height: 1.4; +// $subheader-font-color: lighten($header-font-color, 30%); +// $subheader-font-weight: 300; +// $subheader-top-margin: .2em; +// $subheader-bottom-margin: .5em; + +// A general styling + +// $small-font-size: 60%; +// $small-font-color: lighten($header-font-color, 30%); + +// Style paragraphs + +// $paragraph-font-family: inherit; +// $paragraph-font-weight: normal; +// $paragraph-font-size: 1em; +// $paragraph-line-height: 1.6; +// $paragraph-margin-bottom: em-calc(20); +// $paragraph-aside-font-size: em-calc(14); +// $paragraph-aside-line-height: 1.35; +// $paragraph-aside-font-style: italic; +// $paragraph-text-rendering: optimizeLegibility; + +// Style tags + +// $code-color: darken($alert-color, 15%); +// $code-font-family: Consolas, 'Liberation Mono', Courier, monospace; +// $code-font-weight: bold; + +// Style anchors + +// $anchor-text-decoration: none; +// $anchor-font-color: $primary-color; +// $anchor-font-color-hover: darken($primary-color, 5%); + +// Style the
element + +// $hr-border-width: 1px; +// $hr-border-style: solid; +// $hr-border-color: #ddd; +// $hr-margin: em-calc(20); + +// Style lists + +// $list-style-position: outside; +// $list-side-margin: 0; +// $list-nested-margin: em-calc(20); +// $definition-list-header-weight: bold; +// $definition-list-header-margin-bottom: .3em; +// $definition-list-margin-bottom: em-calc(12); + +// Style blockquotes + +// $blockquote-font-color: lighten($header-font-color, 30%); +// $blockquote-padding: em-calc(9, 20, 0, 19); +// $blockquote-border: 1px solid #ddd; +// $blockquote-cite-font-size: em-calc(13); +// $blockquote-cite-font-color: lighten($header-font-color, 20%); +// $blockquote-cite-link-color: $blockquote-cite-font-color; + +// Acronym styles + +// $acronym-underline: 1px dotted #ddd; + +// Control padding and margin + +// $microformat-padding: em-calc(10 12); +// $microformat-margin: em-calc(0 0 20 0); + +// Control the border styles + +// $microformat-border-width: 1px; +// $microformat-border-style: solid; +// $microformat-border-color: #ddd; + +// Control full name font styles + +// $microformat-fullname-font-weight: bold; +// $microformat-fullname-font-size: em-calc(15); + +// Control the summary font styles + +// $microformat-summary-font-weight: bold; + +// Control abbr padding +// $microformat-abbr-padding: em-calc(0 1); + +// Control abbr font styles + +// $microformat-abbr-font-weight: bold; +// $microformat-abbr-font-decoration: none; + +// +// Form Variables +// + +// We use this to set the base for lots of form spacing and positioning styles + +// $form-spacing: em-calc(16); + +// We use these to style the labels in different ways + +// $form-label-pointer: pointer; +// $form-label-font-size: em-calc(14); +// $form-label-font-weight: 500; +// $form-label-font-color: lighten(#000, 30%); +// $form-label-bottom-margin: em-calc(3); +// $input-font-family: inherit; +// $input-font-color: rgba(0,0,0,0.75); +// $input-font-size: em-calc(14); +// $input-bg-color: #fff; +// $input-focus-bg-color: darken(#fff, 2%); +// $input-border-color: darken(#fff, 20%); +// $input-focus-border-color: darken(#fff, 40%); +// $input-border-style: solid; +// $input-border-width: 1px; +// $input-disabled-bg: #ddd; +// $input-box-shadow: inset 0 1px 2px rgba(0,0,0,0.1); +// $input-include-glowing-effect: true; + +// We use these to style the fieldset border and spacing. + +// $fieldset-border-style: solid; +// $fieldset-border-width: 1px; +// $fieldset-border-color: #ddd; +// $fieldset-padding: em-calc(20); +// $fieldset-margin: em-calc(18 0); + +// We use these to style the legends when you use them + +// $legend-bg: #fff; +// $legend-font-weight: bold; +// $legend-padding: em-calc(0 3); + +// We use these to style the prefix and postfix input elements + +// $input-prefix-bg: darken(#fff, 5%); +// $input-prefix-border-color: darken(#fff, 20%); +// $input-prefix-border-size: 1px; +// $input-prefix-border-type: solid; +// $input-prefix-overflow: hidden; +// $input-prefix-font-color: #333; +// $input-prefix-font-color-alt: #fff; + +// We use these to style the error states for inputs and labels + +// $input-error-message-padding: em-calc(6 4); +// $input-error-message-top: 0; +// $input-error-message-font-size: em-calc(12); +// $input-error-message-font-weight: bold; +// $input-error-message-font-color: #fff; +// $input-error-message-font-color-alt: #333; + +// We use this to style the glowing effect of inputs when focused + +// $glowing-effect-fade-time: 0.45s; +// $glowing-effect-color: $input-focus-border-color; + +// +// Button Variables +// + +// We use these to build padding for buttons. + +// $button-med: em-calc(12); +// $button-tny: em-calc(7); +// $button-sml: em-calc(9); +// $button-lrg: em-calc(16); + +// We use this to control the display property. + +// $button-display: inline-block; +// $button-margin-bottom: em-calc(20); + +// We use these to control button text styles. + +// $button-font-family: inherit; +// $button-font-color: #fff; +// $button-font-color-alt: #333; +// $button-font-med: em-calc(16); +// $button-font-tny: em-calc(11); +// $button-font-sml: em-calc(13); +// $button-font-lrg: em-calc(20); +// $button-font-weight: bold; +// $button-font-align: center; + +// We use these to control various hover effects. + +// $button-function-factor: 10%; + +// We use these to control button border styles. + +// $button-border-width: 1px; +// $button-border-style: solid; + +// We use this to set the default radius used throughout the core. + +// $button-radius: $global-radius; +// $button-round: $global-rounded; + +// We use this to set default opacity for disabled buttons. + +// $button-disabled-opacity: 0.6; + +// +// Button Groups +// + +// Sets the margin for the right side by default, and the left margin if right-to-left direction is used + +// $button-bar-margin-opposite: em-calc(10); + +// +// Dropdown Button Variables +// + +// We use these to set the color of the pip in dropdown buttons + +// $dropdown-button-pip-color: #fff; +// $dropdown-button-pip-color-alt: #333; + +// We use these to style tiny dropdown buttons + +// $dropdown-button-padding-tny: $button-tny * 5; +// $dropdown-button-pip-size-tny: $button-tny; +// $dropdown-button-pip-opposite-tny: $button-tny * 2; +// $dropdown-button-pip-top-tny: -$button-tny / 2 + em-calc(1); + +// We use these to style small dropdown buttons + +// $dropdown-button-padding-sml: $button-sml * 5; +// $dropdown-button-pip-size-sml: $button-sml; +// $dropdown-button-pip-opposite-sml: $button-sml * 2; +// $dropdown-button-pip-top-sml: -$button-sml / 2 + em-calc(1); + +// We use these to style medium dropdown buttons + +// $dropdown-button-padding-med: $button-med * 4 + em-calc(3); +// $dropdown-button-pip-size-med: $button-med - em-calc(3); +// $dropdown-button-pip-opposite-med: $button-med * 2; +// $dropdown-button-pip-top-med: -$button-med / 2 + em-calc(2); + +// We use these to style large dropdown buttons + +// $dropdown-button-padding-lrg: $button-lrg * 4; +// $dropdown-button-pip-size-lrg: $button-lrg - em-calc(6); +// $dropdown-button-pip-opposite-lrg: $button-lrg + em-calc(12); +// $dropdown-button-pip-top-lrg: -$button-lrg / 2 + em-calc(3); + +// +// Split Button Variables +// + +// We use these to control different shared styles for Split Buttons + +// $split-button-function-factor: 15%; +// $split-button-pip-color: #fff; +// $split-button-pip-color-alt: #333; +// $split-button-active-bg-tint: rgba(0,0,0,0.1); + +// We use these to control tiny split buttons + +// $split-button-padding-tny: $button-tny * 9; +// $split-button-span-width-tny: $button-tny * 6.5; +// $split-button-pip-size-tny: $button-tny; +// $split-button-pip-top-tny: $button-tny * 2; +// $split-button-pip-default-float-tny: em-calc(-5); + +// We use these to control small split buttons + +// $split-button-padding-sml: $button-sml * 7; +// $split-button-span-width-sml: $button-sml * 5; +// $split-button-pip-size-sml: $button-sml; +// $split-button-pip-top-sml: $button-sml * 1.5; +// $split-button-pip-default-float-sml: em-calc(-9); + +// We use these to control medium split buttons + +// $split-button-padding-med: $button-med * 6.4; +// $split-button-span-width-med: $button-med * 4; +// $split-button-pip-size-med: $button-med - em-calc(3); +// $split-button-pip-top-med: $button-med * 1.5; +// $split-button-pip-default-float-med: em-calc(-9); + +// We use these to control large split buttons + +// $split-button-padding-lrg: $button-lrg * 6; +// $split-button-span-width-lrg: $button-lrg * 3.75; +// $split-button-pip-size-lrg: $button-lrg - em-calc(6); +// $split-button-pip-top-lrg: $button-lrg + em-calc(5); +// $split-button-pip-default-float-lrg: em-calc(-9); + +// +// Alert Box Variables +// + +// We use this to control alert padding. + +// $alert-padding-top: em-calc(11); +// $alert-padding-default-float: $alert-padding-top; +// $alert-padding-opposite-direction: $alert-padding-top + em-calc(10); +// $alert-padding-bottom: $alert-padding-top + em-calc(1); + +// We use these to control text style. + +// $alert-font-weight: bold; +// $alert-font-size: em-calc(14); +// $alert-font-color: #fff; +// $alert-font-color-alt: darken($secondary-color, 60%); + +// We use this for close hover effect. + +// $alert-function-factor: 10%; + +// We use these to control border styles. + +// $alert-border-style: solid; +// $alert-border-width: 1px; +// $alert-border-color: darken($primary-color, $alert-function-factor); +// $alert-bottom-margin: em-calc(20); + +// We use these to style the close buttons + +// $alert-close-color: #333; +// $alert-close-position: em-calc(5); +// $alert-close-font-size: em-calc(22); +// $alert-close-opacity: 0.3; +// $alert-close-opacity-hover: 0.5; +// $alert-close-padding: 5px 4px 4px; + +// We use this to control border radius + +// $alert-radius: $global-radius; + + +// +// Breadcrumb Variables +// + +// We use this to set the background color for the breadcrumb container. + +// $crumb-bg: lighten($secondary-color, 5%); + +// We use these to set the padding around the breadcrumbs. + +// $crumb-padding: em-calc(9 14 9); +// $crumb-side-padding: em-calc(12); + +// We use these to control border styles. + +// $crumb-function-factor: 10%; +// $crumb-border-size: 1px; +// $crumb-border-style: solid; +// $crumb-border-color: darken($crumb-bg, $crumb-function-factor); +// $crumb-radius: $global-radius; + +// We use these to set various text styles for breadcrumbs. + +// $crumb-font-size: em-calc(11); +// $crumb-font-color: $primary-color; +// $crumb-font-color-current: #333; +// $crumb-font-color-unavailable: #999; +// $crumb-font-transform: uppercase; +// $crumb-link-decor: underline; + +// We use these to control the slash between breadcrumbs + +// $crumb-slash-color: #aaa; +// $crumb-slash: "/"; + +// +// Clearing Variables +// + +// We use these to set the background colors for parts of Clearing. + +// $clearing-bg: #111; +// $clearing-caption-bg: $clearing-bg; +// $clearing-carousel-bg: #111; +// $clearing-img-bg: $clearing-bg; + +// We use these to style the close button + +// $clearing-close-color: #fff; +// $clearing-close-size: 40px; + +// We use these to style the arrows + +// $clearing-arrow-size: 16px; +// $clearing-arrow-color: $clearing-close-color; + +// We use these to style captions + +// $clearing-caption-font-color: #fff; +// $clearing-caption-padding: 10px 30px; + +// We use these to make the image and carousel height and style + +// $clearing-active-img-height: 75%; +// $clearing-carousel-height: 150px; +// $clearing-carousel-thumb-width: 175px; +// $clearing-carousel-thumb-active-border: 4px solid rgb(255,255,255); + +// +// Custom Form Variables +// + +// We use these to control the basic form styles input styles + +// $custom-form-border-color: #ccc; +// $custom-form-border-size: 1px; +// $custom-form-bg: #fff; +// $custom-form-bg-disabled: #ddd; +// $custom-form-input-size: 16px; +// $custom-form-check-color: #222; +// $custom-form-check-size: 16px; +// $custom-form-radio-size: 8px; +// $custom-form-checkbox-radius: 0; + +// We use these to style the custom select form element. + +// $custom-select-bg: #fff; +// $custom-select-fade-to-color: #f3f3f3; +// $custom-select-border-color: #ddd; +// $custom-select-triangle-color: #aaa; +// $custom-select-triangle-color-open: #222; +// $custom-select-height: em-calc(13) + ($form-spacing * 1.5); +// $custom-select-margin-bottom: em-calc(20); +// $custom-select-font-color-selected: #141414; +// $custom-select-disabled-color: #888; + +// We use these to control the style of the custom select dropdown element. + +// $custom-dropdown-height: 200px; +// $custom-dropdown-bg: #fff; +// $custom-dropdown-border-color: darken(#fff, 20%); +// $custom-dropdown-border-width: 1px; +// $custom-dropdown-border-style: solid; +// $custom-dropdown-font-color: #555; +// $custom-dropdown-font-size: em-calc(14); +// $custom-dropdown-color-selected: #eeeeee; +// $custom-dropdown-font-color-selected: #000; +// $custom-dropdown-shadow: 0 2px 2px 0 rgba(0,0,0,0.1); +// $custom-dropdown-offset-top: auto; +// $custom-dropdown-list-padding: em-calc(4); +// $custom-dropdown-default-float-padding: em-calc(6); +// $custom-dropdown-opposite-padding: em-calc(38); +// $custom-dropdown-list-item-min-height: em-calc(24); +// $custom-dropdown-width-small: 134px; +// $custom-dropdown-width-medium: 254px; +// $custom-dropdown-width-large: 434px; + +// +// Dropdown Variables +// + +// We use these to controls height and width styles. + +// $f-dropdown-max-width: 200px; +// $f-dropdown-height: auto; +// $f-dropdown-max-height: none; +// $f-dropdown-margin-top: 2px; + +// We use this to control the background color + +// $f-dropdown-bg: #fff; + +// We use this to set the border styles for dropdowns. + +// $f-dropdown-border-style: solid; +// $f-dropdown-border-width: 1px; +// $f-dropdown-border-color: darken(#fff, 20%); + +// We use these to style the triangle pip. + +// $f-dropdown-triangle-size: 6px; +// $f-dropdown-triangle-color: #fff; +// $f-dropdown-triangle-side-offset: 10px; + +// We use these to control styles for the list elements. + +// $f-dropdown-list-style: none; +// $f-dropdown-font-color: #555; +// $f-dropdown-font-size: em-calc(14); +// $f-dropdown-list-padding: em-calc(5 10); +// $f-dropdown-line-height: em-calc(18); +// $f-dropdown-list-hover-bg: #eeeeee; +// $dropdown-mobile-default-float: 0; + +// We use this to control the styles for when the dropdown has custom content. + +// $f-dropdown-content-padding: em-calc(20); + +// +// Flex Video Variables +// + +// We use these to control video container padding and margins + +// $flex-video-padding-top: em-calc(25); +// $flex-video-padding-bottom: 67.5%; +// $flex-video-margin-bottom: em-calc(16); + +// We use this to control widescreen bottom padding + +// $flex-video-widescreen-padding-bottom: 57.25%; + +// +// Inline List Variables +// + +// We use this to control the margins and padding of the inline list. + +// $inline-list-top-margin: 0; +// $inline-list-opposite-margin: 0; +// $inline-list-bottom-margin: em-calc(17); +// $inline-list-default-float-margin: em-calc(-22); + +// $inline-list-padding: 0; + +// We use this to control the overflow of the inline list. + +// $inline-list-overflow: hidden; + +// We use this to control the list items + +// $inline-list-display: block; + +// We use this to control any elments within list items + +// $inline-list-children-display: block; + +// +// Joyride Variables +// + +// Controlling default Joyride styles + +// $joyride-tip-bg: rgb(0,0,0); +// $joyride-tip-default-width: 300px; +// $joyride-tip-padding: em-calc(18 20 24); +// $joyride-tip-border: solid 1px #555; +// $joyride-tip-radius: 4px; +// $joyride-tip-position-offset: 22px; + +// Here, we're setting the tip dont styles + +// $joyride-tip-font-color: #fff; +// $joyride-tip-font-size: em-calc(14); +// $joyride-tip-header-weight: bold; + +// This changes the nub size + +// $joyride-tip-nub-size: 14px; + +// This adjusts the styles for the timer when its enabled + +// $joyride-tip-timer-width: 50px; +// $joyride-tip-timer-height: 3px; +// $joyride-tip-timer-color: #666; + +// This changes up the styles for the close button + +// $joyride-tip-close-color: #777; +// $joyride-tip-close-size: 30px; +// $joyride-tip-close-weight: normal; + +// When Joyride is filling the screen, we use this style for the bg + +// $joyride-screenfill: rgba(0,0,0,0.5); + +// +// Keystroke Variables +// + +// We use these to control text styles. + +// $keystroke-font: "Consolas", "Menlo", "Courier", monospace; +// $keystroke-font-size: em-calc(14); +// $keystroke-font-color: #222; +// $keystroke-font-color-alt: #fff; +// $keystroke-function-factor: 7%; + +// We use this to control keystroke padding. + +// $keystroke-padding: em-calc(2 4 0); + +// We use these to control background and border styles. + +// $keystroke-bg: darken(#fff, $keystroke-function-factor); +// $keystroke-border-style: solid; +// $keystroke-border-width: 1px; +// $keystroke-border-color: darken($keystroke-bg, $keystroke-function-factor); +// $keystroke-radius: $global-radius; + +// +// Label Variables +// + +// We use these to style the labels + +// $label-padding: em-calc(3 10 4); +// $label-radius: $global-radius; + +// We use these to style the label text + +// $label-font-sizing: em-calc(14); +// $label-font-weight: bold; +// $label-font-color: #333; +// $label-font-color-alt: #fff; + +// +// Magellan Variables +// + +// $magellan-bg: #fff; +// $magellan-padding: 10px; + +// +// Orbit Settings +// + +// We use these to control the caption styles + +// $orbit-container-bg: #f5f5f5; +// $orbit-caption-bg: rgba(0,0,0,0.6); +// $orbit-caption-font-color: #fff; +// $orbit-caption-font-size: emCalc(14); +// $orbit-caption-position: "bottom"; // Supported values: "bottom", "under" +// $orbit-caption-padding: emCalc(10,14); +// $orbit-caption-height: auto; + +// We use these to control the left/right nav styles + +// $orbit-nav-bg: rgba(0,0,0,0.6); +// $orbit-nav-bg-hover: rgba(0,0,0,0.6); +// $orbit-nav-arrow-color: #fff; +// $orbit-nav-arrow-color-hover: #ccc; + +// We use these to control the timer styles + +// $orbit-timer-bg: rgba(0,0,0,0.6); +// $orbit-timer-show-progress-bar: true; + +// We use these to control the bullet nav styles + +// $orbit-bullet-nav-color: #999; +// $orbit-bullet-nav-color-active: #555; +// $orbit-bullet-radius: emCalc(18); + +// We use these to controls the style of slide numbers + +// $orbit-slide-number-bg: rgba(0,0,0,0); +// $orbit-slide-number-font-color: #fff; +// $orbit-slide-number-padding: em-calc(5); + +// Graceful Loading Wrapper and preloader + +// $wrapper-class: "slideshow-wrapper"; +// $preloader-class: "preloader"; + +// +// Pagination Variables +// + +// We use these to control the pagination container + +// $pagination-height: em-calc(24); +// $pagination-margin: em-calc(-5); + +// We use these to set the list-item properties + +// $pagination-li-float: $default-float; +// $pagination-li-height: em-calc(24); +// $pagination-li-font-color: #222; +// $pagination-li-font-size: em-calc(14); +// $pagination-li-margin: em-calc(5); + +// We use these for the pagination anchor links + +// $pagination-link-pad: em-calc(1 7 1); +// $pagination-link-font-color: #999; +// $pagination-link-active-bg: darken(#fff, 10%); + +// We use these for disabled anchor links + +// $pagination-link-unavailable-cursor: default; +// $pagination-link-unavailable-font-color: #999; +// $pagination-link-unavailable-bg-active: transparent; + +// We use these for currently selected anchor links + +// $pagination-link-current-background: $primary-color; +// $pagination-link-current-font-color: #fff; +// $pagination-link-current-font-weight: bold; +// $pagination-link-current-cursor: default; +// $pagination-link-current-active-bg: $primary-color; + +// +// Panel Variables +// + +// We use these to control the background and border styles + +// $panel-bg: darken(#fff, 5%); +// $panel-border-style: solid; +// $panel-border-size: 1px; + +// We use this % to control how much we darken things on hover + +// $panel-function-factor: 10%; +// $panel-border-color: darken($panel-bg, $panel-function-factor); + +// We use these to set default inner padding and bottom margin + +// $panel-margin-bottom: em-calc(20); +// $panel-padding: em-calc(20); + +// We use these to set default font colors + +// $panel-font-color: #333; +// $panel-font-color-alt: #fff; + +// $panel-header-adjust: true; + +// +// Pricing Table Variables +// + +// We use this to control the border color + +// $price-table-border: solid 1px #ddd; + +// We use this to control the bottom margin of the pricing table + +// $price-table-margin-bottom: em-calc(20); + +// We use these to control the title styles + +// $price-title-bg: #ddd; +// $price-title-padding: em-calc(15 20); +// $price-title-align: center; +// $price-title-color: #333; +// $price-title-weight: bold; +// $price-title-size: em-calc(16); + +// We use these to control the price styles + +// $price-money-bg: #eee; +// $price-money-padding: em-calc(15, 20); +// $price-money-align: center; +// $price-money-color: #333; +// $price-money-weight: normal; +// $price-money-size: em-calc(20); + +// We use these to control the description styles + +// $price-bg: #fff; +// $price-desc-color: #777; +// $price-desc-padding: em-calc(15); +// $price-desc-align: center; +// $price-desc-font-size: em-calc(12); +// $price-desc-weight: normal; +// $price-desc-line-height: 1.4; +// $price-desc-bottom-border: dotted 1px #ddd; + +// We use these to control the list item styles + +// $price-item-color: #333; +// $price-item-padding: em-calc(15); +// $price-item-align: center; +// $price-item-font-size: em-calc(14); +// $price-item-weight: normal; +// $price-item-bottom-border: dotted 1px #ddd; + +// We use these to control the CTA area styles + +// $price-cta-bg: #f5f5f5; +// $price-cta-align: center; +// $price-cta-padding: em-calc(20 20 0); + +// +// Progress Bar Variables +// + +// We use this to se the prog bar height + +// $progress-bar-height: em-calc(25); +// $progress-bar-color: transparent; + +// We use these to control the border styles + +// $progress-bar-border-color: darken(#fff, 20%); +// $progress-bar-border-size: 1px; +// $progress-bar-border-style: solid; +// $progress-bar-border-radius: $global-radius; + +// We use these to control the margin & padding + +// $progress-bar-pad: em-calc(2); +// $progress-bar-margin-bottom: em-calc(10); + +// We use these to set the meter colors + +// $progress-meter-color: $primary-color; +// $progress-meter-secondary-color: $secondary-color; +// $progress-meter-success-color: $success-color; +// $progress-meter-alert-color: $alert-color; + +// +// Reveal Variables +// + +// We use these to control the style of the reveal overlay. + +// $reveal-overlay-bg: rgba(#000, .45); +// $reveal-overlay-bg-old: #000; + +// We use these to control the style of the modal itself. + +// $reveal-modal-bg: #fff; +// $reveal-position-top: 50px; +// $reveal-default-width: 80%; +// $reveal-modal-padding: em-calc(20); +// $reveal-box-shadow: 0 0 10px rgba(#000,.4); + +// We use these to style the reveal close button + +// $reveal-close-font-size: em-calc(22); +// $reveal-close-top: em-calc(8); +// $reveal-close-side: em-calc(11); +// $reveal-close-color: #aaa; +// $reveal-close-weight: bold; + +// We use these to control the modal border + +// $reveal-border-style: solid; +// $reveal-border-width: 1px; +// $reveal-border-color: #666; + +// $reveal-modal-class: "reveal-modal"; +// $close-reveal-modal-class: "close-reveal-modal"; + +// +// Section Variables +// + +// We use these to set padding and hover factor + +// $section-title-padding: em-calc(15); +// $section-content-padding: em-calc(15); +// $section-function-factor: 10%; + +// These style the titles + +// $section-title-color: #333; +// $section-title-color-active: #333; +// $section-title-bg: #efefef; +// $section-title-bg-active: darken($section-title-bg, $section-function-factor); +// $section-title-bg-active-tabs: #fff; +// $section-title-bg-hover: darken($section-title-bg, $section-function-factor / 2); + +// Want to control border size, here ya go! + +// $section-border-size: 1px; +// $section-border-style: solid; +// $section-border-color: #ccc; + +// Font controls + +// $section-font-size: em-calc(14); + +// Control the color of the background and some size options + +// $section-content-bg: #fff; +// $section-vertical-nav-min-width: em-calc(200); +// $section-vertical-tabs-title-width: em-calc(200); +// $section-bottom-margin: em-calc(20); + +// $title-selector: ".title"; +// $content-selector: ".content"; +// $active-region-selector: ".active"; + +// +// Side Nav Variables +// + +// We use this to control padding. + +// $side-nav-padding: em-calc(14 0); + +// We use these to control list styles. + +// $side-nav-list-type: none; +// $side-nav-list-position: inside; +// $side-nav-list-margin: em-calc(0 0 7 0); + +// We use these to control link styles. + +// $side-nav-link-color: $primary-color; +// $side-nav-link-color-active: lighten(#000, 30%); +// $side-nav-font-size: em-calc(14); +// $side-nav-font-weight: bold; + +// We use these to control border styles + +// $side-nav-divider-size: 1px; +// $side-nav-divider-style: solid; +// $side-nav-divider-color: darken(#fff, 10%); + +// +// Sub Nav Variables +// + +// We use these to control margin and padding + +// $sub-nav-list-margin: em-calc(-4 0 18); +// $sub-nav-list-padding-top: em-calc(4); + +// We use this to control the definition + +// $sub-nav-font-size: em-calc(14); +// $sub-nav-font-color: #999; +// $sub-nav-font-weight: normal; +// $sub-nav-text-decoration: none; +// $sub-nav-border-radius: 1000px; + +// We use these to control the active item styles + +// $sub-nav-active-font-weight: bold; +// $sub-nav-active-bg: $primary-color; +// $sub-nav-active-color: #fff; +// $sub-nav-active-padding: em-calc(3 9); +// $sub-nav-active-cursor: default; + +// $sub-nav-item-divider: "" !default; +// $sub-nav-item-divider-margin: emCalc(12) !default; + +// +// Switch Variables +// + +// Controlling border styles and background colors for the switch container + +// $switch-border-color: darken(#fff, 20%); +// $switch-border-style: solid; +// $switch-border-width: 1px; +// $switch-bg: #fff; + +// We use these to control the switch heights for our default classes + +// $switch-height-tny: 22px; +// $switch-height-sml: 28px; +// $switch-height-med: 36px; +// $switch-height-lrg: 44px; +// $switch-bottom-margin: em-calc(20); + +// We use these to control default font sizes for our classes. + +// $switch-font-size-tny: 11px; +// $switch-font-size-sml: 12px; +// $switch-font-size-med: 14px; +// $switch-font-size-lrg: 17px; +// $switch-label-side-padding: 6px; + +// We use these to style the switch-paddle + +// $switch-paddle-bg: #fff; +// $switch-paddle-fade-to-color: darken($switch-paddle-bg, 10%); +// $switch-paddle-border-color: darken($switch-paddle-bg, 35%); +// $switch-paddle-border-width: 1px; +// $switch-paddle-border-style: solid; +// $switch-paddle-transition-speed: .1s; +// $switch-paddle-transition-ease: ease-out; +// $switch-positive-color: lighten($success-color, 50%); +// $switch-negative-color: #f5f5f5; + +// Outline Style for tabbing through switches + +// $switch-label-outline: 1px dotted #888; + +// +// Table Variables +// + +// These control the background color for the table and even rows + +// $table-bg: #fff; +// $table-even-row-bg: #f9f9f9; + +// These control the table cell border style + +// $table-border-style: solid; +// $table-border-size: 1px; +// $table-border-color: #ddd; + +// These control the table head styles + +// $table-head-bg: #f5f5f5; +// $table-head-font-size: em-calc(14); +// $table-head-font-color: #222; +// $table-head-font-weight: bold; +// $table-head-padding: em-calc(8 10 10); + +// These control the row padding and font styles + +// $table-row-padding: em-calc(9 10); +// $table-row-font-size: em-calc(14); +// $table-row-font-color: #222; +// $table-line-height: em-calc(18); + +// These are for controlling the display and margin of tables + +// $table-display: table-cell; +// $table-margin-bottom: em-calc(20); + +// +// Image Thumbnail Variables +// + +// We use these to control border styles + +// $thumb-border-style: solid; +// $thumb-border-width: 4px; +// $thumb-border-color: #fff; +// $thumb-box-shadow: 0 0 0 1px rgba(#000,.2); +// $thumb-box-shadow-hover: 0 0 6px 1px rgba($primary-color,0.5); + +// Radius and transition speed for thumbs + +// $thumb-radius: $global-radius; +// $thumb-transition-speed: 200ms; + +// +// Tooltip Variables +// + +// $has-tip-border-bottom: dotted 1px #ccc; +// $has-tip-font-weight: bold; +// $has-tip-font-color: #333; +// $has-tip-border-bottom-hover: dotted 1px darken($primary-color, 20%); +// $has-tip-font-color-hover: $primary-color; +// $has-tip-cursor-type: help; + +// $tooltip-padding: em-calc(8); +// $tooltip-bg: #000; +// $tooltip-font-size: em-calc(15); +// $tooltip-font-weight: bold; +// $tooltip-font-color: #fff; +// $tooltip-line-height: 1.3; +// $tooltip-close-font-size: em-calc(10); +// $tooltip-close-font-weight: normal; +// $tooltip-close-font-color: #888; +// $tooltip-font-size-sml: em-calc(14); +// $tooltip-radius: $global-radius; +// $tooltip-pip-size: 5px; + +// +// Top Bar Variables +// + +// Background color for the top bar + +// $topbar-bg-color: #111; +// $topbar-bg: $topbar-bg-color; + +// Height and margin + +// $topbar-height: 45px; +// $topbar-margin-bottom: 0; + +// Control Input height for top bar + +// $topbar-input-height: 2.45em; + +// Controlling the styles for the title in the top bar + +// $topbar-title-weight: bold; +// $topbar-title-font-size: em-calc(17); + +// Style the top bar dropdown elements + +// $topbar-dropdown-bg: #222; +// $topbar-dropdown-link-color: #fff; +// // $topbar-dropdown-link-bg: lighten($topbar-bg-color, 5%); +// $topbar-dropdown-toggle-size: 5px; +// $topbar-dropdown-toggle-color: #fff; +// $topbar-dropdown-toggle-alpha: 0.5; + +// Set the link colors and styles for top-level nav + +// $topbar-link-color: #fff; +// $topbar-link-color-hover: #fff; +// $topbar-link-color-active: #fff; +// $topbar-link-weight: bold; +// $topbar-link-font-size: em-calc(13); +// $topbar-link-hover-lightness: -30%; // Darken by 30% +// $topbar-link-bg-hover: darken($topbar-bg-color, 3%); +// $topbar-link-bg-active: darken($topbar-bg-color, 3%); + +// $topbar-dropdown-label-color: #555; +// $topbar-dropdown-label-text-transform: uppercase; +// $topbar-dropdown-label-font-weight: bold; +// $topbar-dropdown-label-font-size: em-calc(10); +// $topbar-dropdown-label-bg: lighten($topbar-bg-color, 5%); + +// Top menu icon styles + +// $topbar-menu-link-transform: uppercase; +// $topbar-menu-link-font-size: em-calc(13); +// $topbar-menu-link-weight: bold; +// $topbar-menu-link-color: #fff; +// $topbar-menu-icon-color: #fff; +// $topbar-menu-link-color-toggled: #888; +// $topbar-menu-icon-color-toggled: #888; + +// Transitions and breakpoint styles + +// $topbar-transition-speed: 300ms; +// $topbar-breakpoint: 940 !default; // Change to 9999px for always mobile layout +// $topbar-media-query: "only screen and (min-width: #{$topbar-breakpoint})"; + +// Divider Styles + +// $topbar-divider-border-bottom: solid 1px lighten($topbar-bg-color, 10%); +// $topbar-divider-border-top: solid 1px darken($topbar-bg-color, 10%); + +// Sticky Class + +// $topbar-sticky-class: ".sticky"; +// $topbar-arrows: true; //Set false to remove the triangle icon from the menu item diff --git a/app/assets/javascripts/foundation4/foundation/components/_alert-boxes.scss b/app/assets/javascripts/foundation4/foundation/components/_alert-boxes.scss new file mode 100755 index 0000000000..01c5360250 --- /dev/null +++ b/app/assets/javascripts/foundation4/foundation/components/_alert-boxes.scss @@ -0,0 +1,107 @@ +// +// Alert Box Variables +// +$include-html-alert-classes: $include-html-classes !default; + +// We use this to control alert padding. +$alert-padding-top: em-calc(11) !default; +$alert-padding-default-float: $alert-padding-top !default; +$alert-padding-opposite-direction: $alert-padding-top + em-calc(10) !default; +$alert-padding-bottom: $alert-padding-top + em-calc(1) !default; + +// We use these to control text style. +$alert-font-weight: bold !default; +$alert-font-size: em-calc(14) !default; +$alert-font-color: #fff !default; +$alert-font-color-alt: darken($secondary-color, 60%) !default; + +// We use this for close hover effect. +$alert-function-factor: 10% !default; + +// We use these to control border styles. +$alert-border-style: solid !default; +$alert-border-width: 1px !default; +$alert-border-color: darken($primary-color, $alert-function-factor) !default; +$alert-bottom-margin: em-calc(20) !default; + +// We use these to style the close buttons +$alert-close-color: #333 !default; +$alert-close-position: em-calc(5) !default; +$alert-close-font-size: em-calc(22) !default; +$alert-close-opacity: 0.3 !default; +$alert-close-opacity-hover: 0.5 !default; +$alert-close-padding: 5px 4px 4px !default; + +// We use this to control border radius +$alert-radius: $global-radius !default; + +// +// Alert Mixins +// + +// We use this mixin to create a default alert base. +@mixin alert-base { + border-style: $alert-border-style; + border-width: $alert-border-width; + display: block; + font-weight: $alert-font-weight; + margin-bottom: $alert-bottom-margin; + position: relative; + padding: $alert-padding-top $alert-padding-opposite-direction $alert-padding-bottom $alert-padding-default-float; + font-size: $alert-font-size; +} + +// We use this mixin to add alert styles +@mixin alert-style($bg:$primary-color) { + + // This find the lightness percentage of the background color. + $bg-lightness: lightness($bg); + + // We control which background color and border come through. + background-color: $bg; + border-color: darken($bg, $alert-function-factor); + + // We control the text color for you based on the background color. + @if $bg-lightness > 70% { color: $alert-font-color-alt; } + @else { color: $alert-font-color; } + +} + +// We use this to create the close button. +@mixin alert-close { + font-size: $alert-close-font-size; + padding: $alert-close-padding; + line-height: 0; + position: absolute; + top: $alert-close-position + em-calc(2); + #{$opposite-direction}: $alert-close-position; + color: $alert-close-color; + opacity: $alert-close-opacity; + &:hover, + &:focus { opacity: $alert-close-opacity-hover; } +} + +// We use this to quickly create alerts with a single mixin. +@mixin alert($bg:$primary-color, $radius:false) { + @include alert-base; + @include alert-style($bg); + @include radius($radius); +} + +@if $include-html-alert-classes != false { + + /* Foundation Alerts */ + .alert-box { + @include alert; + + .close { @include alert-close; } + + &.radius { @include radius($alert-radius); } + &.round { @include radius($global-rounded); } + + &.success { @include alert-style($success-color); } + &.alert { @include alert-style($alert-color); } + &.secondary { @include alert-style($secondary-color); } + } + +} diff --git a/app/assets/javascripts/foundation4/foundation/components/_block-grid.scss b/app/assets/javascripts/foundation4/foundation/components/_block-grid.scss new file mode 100755 index 0000000000..4b1474ab98 --- /dev/null +++ b/app/assets/javascripts/foundation4/foundation/components/_block-grid.scss @@ -0,0 +1,71 @@ +// +// Block Grid Variables +// +$include-html-grid-classes: $include-html-classes !default; + +// We use this to control the maximum number of block grid elements per row +$block-grid-elements: 12 !default; +$block-grid-default-spacing: em-calc(20) !default; + +// Enables media queries for block-grid classes. Set to false if writing semantic HTML. +$block-grid-media-queries: true !default; + +// +// Block Grid Mixins +// + +// We use this mixin to create different block-grids. You can apply per-row and spacing options. +// Setting $base-style to false will ommit default styles. +@mixin block-grid($per-row:false, $spacing:$block-grid-default-spacing, $base-style:true) { + + @if $base-style { + display: block; + padding: 0; + margin: 0 (-$spacing/2); + @include clearfix; + + &>li { + display: inline; + height: auto; + float: $default-float; + padding: 0 ($spacing/2) $spacing; + } + } + + @if $per-row { + &>li { + width: 100%/$per-row; + padding: 0 ($spacing/2) $spacing; + + &:nth-of-type(n) { clear: none; } + &:nth-of-type(#{$per-row}n+1) { clear: both; } + } + } + +} + +@if $include-html-grid-classes { + /* Foundation Block Grids for below small breakpoint */ + @media only screen { + [class*="block-grid-"] { @include block-grid; } + + @for $i from 1 through $block-grid-elements { + .small-block-grid-#{($i)} { + @include block-grid($i,$block-grid-default-spacing,false); + } + } + } + + /* Foundation Block Grids for above small breakpoint */ + @media #{$small} { + /* Remove small grid clearing */ + @for $i from 1 through $block-grid-elements { + .small-block-grid-#{($i)} > li:nth-of-type(#{$i}n+1) { clear: none; } + } + @for $i from 1 through $block-grid-elements { + .large-block-grid-#{($i)} { + @include block-grid($i,$block-grid-default-spacing,false); + } + } + } +} diff --git a/app/assets/javascripts/foundation4/foundation/components/_breadcrumbs.scss b/app/assets/javascripts/foundation4/foundation/components/_breadcrumbs.scss new file mode 100755 index 0000000000..24b42246a1 --- /dev/null +++ b/app/assets/javascripts/foundation4/foundation/components/_breadcrumbs.scss @@ -0,0 +1,124 @@ +// +// Breadcrumb Variables +// +$include-html-nav-classes: $include-html-classes !default; + +// We use this to set the background color for the breadcrumb container. +$crumb-bg: lighten($secondary-color, 5%) !default; + +// We use these to set the padding around the breadcrumbs. +$crumb-padding: em-calc(9 14 9) !default; +$crumb-side-padding: em-calc(12) !default; + +// We use these to control border styles. +$crumb-function-factor: 10% !default; +$crumb-border-size: 1px !default; +$crumb-border-style: solid !default; +$crumb-border-color: darken($crumb-bg, $crumb-function-factor) !default; +$crumb-radius: $global-radius !default; + +// We use these to set various text styles for breadcrumbs. +$crumb-font-size: em-calc(11) !default; +$crumb-font-color: $primary-color !default; +$crumb-font-color-current: #333 !default; +$crumb-font-color-unavailable: #999 !default; +$crumb-font-transform: uppercase !default; +$crumb-link-decor: underline !default; + +// We use these to control the slash between breadcrumbs +$crumb-slash-color: #aaa !default; +$crumb-slash: "/" !default; + +// +// Breakcrumb Mixins +// + +// We use this mixin to create a container around our breadcrumbs +@mixin crumb-container { + display: block; + padding: $crumb-padding; + overflow: hidden; + margin-#{$default-float}: 0; + list-style: none; + border-style: $crumb-border-style; + border-width: $crumb-border-size; + + // We control which background color and border come through. + background-color: $crumb-bg; + border-color: $crumb-border-color; +} + +// We use this mixin to create breadcrumb styles from list items. +@mixin crumbs { + + // A normal state will make the links look and act like clickable breadcrumbs. + margin: 0; + float: $default-float; + font-size: $crumb-font-size; + text-transform: $crumb-font-transform; + + &:hover a, &:focus a { text-decoration: $crumb-link-decor; } + + a, + span { + text-transform: $crumb-font-transform; + color: $crumb-font-color; + } + + // Current is for the link of the current page + &.current { + cursor: $cursor-default-value; + color: $crumb-font-color-current; + a { + cursor: $cursor-default-value; + color: $crumb-font-color-current; + } + + &:hover, &:hover a, + &:focus, &:focus a { text-decoration: none; } + } + + // Unavailable removed color and link styles so it looks inactive. + &.unavailable { + color: $crumb-font-color-unavailable; + a { color: $crumb-font-color-unavailable; } + + &:hover, + &:hover a, + &:focus, + a:focus { + text-decoration: none; + color: $crumb-font-color-unavailable; + cursor: $cursor-default-value; + } + } + + &:before { + content: "#{$crumb-slash}"; + color: $crumb-slash-color; + margin: 0 $crumb-side-padding; + position: relative; + top: 1px; + } + + &:first-child:before { + content: " "; + margin: 0; + } + +} + + +@if $include-html-nav-classes != false { + + /* Breadcrumbs */ + .breadcrumbs { + @include crumb-container; + @include radius($crumb-radius); + + &>* { + @include crumbs; + } + } + +} diff --git a/app/assets/javascripts/foundation4/foundation/components/_button-groups.scss b/app/assets/javascripts/foundation4/foundation/components/_button-groups.scss new file mode 100755 index 0000000000..d35e05ad9a --- /dev/null +++ b/app/assets/javascripts/foundation4/foundation/components/_button-groups.scss @@ -0,0 +1,89 @@ +// +// Button Group Variables +// +$include-html-button-classes: $include-html-classes !default; + +// Sets the margin for the right side by default, and the left margin if right-to-left direction is used +$button-bar-margin-opposite: em-calc(10) !default; + +// +// Button Group Mixins +// + +// We use this to add styles for a button group container +@mixin button-group-container($styles:true, $float:false) { + @if $styles { + list-style: none; + margin: 0; + @include clearfix(); + } + @if $float { + float: #{$default-float}; + margin-#{$opposite-direction}: $button-bar-margin-opposite; + & div { overflow: hidden; } + } +} + +// We use this to control styles for button groups +@mixin button-group-style($radius:false, $even:false, $float:$default-float) { + + // We use this to control the flow, or remove those styles completely. + @if $float { + margin: 0 0 0 (-$button-border-width); + float: $float; + // Make sure the first child doesn't get the negative margin. + &:first-child { margin-#{$default-float}: 0; } + } + + // We use these to control left and right radius on first/last buttons in the group. + @if $radius == true { + &:first-child, + &:first-child > a, + &:first-child > button, + &:first-child > .button { @include side-radius($default-float, $button-radius); } + &:last-child, + &:last-child > a, + &:last-child > button, + &:last-child > .button { @include side-radius($opposite-direction, $button-radius); } + } + @else if $radius { + &:first-child, + &:first-child > a, + &:first-child > button, + &:first-child > .button { @include side-radius($default-float, $radius); } + &:last-child, + &:last-child > a, + &:last-child > button, + &:last-child > .button { @include side-radius($opposite-direction, $radius); } + } + + // We use this to make the buttons even width across their container + @if $even { + width: percentage((100/$even) / 100); + button, .button { width: 100%; } + } + +} + +// Only include these CSS classes if $include-html-classes: true +@if $include-html-button-classes != false { + + /* Button Groups */ + .button-group { @include button-group-container; + + &> * { @include button-group-style(); } + + &.radius > * { @include button-group-style($radius:$button-radius, $float:null); } + &.round > * { @include button-group-style($radius:$button-round, $float:null); } + + @for $i from 2 through 8 { + &.even#{-$i} li { @include button-group-style($even:$i, $float:null); } + } + } + + .button-bar { + @include clearfix; + .button-group { @include button-group-container($styles:false,$float:true); } + } + +} diff --git a/app/assets/javascripts/foundation4/foundation/components/_buttons.scss b/app/assets/javascripts/foundation4/foundation/components/_buttons.scss new file mode 100755 index 0000000000..c5954ad821 --- /dev/null +++ b/app/assets/javascripts/foundation4/foundation/components/_buttons.scss @@ -0,0 +1,230 @@ +// +// Button Variables +// +$include-html-button-classes: $include-html-classes !default; + +// We use these to build padding for buttons. +$button-med: em-calc(12) !default; +$button-tny: em-calc(7) !default; +$button-sml: em-calc(9) !default; +$button-lrg: em-calc(16) !default; + +// We use this to control the display property. +$button-display: inline-block !default; +$button-margin-bottom: em-calc(20) !default; + +// We use these to control button text styles. +$button-font-family: inherit !default; +$button-font-color: #fff !default; +$button-font-color-alt: #333 !default; +$button-font-med: em-calc(16) !default; +$button-font-tny: em-calc(11) !default; +$button-font-sml: em-calc(13) !default; +$button-font-lrg: em-calc(20) !default; +$button-font-weight: bold !default; +$button-font-align: center !default; + +// We use these to control various hover effects. +$button-function-factor: 10% !default; + +// We use these to control button border styles. +$button-border-width: 1px !default; +$button-border-style: solid !default; + +// We use this to set the default radius used throughout the core. +$button-radius: $global-radius !default; +$button-round: $global-rounded !default; + +// We use this to set default opacity for disabled buttons. +$button-disabled-opacity: 0.6 !default; + + +// +// Button Mixins +// + +// We use this mixin to create a default button base. +@mixin button-base($style:true, $display:$button-display) { + @if $style { + border-style: $button-border-style; + border-width: $button-border-width; + cursor: $cursor-pointer-value; + font-family: $button-font-family; + font-weight: $button-font-weight; + line-height: normal; + margin: 0 0 $button-margin-bottom; + position: relative; + text-decoration: none; + text-align: $button-font-align; + } + @if $display { display: $display; } +} + +// We use this mixin to add button size styles +@mixin button-size($padding:$button-med, $full-width:false, $is-input:false) { + + // We control which padding styles come through, + // these can be turned off by setting $padding:false + @if $padding { + padding-top: $padding; + padding-#{$opposite-direction}: $padding * 2; + padding-bottom: $padding + em-calc(1); + padding-#{$default-float}: $padding * 2; + + // We control the font-size based on mixin input. + @if $padding == $button-med { font-size: $button-font-med; } + @else if $padding == $button-tny { font-size: $button-font-tny; } + @else if $padding == $button-sml { font-size: $button-font-sml; } + @else if $padding == $button-lrg { font-size: $button-font-lrg; } + @else { font-size: $padding - em-calc(2); } + } + + // We can set $full-width:true to remove side padding extend width. + @if $full-width { + // We still need to check if $padding is set. + @if $padding { + padding-top: $padding; + padding-bottom: $padding + em-calc(1); + } @else if $padding == false { + padding-top:0; + padding-bottom:0; + } + padding-right: 0; + padding-left: 0; + width: 100%; + } + + // 's and + +
+ + First Tab + + Alert me! + Second Tab, with alert callback and html heading! + + + {{item.content}} + + + + + + function TabsDemoCtrl($scope) { + $scope.items = [ + { title:"Dynamic Title 1", content:"Dynamic Item 0" }, + { title:"Dynamic Title 2", content:"Dynamic Item 1", disabled: true } + ]; + + $scope.alertMe = function() { + setTimeout(function() { + alert("You've selected the alert tab!"); + }); + }; + }; + + + */ + +/** + * @ngdoc directive + * @name mm.foundation.tabs.directive:tabHeading + * @restrict EA + * + * @description + * Creates an HTML heading for a {@link mm.foundation.tabs.directive:tab tab}. Must be placed as a child of a tab element. + * + * @example + + + + + HTML in my titles?! + And some content, too! + + + Icon heading?!? + That's right. + + + + + */ + +.directive('tab', ['$parse', function($parse) { + return { + require: '^tabset', + restrict: 'EA', + replace: true, + templateUrl: 'template/tabs/tab.html', + transclude: true, + scope: { + heading: '@', + // TODO: is this broken now? + onSelect: '&select', //This callback is called in contentHeadingTransclude + //once it inserts the tab's content into the dom + onDeselect: '&deselect' + }, + controller: function() { + //Empty controller so other directives can require being 'under' a tab + }, + compile: function(elm, attrs, transclude) { + return function postLink(scope, elm, attrs, tabsetCtrl) { + var getActive, setActive; + + // Here we parse the provided selectExpression + // This expression is executed when the tab is clicked/selected + // It is responsible for making appropriate state changes, such that getActive now returns appropriate values + // Fill in your logic here! + if (attrs.select) { + scope.selectExpression = $parse(attrs.select); + } + + // This expression is now the only thing controlling whether a tab is selected + // We no longer set scope.active/tab.active, except in response to changes to the result of this expression + if (attrs.active) { + getActive = $parse(attrs.active); + setActive = getActive.assign; + scope.$parent.$watch(getActive, function updateActive(value, oldVal) { + // Avoid re-initializing scope.active as it is already initialized + // below. (watcher is called async during init with value === + // oldVal) + if (value !== oldVal) { + scope.active = !!value; + } + }); + scope.active = getActive(scope.$parent); + } else { + setActive = getActive = angular.noop; + } + + // Commented out because: + // 1: two-way binding is limited to variables + // 2: We no longer toggle scope.active internal to this directive/plugin, so don't need to trigger responses + // 3: I'm now executing the callbacks inside scope.select() + + //scope.$watch('active', function(active) { + //// Note this watcher also initializes and assigns scope.active to the + //// attrs.active expression. + ////setActive(scope.$parent, active); + //}); + + scope.disabled = false; + if ( attrs.disabled ) { + scope.$parent.$watch($parse(attrs.disabled), function(value) { + scope.disabled = !! value; + }); + } + + scope.select = function() { + if ( ! scope.disabled ) { + scope.selectExpression(scope.$parent); + } + }; + + tabsetCtrl.addTab(scope); + scope.$on('$destroy', function() { + tabsetCtrl.removeTab(scope); + }); + + + //We need to transclude later, once the content container is ready. + //when this link happens, we're inside a tab heading. + scope.$transcludeFn = transclude; + }; + } + }; +}]) + +.directive('tabHeadingTransclude', [function() { + return { + restrict: 'A', + require: '^tab', + link: function(scope, elm, attrs, tabCtrl) { + scope.$watch('headingElement', function updateHeadingElement(heading) { + if (heading) { + elm.html(''); + elm.append(heading); + } + }); + } + }; +}]) + +.directive('tabContentTransclude', function() { + return { + restrict: 'A', + require: '^tabset', + link: function(scope, elm, attrs) { + var tab = scope.$eval(attrs.tabContentTransclude); + + //Now our tab is ready to be transcluded: both the tab heading area + //and the tab content area are loaded. Transclude 'em both. + tab.$transcludeFn(tab.$parent, function(contents) { + angular.forEach(contents, function(node) { + if (isTabHeading(node)) { + //Let tabHeadingTransclude know. + tab.headingElement = node; + } else { + elm.append(node); + } + }); + }); + } + }; + function isTabHeading(node) { + return node.tagName && ( + node.hasAttribute('tab-heading') || + node.hasAttribute('data-tab-heading') || + node.tagName.toLowerCase() === 'tab-heading' || + node.tagName.toLowerCase() === 'data-tab-heading' + ); + } +}) + +; + +angular.module( 'mm.foundation.tour', [ 'mm.foundation.position', 'mm.foundation.tooltip' ] ) + +.service( '$tour', [ '$window', function ( $window ) { + var currentIndex = getCurrentStep(); + var ended = false; + var steps = {}; + + function getCurrentStep() { + return parseInt( $window.localStorage.getItem( 'mm.tour.step' ), 10 ); + } + + function setCurrentStep(step) { + currentIndex = step; + $window.localStorage.setItem( 'mm.tour.step', step ); + } + + this.add = function ( index, attrs ) { + steps[ index ] = attrs; + }; + + this.has = function ( index ) { + return !!steps[ index ]; + }; + + this.isActive = function () { + return currentIndex > 0; + }; + + this.current = function ( index ) { + if ( index ) { + setCurrentStep( currentIndex ); + } else { + return currentIndex; + } + }; + + this.start = function () { + setCurrentStep( 1 ); + }; + + this.next = function () { + setCurrentStep( currentIndex + 1 ); + }; + + this.end = function () { + setCurrentStep( 0 ); + }; +}]) + +.directive( 'stepPopup', ['$tour', function ( $tour ) { + return { + restrict: 'EA', + replace: true, + scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&' }, + templateUrl: 'template/tour/tour.html', + link: function (scope, element) { + scope.isLastStep = function () { + return !$tour.has( $tour.current() + 1 ); + }; + + scope.endTour = function () { + element.remove(); + $tour.end(); + }; + + scope.nextStep = function () { + element.remove(); + $tour.next(); + }; + } + }; +}]) + +.directive( 'stepText', [ '$position', '$tooltip', '$tour', '$window', function ( $position, $tooltip, $tour, $window ) { + function isElementInViewport( element ) { + var rect = element[0].getBoundingClientRect(); + + return ( + rect.top >= 0 && + rect.left >= 0 && + rect.bottom <= ($window.innerHeight - 80) && + rect.right <= $window.innerWidth + ); + } + + function show( scope, element, attrs ) { + var index = parseInt( attrs.stepIndex, 10); + + if ( $tour.isActive() && index ) { + $tour.add( index, attrs ); + + if ( index === $tour.current() ) { + if ( !isElementInViewport( element ) ) { + var offset = $position.offset( element ); + $window.scrollTo( 0, offset.top - $window.innerHeight / 2 ); + } + + return true; + } + } + + return false; + } + + return $tooltip( 'step', 'step', show ); +}]); + +angular.module('mm.foundation.typeahead', ['mm.foundation.position', 'mm.foundation.bindHtml']) + +/** + * A helper service that can parse typeahead's syntax (string provided by users) + * Extracted to a separate service for ease of unit testing + */ + .factory('typeaheadParser', ['$parse', function ($parse) { + + // 00000111000000000000022200000000000000003333333333333330000000000044000 + var TYPEAHEAD_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+(.*)$/; + + return { + parse:function (input) { + + var match = input.match(TYPEAHEAD_REGEXP), modelMapper, viewMapper, source; + if (!match) { + throw new Error( + "Expected typeahead specification in form of '_modelValue_ (as _label_)? for _item_ in _collection_'" + + " but got '" + input + "'."); + } + + return { + itemName:match[3], + source:$parse(match[4]), + viewMapper:$parse(match[2] || match[1]), + modelMapper:$parse(match[1]) + }; + } + }; +}]) + + .directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$position', 'typeaheadParser', + function ($compile, $parse, $q, $timeout, $document, $position, typeaheadParser) { + + var HOT_KEYS = [9, 13, 27, 38, 40]; + + return { + require:'ngModel', + link:function (originalScope, element, attrs, modelCtrl) { + + //SUPPORTED ATTRIBUTES (OPTIONS) + + //minimal no of characters that needs to be entered before typeahead kicks-in + var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1; + + //minimal wait time after last character typed before typehead kicks-in + var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0; + + //should it restrict model values to the ones selected from the popup only? + var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false; + + //binding to a variable that indicates if matches are being retrieved asynchronously + var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop; + + //a callback executed when a match is selected + var onSelectCallback = $parse(attrs.typeaheadOnSelect); + + var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined; + + var appendToBody = attrs.typeaheadAppendToBody ? $parse(attrs.typeaheadAppendToBody) : false; + + //INTERNAL VARIABLES + + //model setter executed upon match selection + var $setModelValue = $parse(attrs.ngModel).assign; + + //expressions used by typeahead + var parserResult = typeaheadParser.parse(attrs.typeahead); + + var hasFocus; + + //pop-up element used to display matches + var popUpEl = angular.element('
'); + popUpEl.attr({ + matches: 'matches', + active: 'activeIdx', + select: 'select(activeIdx)', + query: 'query', + position: 'position' + }); + //custom item template + if (angular.isDefined(attrs.typeaheadTemplateUrl)) { + popUpEl.attr('template-url', attrs.typeaheadTemplateUrl); + } + + //create a child scope for the typeahead directive so we are not polluting original scope + //with typeahead-specific data (matches, query etc.) + var scope = originalScope.$new(); + originalScope.$on('$destroy', function(){ + scope.$destroy(); + }); + + var resetMatches = function() { + scope.matches = []; + scope.activeIdx = -1; + }; + + var getMatchesAsync = function(inputValue) { + + var locals = {$viewValue: inputValue}; + isLoadingSetter(originalScope, true); + $q.when(parserResult.source(originalScope, locals)).then(function(matches) { + + //it might happen that several async queries were in progress if a user were typing fast + //but we are interested only in responses that correspond to the current view value + if (inputValue === modelCtrl.$viewValue && hasFocus) { + if (matches.length > 0) { + + scope.activeIdx = 0; + scope.matches.length = 0; + + //transform labels + for(var i=0; i= minSearch) { + if (waitTime > 0) { + if (timeoutPromise) { + $timeout.cancel(timeoutPromise);//cancel previous timeout + } + timeoutPromise = $timeout(function () { + getMatchesAsync(inputValue); + }, waitTime); + } else { + getMatchesAsync(inputValue); + } + } else { + isLoadingSetter(originalScope, false); + resetMatches(); + } + + if (isEditable) { + return inputValue; + } else { + if (!inputValue) { + // Reset in case user had typed something previously. + modelCtrl.$setValidity('editable', true); + return inputValue; + } else { + modelCtrl.$setValidity('editable', false); + return undefined; + } + } + }); + + modelCtrl.$formatters.push(function (modelValue) { + + var candidateViewValue, emptyViewValue; + var locals = {}; + + if (inputFormatter) { + + locals['$model'] = modelValue; + return inputFormatter(originalScope, locals); + + } else { + + //it might happen that we don't have enough info to properly render input value + //we need to check for this situation and simply return model value if we can't apply custom formatting + locals[parserResult.itemName] = modelValue; + candidateViewValue = parserResult.viewMapper(originalScope, locals); + locals[parserResult.itemName] = undefined; + emptyViewValue = parserResult.viewMapper(originalScope, locals); + + return candidateViewValue!== emptyViewValue ? candidateViewValue : modelValue; + } + }); + + scope.select = function (activeIdx) { + //called from within the $digest() cycle + var locals = {}; + var model, item; + + locals[parserResult.itemName] = item = scope.matches[activeIdx].model; + model = parserResult.modelMapper(originalScope, locals); + $setModelValue(originalScope, model); + modelCtrl.$setValidity('editable', true); + + onSelectCallback(originalScope, { + $item: item, + $model: model, + $label: parserResult.viewMapper(originalScope, locals) + }); + + resetMatches(); + + //return focus to the input element if a mach was selected via a mouse click event + element[0].focus(); + }; + + //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27) + element.bind('keydown', function (evt) { + + //typeahead is open and an "interesting" key was pressed + if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) { + return; + } + + evt.preventDefault(); + + if (evt.which === 40) { + scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length; + scope.$digest(); + + } else if (evt.which === 38) { + scope.activeIdx = (scope.activeIdx ? scope.activeIdx : scope.matches.length) - 1; + scope.$digest(); + + } else if (evt.which === 13 || evt.which === 9) { + scope.$apply(function () { + scope.select(scope.activeIdx); + }); + + } else if (evt.which === 27) { + evt.stopPropagation(); + + resetMatches(); + scope.$digest(); + } + }); + + element.bind('blur', function (evt) { + hasFocus = false; + }); + + // Keep reference to click handler to unbind it. + var dismissClickHandler = function (evt) { + if (element[0] !== evt.target) { + resetMatches(); + scope.$digest(); + } + }; + + $document.bind('click', dismissClickHandler); + + originalScope.$on('$destroy', function(){ + $document.unbind('click', dismissClickHandler); + }); + + var $popup = $compile(popUpEl)(scope); + if ( appendToBody ) { + $document.find('body').append($popup); + } else { + element.after($popup); + } + } + }; + +}]) + + .directive('typeaheadPopup', function () { + return { + restrict:'EA', + scope:{ + matches:'=', + query:'=', + active:'=', + position:'=', + select:'&' + }, + replace:true, + templateUrl:'template/typeahead/typeahead-popup.html', + link:function (scope, element, attrs) { + + scope.templateUrl = attrs.templateUrl; + + scope.isOpen = function () { + return scope.matches.length > 0; + }; + + scope.isActive = function (matchIdx) { + return scope.active == matchIdx; + }; + + scope.selectActive = function (matchIdx) { + scope.active = matchIdx; + }; + + scope.selectMatch = function (activeIdx) { + scope.select({activeIdx:activeIdx}); + }; + } + }; + }) + + .directive('typeaheadMatch', ['$http', '$templateCache', '$compile', '$parse', function ($http, $templateCache, $compile, $parse) { + return { + restrict:'EA', + scope:{ + index:'=', + match:'=', + query:'=' + }, + link:function (scope, element, attrs) { + var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'template/typeahead/typeahead-match.html'; + $http.get(tplUrl, {cache: $templateCache}).success(function(tplContent){ + element.replaceWith($compile(tplContent.trim())(scope)); + }); + } + }; + }]) + + .filter('typeaheadHighlight', function() { + + function escapeRegexp(queryToEscape) { + return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1"); + } + + return function(matchItem, query) { + return query ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '$&') : matchItem; + }; + }); + +angular.module("template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/accordion/accordion-group.html", + "
\n" + + " {{heading}}\n" + + "
\n" + + "
\n" + + ""); +}]); + +angular.module("template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/accordion/accordion.html", + "
\n" + + ""); +}]); + +angular.module("template/alert/alert.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/alert/alert.html", + "
\n" + + " \n" + + " ×\n" + + "
\n" + + ""); +}]); + +angular.module("template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/modal/backdrop.html", + "
\n" + + ""); +}]); + +angular.module("template/modal/window.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/modal/window.html", + "
\n" + + "
\n" + + "
\n" + + ""); +}]); + +angular.module("template/pagination/pager.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/pagination/pager.html", + "\n" + + ""); +}]); + +angular.module("template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/pagination/pagination.html", + "\n" + + ""); +}]); + +angular.module("template/tooltip/tooltip-html-unsafe-popup.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/tooltip/tooltip-html-unsafe-popup.html", + "\n" + + " \n" + + " \n" + + "\n" + + ""); +}]); + +angular.module("template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/tooltip/tooltip-popup.html", + "\n" + + " \n" + + " \n" + + "\n" + + ""); +}]); + +angular.module("template/popover/popover.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/popover/popover.html", + "
\n" + + " \n" + + "
\n" + + "

\n" + + "

\n" + + "
\n" + + "
\n" + + ""); +}]); + +angular.module("template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/progressbar/bar.html", + "\n" + + ""); +}]); + +angular.module("template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/progressbar/progress.html", + "
\n" + + ""); +}]); + +angular.module("template/progressbar/progressbar.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/progressbar/progressbar.html", + "
\n" + + " \n" + + "
\n" + + ""); +}]); + +angular.module("template/rating/rating.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/rating/rating.html", + "\n" + + " \n" + + "\n" + + ""); +}]); + +angular.module("template/tabs/tab.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/tabs/tab.html", + "
\n" + + " {{heading}}\n" + + "
\n" + + ""); +}]); + +angular.module("template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/tabs/tabset.html", + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + "
\n" + + ""); +}]); + +angular.module("template/tour/tour.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/tour/tour.html", + "
\n" + + " \n" + + "
\n" + + "

\n" + + "

\n" + + " Next\n" + + " End\n" + + " ×\n" + + "
\n" + + "
\n" + + ""); +}]); + +angular.module("template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/typeahead/typeahead-match.html", + ""); +}]); + +angular.module("template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) { + $templateCache.put("template/typeahead/typeahead-popup.html", + "
    \n" + + "
  • \n" + + "
    \n" + + "
  • \n" + + "
\n" + + ""); +}]); diff --git a/app/assets/stylesheets/darkswarm/header.css.sass b/app/assets/stylesheets/darkswarm/header.css.sass index a33fa1b30d..5d1764b8e1 100644 --- a/app/assets/stylesheets/darkswarm/header.css.sass +++ b/app/assets/stylesheets/darkswarm/header.css.sass @@ -1,3 +1,6 @@ /*body { background: #ff0000; }*/ nav.top-bar margin-bottom: 0px + +body > section[role='main'] + padding: 0px diff --git a/app/assets/stylesheets/darkswarm/shop.css.sass b/app/assets/stylesheets/darkswarm/shop.css.sass index 58776a6fd5..e8a545b679 100644 --- a/app/assets/stylesheets/darkswarm/shop.css.sass +++ b/app/assets/stylesheets/darkswarm/shop.css.sass @@ -58,7 +58,7 @@ product padding-right: 14px @media all and (max-width: 768px) font-size: 1.2em - .custom.dropdown + select width: 280px display: inline-block background: transparent @@ -66,6 +66,7 @@ product border-color: #666666 font-size: 1em margin-bottom: 0 + padding: 8px 0px 8px 12px @media all and (max-width: 768px) font-size: 1.2em width: 180px @@ -74,41 +75,6 @@ product display: block padding-top: 14px - tabs - background: url("/assets/matte.png") top left repeat - display: block - .section-container.auto - margin-bottom: 0 - & > section - transition:height 0s linear 0.5s - & > .content - background: none - border: none - img - margin: 0px 0px 0px 40px - p - max-width: 555px - @media all and (max-width: 768px) - height: auto !important - ul - list-style-type: none - padding-left: none - .panel - padding-bottom: 1.25em - - & > .title, &.active > .title - text-transform: uppercase - line-height: 50px - @media all and (max-width: 768px) - line-height: 30px !important - border: none - &, &:hover - background: none - a - padding: 0px 2.2em - @media all and (max-width: 768px) - line-height: inherit !important - products display: block padding-top: 2.3em diff --git a/app/assets/stylesheets/darkswarm/sidebar.css.sass b/app/assets/stylesheets/darkswarm/sidebar.css.sass new file mode 100644 index 0000000000..47a3d8f3c1 --- /dev/null +++ b/app/assets/stylesheets/darkswarm/sidebar.css.sass @@ -0,0 +1,22 @@ +// OMG +// We can't import foundation components? +// See https://github.com/zurb/foundation/issues/3855#issuecomment-30372252 + +@import "variables" +@import "components/global" +@import "components/buttons" +@import "components/panels" + + +#sidebar + margin-top: 1.875em + $bg: #222 + $padding: emCalc(20) + $adjust: true + $adjust: true + @include panel($bg, $padding, $adjust) + .content + background: white + + .tabs dd a + padding: 0.5em 1em diff --git a/app/assets/stylesheets/darkswarm/sidepanel.css.sass b/app/assets/stylesheets/darkswarm/sidepanel.css.sass deleted file mode 100644 index 65b323a2e9..0000000000 --- a/app/assets/stylesheets/darkswarm/sidepanel.css.sass +++ /dev/null @@ -1,13 +0,0 @@ -@import "foundation/variables" -@import "foundation/components/global" -@import "foundation/components/buttons" -@import "foundation/components/panels" - - -#sidebar - margin-top: 1.875em - .login-panel - $bg: #222 - $padding: emCalc(20) - $adjust: true - @include panel($bg, $padding, $adjust) diff --git a/app/assets/stylesheets/darkswarm/tabs.css.sass b/app/assets/stylesheets/darkswarm/tabs.css.sass new file mode 100644 index 0000000000..dda03648d1 --- /dev/null +++ b/app/assets/stylesheets/darkswarm/tabs.css.sass @@ -0,0 +1,37 @@ +@import "darkswarm/typography" + +#tabs + background: url("/assets/matte.png") top left repeat + display: block + dl dd a + @include avenir + background: transparent + text-transform: uppercase + line-height: 50px + font-size: 0.875em + @media all and (max-width: 768px) + line-height: 30px !important + border: none + &, &:hover + background: none + padding: 0px 2.2em + @media all and (max-width: 768px) + line-height: inherit !important + + .tabs-content + margin-bottom: 0 + & > .content + background: none + border: none + img + margin: 0px 0px 0px 40px + p + max-width: 555px + @media all and (max-width: 768px) + height: auto !important + ul + list-style-type: none + padding-left: none + .panel + padding-bottom: 1.25em + diff --git a/app/assets/stylesheets/darkswarm/typography.css.sass b/app/assets/stylesheets/darkswarm/typography.css.sass index f4d514b4e4..35ab5a8db7 100644 --- a/app/assets/stylesheets/darkswarm/typography.css.sass +++ b/app/assets/stylesheets/darkswarm/typography.css.sass @@ -22,9 +22,12 @@ a &:hover text-decoration: underline -h1, h2, h3, h4, h5, h6, .avenir +@mixin avenir color: #333333 font-family: "AvenirBla_IE", "AvenirBla" + +h1, h2, h3, h4, h5, h6, .avenir + @include avenir padding: 0px diff --git a/app/assets/stylesheets/search/auto_complete.css.scss b/app/assets/stylesheets/search/auto_complete.css.scss index 844b1649bf..52ff03a21b 100644 --- a/app/assets/stylesheets/search/auto_complete.css.scss +++ b/app/assets/stylesheets/search/auto_complete.css.scss @@ -1,5 +1,5 @@ -@import "foundation/variables"; -@import "foundation/components/global"; +@import "foundation4/foundation/variables"; +@import "foundation4/foundation/components/global"; ul.ui-autocomplete { position: absolute; @@ -31,4 +31,4 @@ ul.ui-autocomplete { background-color: #FFFCB2; } } -} \ No newline at end of file +} diff --git a/app/assets/stylesheets/search/enteprise_search.css.scss b/app/assets/stylesheets/search/enteprise_search.css.scss index d5ab9be68c..5edf4bff6c 100644 --- a/app/assets/stylesheets/search/enteprise_search.css.scss +++ b/app/assets/stylesheets/search/enteprise_search.css.scss @@ -1,5 +1,5 @@ -@import "foundation/variables"; -@import "foundation/components/global"; +@import "foundation4/foundation/variables"; +@import "foundation4/foundation/components/global"; .search-result { min-height: 3em; @@ -13,4 +13,4 @@ .with-separator { border-bottom: 1px solid #E0E0E0; -} \ No newline at end of file +} diff --git a/app/assets/stylesheets/search/foundation_and_overrides.scss b/app/assets/stylesheets/search/foundation_and_overrides.scss index a34a8b6570..101aec7299 100644 --- a/app/assets/stylesheets/search/foundation_and_overrides.scss +++ b/app/assets/stylesheets/search/foundation_and_overrides.scss @@ -380,6 +380,7 @@ $default-float: left; // $button-radius: $global-radius; // $button-round: $global-rounded; + // We use this to set default opacity for disabled buttons. // $button-disabled-opacity: 0.6; @@ -1301,7 +1302,7 @@ $topbar-link-font-size: emCalc(16); // $topbar-sticky-class: ".sticky"; -@import 'foundation'; +@import 'foundation4/foundation'; textarea { min-height: emCalc(120) !important; diff --git a/app/assets/stylesheets/search/home.css.scss b/app/assets/stylesheets/search/home.css.scss index b523bf17b9..7580aa69f0 100644 --- a/app/assets/stylesheets/search/home.css.scss +++ b/app/assets/stylesheets/search/home.css.scss @@ -2,10 +2,10 @@ // They will automatically be included in application.css. // You can use Sass (SCSS) here: http://sass-lang.com/ -@import "foundation/variables"; -@import "foundation/components/global"; -@import "foundation/components/buttons"; -@import "foundation/components/panels"; +@import "foundation4/foundation/variables"; +@import "foundation4/foundation/components/global"; +@import "foundation4/foundation/components/buttons"; +@import "foundation4/foundation/components/panels"; #postcode_select_box { text-align: center; diff --git a/app/assets/stylesheets/search/products.css.scss b/app/assets/stylesheets/search/products.css.scss index 117a70dcde..7deb5c73ae 100644 --- a/app/assets/stylesheets/search/products.css.scss +++ b/app/assets/stylesheets/search/products.css.scss @@ -1,8 +1,8 @@ -@import "foundation/variables"; -@import "foundation/components/global"; +@import "foundation4/foundation/variables"; +@import "foundation4/foundation/components/global"; .products { margin: 0, emCalc(20); border: 1px solid black; padding: emCalc(10); -} \ No newline at end of file +} diff --git a/app/assets/stylesheets/search/temp_landing_page.css.scss b/app/assets/stylesheets/search/temp_landing_page.css.scss index 89c5ffd6bb..566b1d5e9f 100644 --- a/app/assets/stylesheets/search/temp_landing_page.css.scss +++ b/app/assets/stylesheets/search/temp_landing_page.css.scss @@ -1,5 +1,5 @@ -@import "foundation/variables"; -@import "foundation/components/global"; +@import "foundation4/foundation/variables"; +@import "foundation4/foundation/components/global"; .landing-page-row { padding-top: emCalc(40); diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 4b0cc37e9f..d2f58096e8 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -5,6 +5,7 @@ class ApplicationController < ActionController::Base before_filter :load_data_for_sidebar before_filter :require_certified_hostname + include EnterprisesHelper def after_sign_in_path_for(resource) if request.referer and referer_path = URI(request.referer).path @@ -44,6 +45,12 @@ class ApplicationController < ActionController::Base end end + def require_order_cycle + unless current_order_cycle + redirect_to main_app.shop_path + end + end + def check_order_cycle_expiry if current_order_cycle.andand.closed? session[:expired_order_cycle_id] = current_order_cycle.id diff --git a/app/controllers/shop/checkout_controller.rb b/app/controllers/shop/checkout_controller.rb index 5a193e392d..2e817150f5 100644 --- a/app/controllers/shop/checkout_controller.rb +++ b/app/controllers/shop/checkout_controller.rb @@ -1,6 +1,5 @@ class Shop::CheckoutController < Spree::CheckoutController layout 'darkswarm' - prepend_before_filter :require_order_cycle prepend_before_filter :require_distributor_chosen skip_before_filter :check_registration @@ -62,12 +61,6 @@ class Shop::CheckoutController < Spree::CheckoutController redirect_to main_app.root_path end end - - def require_order_cycle - unless current_order_cycle - redirect_to main_app.shop_path - end - end def load_order @order = current_order diff --git a/app/controllers/spree/orders_controller_decorator.rb b/app/controllers/spree/orders_controller_decorator.rb index 92211bf6a6..65d1908723 100644 --- a/app/controllers/spree/orders_controller_decorator.rb +++ b/app/controllers/spree/orders_controller_decorator.rb @@ -5,6 +5,9 @@ Spree::OrdersController.class_eval do before_filter :update_distribution, :only => :update before_filter :filter_order_params, :only => :update + prepend_before_filter :require_order_cycle, only: [:edit] + prepend_before_filter :require_distributor_chosen, only: [:edit] + layout 'darkswarm' # Patch Orders#populate to populate multi_cart (if enabled) diff --git a/app/controllers/user_passwords_controller.rb b/app/controllers/user_passwords_controller.rb new file mode 100644 index 0000000000..1a357a6c37 --- /dev/null +++ b/app/controllers/user_passwords_controller.rb @@ -0,0 +1,3 @@ +class UserPasswordsController < Spree::UserPasswordsController + +end diff --git a/app/controllers/user_registrations_controller.rb b/app/controllers/user_registrations_controller.rb new file mode 100644 index 0000000000..3d106bbc96 --- /dev/null +++ b/app/controllers/user_registrations_controller.rb @@ -0,0 +1,32 @@ +class UserRegistrationsController < Spree::UserRegistrationsController + + # POST /resource/sign_up + def create + @user = build_resource(params[:spree_user]) + if resource.save + set_flash_message(:notice, :signed_up) + sign_in(:spree_user, @user) + session[:spree_user_signup] = true + associate_user + + respond_to do |format| + format.html do + sign_in_and_redirect(:spree_user, @user) + end + format.js do + render json: { email: @user.email } + end + end + else + clean_up_passwords(resource) + respond_to do |format| + format.html do + render :new + end + format.js do + render json: @user.errors, status: :unauthorized + end + end + end + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 7afe5e180b..15c20dfd55 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,6 +1,7 @@ module ApplicationHelper include FoundationRailsHelper::FlashHelper + def home_page_cms_content if controller.controller_name == 'home' && controller.action_name == 'index' cms_page_content(:content, Cms::Page.find_by_full_path('/')) diff --git a/app/helpers/shared_helper.rb b/app/helpers/shared_helper.rb index 8ddad2524f..5082d3dddb 100644 --- a/app/helpers/shared_helper.rb +++ b/app/helpers/shared_helper.rb @@ -11,7 +11,11 @@ module SharedHelper # all suppliers of current distributor's products def current_producers - Exchange.where(receiver_id: current_distributor.id).map{ |ex| ex.variants.map {|v| v.product.supplier }}.flatten.uniq + if current_distributor && current_order_cycle + variants = current_order_cycle.variants_distributed_by(current_distributor) + Enterprise.supplying_variant_in(variants) + else + [] + end end end - diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index 501f18f4a7..ca31f5bc8c 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -32,6 +32,7 @@ class Enterprise < ActiveRecord::Base scope :by_name, order('name') scope :is_primary_producer, where(:is_primary_producer => true) scope :is_distributor, where(:is_distributor => true) + scope :supplying_variant_in, lambda { |variants| joins(:supplied_products => :variants_including_master).where('spree_variants.id IN (?)', variants).select('DISTINCT enterprises.*') } scope :with_supplied_active_products_on_hand, lambda { joins(:supplied_products) .where('spree_products.deleted_at IS NULL AND spree_products.available_on <= ? AND spree_products.count_on_hand > 0', Time.now) @@ -47,7 +48,7 @@ class Enterprise < ActiveRecord::Base joins('LEFT OUTER JOIN product_distributions ON product_distributions.distributor_id = enterprises.id'). joins('LEFT OUTER JOIN spree_products ON spree_products.id = product_distributions.product_id') scope :with_order_cycles_outer, - joins('LEFT OUTER JOIN exchanges ON (exchanges.receiver_id = enterprises.id)'). + joins("LEFT OUTER JOIN exchanges ON (exchanges.receiver_id = enterprises.id AND exchanges.incoming = 'f')"). joins('LEFT OUTER JOIN order_cycles ON (order_cycles.id = exchanges.order_cycle_id)') scope :with_order_cycles_and_exchange_variants_outer, diff --git a/app/models/exchange.rb b/app/models/exchange.rb index 66a5894c1e..243a311bcc 100644 --- a/app/models/exchange.rb +++ b/app/models/exchange.rb @@ -15,14 +15,19 @@ class Exchange < ActiveRecord::Base accepts_nested_attributes_for :variants - scope :incoming, joins(:order_cycle).where('exchanges.receiver_id = order_cycles.coordinator_id') - scope :outgoing, joins(:order_cycle).where('exchanges.sender_id = order_cycles.coordinator_id') + scope :in_order_cycle, lambda { |order_cycle| where(order_cycle_id: order_cycle) } + scope :incoming, where(incoming: true) + scope :outgoing, where(incoming: false) + scope :from_enterprise, lambda { |enterprise| where(sender_id: enterprise) } + scope :to_enterprise, lambda { |enterprise| where(receiver_id: enterprise) } scope :from_enterprises, lambda { |enterprises| where('exchanges.sender_id IN (?)', enterprises) } scope :to_enterprises, lambda { |enterprises| where('exchanges.receiver_id IN (?)', enterprises) } + scope :supplying_to, lambda { |distributor| where('exchanges.incoming OR exchanges.receiver_id = ?', distributor) } scope :with_variant, lambda { |variant| joins(:exchange_variants).where('exchange_variants.variant_id = ?', variant) } scope :with_any_variant, lambda { |variants| joins(:exchange_variants).where('exchange_variants.variant_id IN (?)', variants).select('DISTINCT exchanges.*') } scope :with_product, lambda { |product| joins(:exchange_variants).where('exchange_variants.variant_id IN (?)', product.variants_including_master) } + def clone!(new_order_cycle) exchange = self.dup exchange.order_cycle = new_order_cycle @@ -32,17 +37,13 @@ class Exchange < ActiveRecord::Base exchange end - def incoming? - receiver == order_cycle.coordinator - end - def role incoming? ? 'supplier' : 'distributor' end - def to_h(core=false) + def to_h(core_only=false) h = attributes.merge({ 'variant_ids' => variant_ids.sort, 'enterprise_fee_ids' => enterprise_fee_ids.sort }) - h.reject! { |k| %w(id order_cycle_id created_at updated_at).include? k } if core + h.reject! { |k| %w(id order_cycle_id created_at updated_at).include? k } if core_only h end @@ -54,5 +55,4 @@ class Exchange < ActiveRecord::Base end end - end diff --git a/app/models/order_cycle.rb b/app/models/order_cycle.rb index a6707d9220..d98b565239 100644 --- a/app/models/order_cycle.rb +++ b/app/models/order_cycle.rb @@ -20,20 +20,20 @@ class OrderCycle < ActiveRecord::Base scope :closed, lambda { where('order_cycles.orders_close_at < ?', Time.now) } scope :undated, where(orders_open_at: nil, orders_close_at: nil) - scope :distributing_product, lambda { |product| - joins(:exchanges => :variants). - merge(Exchange.outgoing). - where('spree_variants.id IN (?)', product.variants_including_master.pluck(:id)). - select('DISTINCT order_cycles.*') } - - scope :with_distributor, lambda { |distributor| - joins(:exchanges).merge(Exchange.outgoing).where('exchanges.receiver_id = ?', distributor) - } - scope :soonest_closing, lambda { active.order('order_cycles.orders_close_at ASC') } scope :most_recently_closed, lambda { closed.order('order_cycles.orders_close_at DESC') } scope :soonest_opening, lambda { upcoming.order('order_cycles.orders_open_at ASC') } + scope :distributing_product, lambda { |product| + joins(:exchanges). + merge(Exchange.outgoing). + merge(Exchange.with_product(product)). + select('DISTINCT order_cycles.*') } + + scope :with_distributor, lambda { |distributor| + joins(:exchanges).merge(Exchange.outgoing).merge(Exchange.to_enterprise(distributor)) + } + scope :managed_by, lambda { |user| if user.has_spree_role?('admin') @@ -43,7 +43,7 @@ class OrderCycle < ActiveRecord::Base end } - # Order cycles that user coordinates, sends to or receives from + # Return order cycles that user coordinates, sends to or receives from scope :accessible_by, lambda { |user| if user.has_spree_role?('admin') scoped @@ -59,12 +59,13 @@ class OrderCycle < ActiveRecord::Base joins('LEFT OUTER JOIN enterprises ON (enterprises.id = exchanges.sender_id OR enterprises.id = exchanges.receiver_id)') } + def self.first_opening_for(distributor) with_distributor(distributor).soonest_opening.first end def self.most_recently_closed_for(distributor) - OrderCycle.with_distributor(distributor).most_recently_closed.first + with_distributor(distributor).most_recently_closed.first end def clone! @@ -78,11 +79,11 @@ class OrderCycle < ActiveRecord::Base end def suppliers - self.exchanges.where(:receiver_id => self.coordinator).map(&:sender).uniq + self.exchanges.incoming.map(&:sender).uniq end def distributors - self.exchanges.where(:sender_id => self.coordinator).map(&:receiver).uniq + self.exchanges.outgoing.map(&:receiver).uniq end def variants @@ -90,15 +91,15 @@ class OrderCycle < ActiveRecord::Base end def distributed_variants - self.exchanges.where(:sender_id => self.coordinator).map(&:variants).flatten.uniq + self.exchanges.outgoing.map(&:variants).flatten.uniq end def variants_distributed_by(distributor) Spree::Variant. joins(:exchanges). + merge(Exchange.in_order_cycle(self)). merge(Exchange.outgoing). - where('exchanges.order_cycle_id = ?', self). - where('exchanges.receiver_id = ?', distributor) + merge(Exchange.to_enterprise(distributor)) end def products_distributed_by(distributor) @@ -239,11 +240,10 @@ class OrderCycle < ActiveRecord::Base end def exchanges_carrying(variant, distributor) - exchanges.to_enterprises([coordinator, distributor]).with_variant(variant) + exchanges.supplying_to(distributor).with_variant(variant) end def exchanges_supplying(order) - variants = order.line_items.map(&:variant) - exchanges.to_enterprises([coordinator, order.distributor]).with_any_variant(variants) + exchanges.supplying_to(order.distributor).with_any_variant(order.variants) end end diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb index 7f84913d68..282a93cfb5 100644 --- a/app/models/spree/product_decorator.rb +++ b/app/models/spree/product_decorator.rb @@ -35,10 +35,8 @@ Spree::Product.class_eval do joins('LEFT OUTER JOIN exchanges AS o_exchanges ON (o_exchanges.id = o_exchange_variants.exchange_id)'). joins('LEFT OUTER JOIN order_cycles AS o_order_cycles ON (o_order_cycles.id = o_exchanges.order_cycle_id)') - scope :with_order_cycles_inner, joins('INNER JOIN spree_variants ON (spree_variants.product_id = spree_products.id)'). - joins('INNER JOIN exchange_variants ON (exchange_variants.variant_id = spree_variants.id)'). - joins('INNER JOIN exchanges ON (exchanges.id = exchange_variants.exchange_id)'). - joins('INNER JOIN order_cycles ON (order_cycles.id = exchanges.order_cycle_id)') + scope :with_order_cycles_inner, joins(:variants_including_master => {:exchanges => :order_cycle}) + # -- Scopes scope :in_supplier, lambda { |supplier| where(:supplier_id => supplier) } @@ -52,7 +50,7 @@ Spree::Product.class_eval do distributor = distributor.respond_to?(:id) ? distributor.id : distributor.to_i with_product_distributions_outer.with_order_cycles_outer. - where('product_distributions.distributor_id = ? OR (o_exchanges.sender_id = o_order_cycles.coordinator_id AND o_exchanges.receiver_id = ?)', distributor, distributor). + where('product_distributions.distributor_id = ? OR (o_exchanges.incoming = ? AND o_exchanges.receiver_id = ?)', distributor, false, distributor). select('distinct spree_products.*') } @@ -69,13 +67,13 @@ Spree::Product.class_eval do enterprise = enterprise.respond_to?(:id) ? enterprise.id : enterprise.to_i with_product_distributions_outer.with_order_cycles_outer. - where('spree_products.supplier_id = ? OR product_distributions.distributor_id = ? OR (o_exchanges.sender_id = o_order_cycles.coordinator_id AND o_exchanges.receiver_id = ?)', enterprise, enterprise, enterprise). + where('spree_products.supplier_id = ? OR product_distributions.distributor_id = ? OR (o_exchanges.incoming = ? AND o_exchanges.receiver_id = ?)', enterprise, enterprise, false, enterprise). select('distinct spree_products.*') } # Find products that are distributed by the given order cycle scope :in_order_cycle, lambda { |order_cycle| with_order_cycles_inner. - where('exchanges.sender_id = order_cycles.coordinator_id'). + merge(Exchange.outgoing). where('order_cycles.id = ?', order_cycle) } scope :managed_by, lambda { |user| if user.has_spree_role?('admin') diff --git a/app/views/admin/order_cycles/show.rep b/app/views/admin/order_cycles/show.rep index fbf297822f..e5ccd66226 100644 --- a/app/views/admin/order_cycles/show.rep +++ b/app/views/admin/order_cycles/show.rep @@ -13,6 +13,7 @@ r.element :order_cycle, @order_cycle do r.element :id r.element :sender_id r.element :receiver_id + r.element :incoming r.element :variants, Hash[ exchange.variants.map { |v| [v.id, true] } ], {} diff --git a/app/views/layouts/darkswarm.html.haml b/app/views/layouts/darkswarm.html.haml index 5ceb406a58..048e8a9dc1 100644 --- a/app/views/layouts/darkswarm.html.haml +++ b/app/views/layouts/darkswarm.html.haml @@ -16,9 +16,7 @@ = render partial: "shared/menu" = display_flash_messages - %section#sidebar{ role: "complementary", "ng-controller" => "SidebarCtrl", "ng-class" => "active()"} - = render partial: "shared/login_panel" - = yield :sidebar + = render "shared/sidebar" %section{ role: "main" } = yield @@ -27,4 +25,3 @@ = yield :scripts - diff --git a/app/views/shared/_forgot_sidebar.html.haml b/app/views/shared/_forgot_sidebar.html.haml new file mode 100644 index 0000000000..8f1f336bbd --- /dev/null +++ b/app/views/shared/_forgot_sidebar.html.haml @@ -0,0 +1,5 @@ +%tab#forgot{"ng-controller" => "ForgotSidebarCtrl", + heading: "Forgot Password?", + active: "active()", + select: "select()"} + Well you're a bit stupid then diff --git a/app/views/shared/_login_panel.html.haml b/app/views/shared/_login_panel.html.haml deleted file mode 100644 index e2b3d561a8..0000000000 --- a/app/views/shared/_login_panel.html.haml +++ /dev/null @@ -1,38 +0,0 @@ -.login-panel - %a{href: "#"} Close - #login-content{"ng-controller" => "LoginSidebarCtrl", "ng-show" => "active()"} - %h2 Login - %form{"ng-submit" => "submit()"} - .alert-box.alert{"ng-show" => "errors != null"} - {{ errors }} - .row - .large-12.columns - %label{for: "email"} Email - %input.title.input-text{name: "email", - type: "email", - tabindex: 1, - "ng-model" => "spree_user.email"} - .row - .large-12.columns - %label{for: "password"} Password - %input.title.input-text{name: "password", - type: "password", - autocomplete: "off", - tabindex: 2, - "ng-model" => "spree_user.password"} - .row - .large-12.columns - %label{for: "remember_me"} Remember Me - %input{name: "remember_me", - type: "checkbox", - value: "1", - "ng-model" => "spree_user.remember_me"} - .row - .large-12.columns - %input.button.primary{name: "commit", - tabindex: "3", - type: "submit", - value: "Login"} - - #sign-up-content{"ng-controller" => "SignupSidebarCtrl", "ng-show" => "active()"} - = render "home/signup" diff --git a/app/views/shared/_login_sidebar.html.haml b/app/views/shared/_login_sidebar.html.haml new file mode 100644 index 0000000000..b5b05ca245 --- /dev/null +++ b/app/views/shared/_login_sidebar.html.haml @@ -0,0 +1,35 @@ +%tab#login-content{"ng-controller" => "LoginSidebarCtrl", + heading: "Login", + active: "active()", + select: "select()"} + %form{"ng-submit" => "submit()"} + .alert-box.alert{"ng-show" => "errors != null"} + {{ errors }} + .row + .large-12.columns + %label{for: "email"} Email + %input.title.input-text{name: "email", + type: "email", + tabindex: 1, + "ng-model" => "spree_user.email"} + .row + .large-12.columns + %label{for: "password"} Password + %input.title.input-text{name: "password", + type: "password", + autocomplete: "off", + tabindex: 2, + "ng-model" => "spree_user.password"} + .row + .large-12.columns + %label{for: "remember_me"} Remember Me + %input{name: "remember_me", + type: "checkbox", + value: "1", + "ng-model" => "spree_user.remember_me"} + .row + .large-12.columns + %input.button.primary{name: "commit", + tabindex: "3", + type: "submit", + value: "Login"} diff --git a/app/views/shared/_menu.html.haml b/app/views/shared/_menu.html.haml index 1250158386..fe0faf1143 100644 --- a/app/views/shared/_menu.html.haml +++ b/app/views/shared/_menu.html.haml @@ -1,6 +1,6 @@ %nav.top-bar %section.top-bar-section - %ul.left + %ul.left{"ng-controller" => "MenuCtrl"} %li= link_to image_tag("ofn_logo_small.png"), root_path %li.divider - if spree_current_user.nil? diff --git a/app/views/shared/_sidebar.html.haml b/app/views/shared/_sidebar.html.haml new file mode 100644 index 0000000000..d4c96f4a33 --- /dev/null +++ b/app/views/shared/_sidebar.html.haml @@ -0,0 +1,7 @@ +%section#sidebar{ role: "complementary", "ng-controller" => "SidebarCtrl", +"ng-class" => "{'active' : active()}"} + %tabset + = render partial: "shared/login_sidebar" + = render partial: "shared/signup_sidebar" + = render partial: "shared/forgot_sidebar" + = yield :sidebar diff --git a/app/views/shared/_signed_out.html.haml b/app/views/shared/_signed_out.html.haml index 6c7c4e9d21..1705c6b5bf 100644 --- a/app/views/shared/_signed_out.html.haml +++ b/app/views/shared/_signed_out.html.haml @@ -1,5 +1,8 @@ -%li#login-link= link_to "Login", "#login", id: "sidebarLoginButton", class: "sidebar-button" +%li#login-link + %a.sidebar-button{"ng-click" => "toggleLogin()"} Login + %li#login-name.hide %li.divider -%li#sign-up-link= link_to "Sign Up", "#signup", id: "sidebarSignUpButton", class: "sidebar-button" -%li#sign-out-link.hide= link_to "Sign Out", "/logout" + +%li#sign-up-link + %a.sidebar-button{"ng-click" => "toggleSignup()"} Sign Up diff --git a/app/views/shared/_signup_sidebar.html.haml b/app/views/shared/_signup_sidebar.html.haml new file mode 100644 index 0000000000..5804151ff6 --- /dev/null +++ b/app/views/shared/_signup_sidebar.html.haml @@ -0,0 +1,38 @@ +%tab#sign-up-content{"ng-controller" => "SignupSidebarCtrl", + heading: "Signup", + active: "active()", + select: "select()"} + %form{"ng-submit" => "submit()"} + .row + .large-12.columns + %label{for: "email"} Email + %input.title.input-text{name: "email", + type: "email", + tabindex: 1, + "ng-model" => "spree_user.email"} + %span.error{"ng-show" => "errors.email != null"} + {{ errors.email.join(' ') }} + .row + .large-12.columns + %label{for: "password"} Password + %input.title.input-text{name: "password", + type: "password", + autocomplete: "off", + tabindex: 2, + "ng-model" => "spree_user.password"} + %span.error{"ng-show" => "errors.password != null"} + {{ errors.password.join(' ') }} + .row + .large-12.columns + %label{for: "password"} Password Confirmation + %input.title.input-text{name: "password_confirmation", + type: "password", + autocomplete: "off", + tabindex: 2, + "ng-model" => "spree_user.password_confirmation"} + .row + .large-12.columns + %input.button.primary{name: "commit", + tabindex: "3", + type: "submit", + value: "Signup"} diff --git a/app/views/shop/_tabs.html.haml b/app/views/shop/_tabs.html.haml index f5acad4208..a548a6710e 100644 --- a/app/views/shop/_tabs.html.haml +++ b/app/views/shop/_tabs.html.haml @@ -1,7 +1,13 @@ -%tabs +#tabs{"ng-controller" => "TabsCtrl"} .row - .section-container.auto{"data-section" => "", "data-options" => "one_up: false"} - = render 'shop/shop/about_us' - = render 'shop/shop/producers' - = render 'shop/shop/groups' - = render 'shop/shop/contact' + %tabset + - for name, heading in { about: "About Us", + producers: "Producers", + groups: "Groups", + contact: "Contact"} + + %tab{heading: heading, + id: "tab_#{name}", + active: "active(#{name}.path)", + select: "select(#{name})"} + = render "shop/shop/#{name}" diff --git a/app/views/shop/shop/_about.html.haml b/app/views/shop/shop/_about.html.haml new file mode 100644 index 0000000000..5c6ddd684b --- /dev/null +++ b/app/views/shop/shop/_about.html.haml @@ -0,0 +1,3 @@ +.content#about + %img.about.right{src: current_distributor.promo_image.url(:large)} + %p= current_distributor.long_description.andand.html_safe diff --git a/app/views/shop/shop/_about_us.html.haml b/app/views/shop/shop/_about_us.html.haml deleted file mode 100644 index 920a4a8236..0000000000 --- a/app/views/shop/shop/_about_us.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -%section#about{class: current_order_cycle ? nil : "active" } - %p.title.avenir{"data-section-title" => ""} - %a{href: "#about"} About Us - - .content{"data-section-content" => ""} - %img.about.right{src: current_distributor.promo_image.url(:large)} - %p= current_distributor.long_description.andand.html_safe - - -#.panel - -#= @distributor.distributor_info.andand.html_safe diff --git a/app/views/shop/shop/_contact.html.haml b/app/views/shop/shop/_contact.html.haml index bc5497c173..9ef1801fba 100644 --- a/app/views/shop/shop/_contact.html.haml +++ b/app/views/shop/shop/_contact.html.haml @@ -1,18 +1,15 @@ -%section - %p.title.avenir{"data-section-title" => ""} - %a{href: "#contact"} Contact - .content{"data-section-content" => ""} - .panel - %p - %strong E - %a{href: "mailto:#{current_distributor.email}"}= current_distributor.email - - unless current_distributor.website.blank? - %br - %strong W - %a{href: current_distributor.website}= current_distributor.website +.content#contact + .panel + %p + %strong E + %a{href: "mailto:#{current_distributor.email}"}= current_distributor.email + - unless current_distributor.website.blank? %br - = [current_distributor.address.address1, current_distributor.address.address2].join ", " - %br - = current_distributor.address.city - = current_distributor.address.state - = current_distributor.address.zipcode + %strong W + %a{href: current_distributor.website}= current_distributor.website + %br + = [current_distributor.address.address1, current_distributor.address.address2].join ", " + %br + = current_distributor.address.city + = current_distributor.address.state + = current_distributor.address.zipcode diff --git a/app/views/shop/shop/_groups.html.haml b/app/views/shop/shop/_groups.html.haml index c3837bf635..6a0f6ff290 100644 --- a/app/views/shop/shop/_groups.html.haml +++ b/app/views/shop/shop/_groups.html.haml @@ -1,13 +1,10 @@ -%section - %p.title.avenir{"data-section-title" => ""} - %a{href: "#groups"} Our Groups - .content{"data-section-content" => ""} - %ul - - for group in current_distributor.groups - %li - %h4= group.name - %ul - - for sibling in group.enterprises.except(current_distributor) - %li - %a{"data-reveal-id" => "sibling_details_#{sibling.id}"} - = sibling.name +.content#groups + %ul + - for group in current_distributor.groups + %li + %h4= group.name + %ul + - for sibling in group.enterprises.except(current_distributor) + %li + %a{"data-reveal-id" => "sibling_details_#{sibling.id}"} + = sibling.name diff --git a/app/views/shop/shop/_producers.html.haml b/app/views/shop/shop/_producers.html.haml index 834d15d53f..1b22361a14 100644 --- a/app/views/shop/shop/_producers.html.haml +++ b/app/views/shop/shop/_producers.html.haml @@ -1,9 +1,6 @@ -%section - %p.title.avenir{"data-section-title" => ""} - %a{href: "#producers"} Our Producers - .content{"data-section-content" => ""} - %ul - - for producer in current_producers - %li - %a{"data-reveal-id" => "producer_details_#{producer.id}"} - = producer.name +.content#producers + %ul + - for producer in current_producers + %li + %a{"data-reveal-id" => "producer_details_#{producer.id}"} + = producer.name diff --git a/app/views/shop/shop/show.html.haml b/app/views/shop/shop/show.html.haml index 250942357c..df054840da 100644 --- a/app/views/shop/shop/show.html.haml +++ b/app/views/shop/shop/show.html.haml @@ -8,7 +8,7 @@ %closing -#%img{src: "/icon/goes/here"} Orders close - %strong {{ order_cycle.orders_close_at | date_in_words }} + %strong {{ OrderCycle.orders_close_at() | date_in_words }} = render partial: "shop/details" %products.row diff --git a/app/views/spree/api/products/bulk_show.v1.rabl b/app/views/spree/api/products/bulk_show.v1.rabl index a220737007..37a4680e2f 100644 --- a/app/views/spree/api/products/bulk_show.v1.rabl +++ b/app/views/spree/api/products/bulk_show.v1.rabl @@ -1,8 +1,9 @@ object @product -attributes :id, :name, :price, :variant_unit, :variant_unit_scale, :variant_unit_name, :on_demand +attributes :id, :name, :variant_unit, :variant_unit_scale, :variant_unit_name, :on_demand # Infinity is not a valid JSON object, but Rails encodes it anyway -node( :on_hand ) { |p| p.on_hand.to_f.finite? ? p.on_hand : "On demand" } +node( :on_hand ) { |p| p.on_hand.nil? ? 0 : p.on_hand.to_f.finite? ? p.on_hand : "On demand" } +node( :price ) { |p| p.price.nil? ? '0.0' : p.price } node( :available_on ) { |p| p.available_on.blank? ? "" : p.available_on.strftime("%F %T") } node( :permalink_live ) { |p| p.permalink } diff --git a/app/views/spree/api/variants/bulk_show.v1.rabl b/app/views/spree/api/variants/bulk_show.v1.rabl index ee069c600c..874edf3f8b 100644 --- a/app/views/spree/api/variants/bulk_show.v1.rabl +++ b/app/views/spree/api/variants/bulk_show.v1.rabl @@ -1,6 +1,7 @@ object @variant -attributes :id, :options_text, :price, :unit_value, :unit_description, :on_demand +attributes :id, :options_text, :unit_value, :unit_description, :on_demand # Infinity is not a valid JSON object, but Rails encodes it anyway -node( :on_hand ) { |p| p.on_hand.to_f.finite? ? p.on_hand : "On demand" } +node( :on_hand ) { |v| v.on_hand.nil? ? 0 : v.on_hand.to_f.finite? ? v.on_hand : "On demand" } +node( :price ) { |v| v.price.nil? ? '0.0' : v.price } \ No newline at end of file diff --git a/config/application.rb b/config/application.rb index 49936d7baf..8f0728dd45 100644 --- a/config/application.rb +++ b/config/application.rb @@ -75,6 +75,11 @@ module Openfoodnetwork # Version of your assets, change this if you want to expire all your assets config.assets.version = '1.0' + config.sass.load_paths += [ + "#{Gem.loaded_specs['foundation-rails'].full_gem_path}/vendor/assets/stylesheets/foundation/components", + "#{Gem.loaded_specs['foundation-rails'].full_gem_path}/vendor/assets/stylesheets/foundation/" + ] + # css and js files other than application.* are not precompiled by default # Instead, they must be explicitly included below # http://stackoverflow.com/questions/8012434/what-is-the-purpose-of-config-assets-precompile @@ -85,5 +90,6 @@ module Openfoodnetwork config.assets.precompile += ['comfortable_mexican_sofa/*'] config.assets.precompile += ['search/all.css', 'search/*.js'] config.assets.precompile += ['shared/*'] + end end diff --git a/config/ng-test.conf.js b/config/ng-test.conf.js index 47c41afd67..b1bddd6532 100644 --- a/config/ng-test.conf.js +++ b/config/ng-test.conf.js @@ -9,6 +9,7 @@ module.exports = function(config) { 'app/assets/javascripts/shared/angular.js', 'app/assets/javascripts/shared/angular-*.js', 'app/assets/javascripts/shared/jquery.timeago.js', + 'app/assets/javascripts/shared/mm-foundation-tpls-0.2.0-SNAPSHOT.js', 'app/assets/javascripts/admin/shared_directives.js.coffee', 'app/assets/javascripts/admin/shared_services.js.coffee', diff --git a/config/routes.rb b/config/routes.rb index 14519b4b13..76981588f8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,12 +8,8 @@ Openfoodnetwork::Application.routes.draw do end namespace :shop do - #resource :checkout, only: :edit, controller: :checkout do - #get '', to: "" - #end get '/checkout', :to => 'checkout#edit' , :as => :checkout put '/checkout', :to => 'checkout#update' , :as => :update_checkout - get "/checkout/paypal_payment", to: 'checkout#paypal_payment', as: :paypal_payment end @@ -73,6 +69,20 @@ Openfoodnetwork::Application.routes.draw do end +# Overriding Devise routes to use our own controller +Spree::Core::Engine.routes.draw do + devise_for :spree_user, + :class_name => 'Spree::User', + :controllers => { :sessions => 'spree/user_sessions', + :registrations => 'user_registrations', + :passwords => 'spree/user_passwords' }, + :skip => [:unlocks, :omniauth_callbacks], + :path_names => { :sign_out => 'logout' }, + :path_prefix => :user +end + + + Spree::Core::Engine.routes.prepend do match '/admin/reports/orders_and_distributors' => 'admin/reports#orders_and_distributors', :as => "orders_and_distributors_admin_reports", :via => [:get, :post] match '/admin/reports/group_buys' => 'admin/reports#group_buys', :as => "group_buys_admin_reports", :via => [:get, :post] diff --git a/db/migrate/20140324025840_add_incoming_to_exchanges.rb b/db/migrate/20140324025840_add_incoming_to_exchanges.rb new file mode 100644 index 0000000000..37df0a48db --- /dev/null +++ b/db/migrate/20140324025840_add_incoming_to_exchanges.rb @@ -0,0 +1,25 @@ +class AddIncomingToExchanges < ActiveRecord::Migration + class Exchange < ActiveRecord::Base + belongs_to :order_cycle + belongs_to :receiver, :class_name => 'Enterprise' + + def incoming? + receiver == order_cycle.coordinator + end + end + + + def up + add_column :exchanges, :incoming, :boolean, null: false, default: false + + # Initialise based on whether the exchange is going to or coming + # from the order cycle coordinator + Exchange.all.each do |exchange| + exchange.update_attribute :incoming, exchange.incoming? + end + end + + def down + remove_column :exchanges, :incoming + end +end diff --git a/db/schema.rb b/db/schema.rb index b9f151fedf..892382d1d7 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 => 20140213003443) do +ActiveRecord::Schema.define(:version => 20140324025840) do create_table "adjustment_metadata", :force => true do |t| t.integer "adjustment_id" @@ -236,8 +236,9 @@ ActiveRecord::Schema.define(:version => 20140213003443) do t.integer "payment_enterprise_id" t.string "pickup_time" t.string "pickup_instructions" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + t.boolean "incoming", :default => false, :null => false end create_table "landing_page_images", :force => true do |t| diff --git a/lib/open_food_network/order_cycle_form_applicator.rb b/lib/open_food_network/order_cycle_form_applicator.rb index ef8b4f86e7..dc5056d53a 100644 --- a/lib/open_food_network/order_cycle_form_applicator.rb +++ b/lib/open_food_network/order_cycle_form_applicator.rb @@ -17,11 +17,11 @@ module OpenFoodNetwork variant_ids = exchange_variant_ids(exchange) enterprise_fee_ids = exchange[:enterprise_fee_ids] - if exchange_exists?(exchange[:enterprise_id], @order_cycle.coordinator_id) - update_exchange(exchange[:enterprise_id], @order_cycle.coordinator_id, + if exchange_exists?(exchange[:enterprise_id], @order_cycle.coordinator_id, true) + update_exchange(exchange[:enterprise_id], @order_cycle.coordinator_id, true, {variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids}) else - add_exchange(exchange[:enterprise_id], @order_cycle.coordinator_id, + add_exchange(exchange[:enterprise_id], @order_cycle.coordinator_id, true, {variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids}) end end @@ -31,12 +31,12 @@ module OpenFoodNetwork variant_ids = exchange_variant_ids(exchange) enterprise_fee_ids = exchange[:enterprise_fee_ids] - if exchange_exists?(@order_cycle.coordinator_id, exchange[:enterprise_id]) - update_exchange(@order_cycle.coordinator_id, exchange[:enterprise_id], + if exchange_exists?(@order_cycle.coordinator_id, exchange[:enterprise_id], false) + update_exchange(@order_cycle.coordinator_id, exchange[:enterprise_id], false, {variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids, pickup_time: exchange[:pickup_time], pickup_instructions: exchange[:pickup_instructions]}) else - add_exchange(@order_cycle.coordinator_id, exchange[:enterprise_id], + add_exchange(@order_cycle.coordinator_id, exchange[:enterprise_id], false, {variant_ids: variant_ids, enterprise_fee_ids: enterprise_fee_ids, pickup_time: exchange[:pickup_time], pickup_instructions: exchange[:pickup_instructions]}) end @@ -50,18 +50,18 @@ module OpenFoodNetwork attr_accessor :touched_exchanges - def exchange_exists?(sender_id, receiver_id) - @order_cycle.exchanges.where(:sender_id => sender_id, :receiver_id => receiver_id).present? + def exchange_exists?(sender_id, receiver_id, incoming) + @order_cycle.exchanges.where(:sender_id => sender_id, :receiver_id => receiver_id, :incoming => incoming).present? end - def add_exchange(sender_id, receiver_id, attrs={}) - attrs = attrs.reverse_merge(:sender_id => sender_id, :receiver_id => receiver_id) + def add_exchange(sender_id, receiver_id, incoming, attrs={}) + attrs = attrs.reverse_merge(:sender_id => sender_id, :receiver_id => receiver_id, :incoming => incoming) exchange = @order_cycle.exchanges.create! attrs @touched_exchanges << exchange end - def update_exchange(sender_id, receiver_id, attrs={}) - exchange = @order_cycle.exchanges.where(:sender_id => sender_id, :receiver_id => receiver_id).first + def update_exchange(sender_id, receiver_id, incoming, attrs={}) + exchange = @order_cycle.exchanges.where(:sender_id => sender_id, :receiver_id => receiver_id, :incoming => incoming).first exchange.update_attributes!(attrs) @touched_exchanges << exchange diff --git a/spec/archive/features/consumer/checkout_spec.rb b/spec/archive/features/consumer/checkout_spec.rb index 2c42a023e7..525805eee5 100644 --- a/spec/archive/features/consumer/checkout_spec.rb +++ b/spec/archive/features/consumer/checkout_spec.rb @@ -490,8 +490,8 @@ feature %q{ supplier_fee2 = create(:enterprise_fee, enterprise: supplier1, fee_type: 'transport', amount: 4) supplier_fee3 = create(:enterprise_fee, enterprise: supplier2, fee_type: 'admin', amount: 5) supplier_fee4 = create(:enterprise_fee, enterprise: supplier2, fee_type: 'sales', amount: 6) - ex1 = create(:exchange, order_cycle: oc, sender: supplier1, receiver: oc.coordinator) - ex2 = create(:exchange, order_cycle: oc, sender: supplier2, receiver: oc.coordinator) + ex1 = create(:exchange, order_cycle: oc, sender: supplier1, receiver: oc.coordinator, incoming: true) + ex2 = create(:exchange, order_cycle: oc, sender: supplier2, receiver: oc.coordinator, incoming: true) ExchangeFee.create!(exchange: ex1, enterprise_fee: supplier_fee1) ExchangeFee.create!(exchange: ex1, enterprise_fee: supplier_fee2) ExchangeFee.create!(exchange: ex2, enterprise_fee: supplier_fee3) @@ -506,10 +506,10 @@ feature %q{ distributor_fee2 = create(:enterprise_fee, enterprise: distributor1, fee_type: 'transport', amount: 8) distributor_fee3 = create(:enterprise_fee, enterprise: distributor2, fee_type: 'admin', amount: 9) distributor_fee4 = create(:enterprise_fee, enterprise: distributor2, fee_type: 'sales', amount: 10) - ex3 = create(:exchange, order_cycle: oc, + ex3 = create(:exchange, order_cycle: oc, incoming: false, sender: oc.coordinator, receiver: distributor1, pickup_time: 'time 0', pickup_instructions: 'instructions 0') - ex4 = create(:exchange, order_cycle: oc, + ex4 = create(:exchange, order_cycle: oc, incoming: false, sender: oc.coordinator, receiver: distributor2, pickup_time: 'time 1', pickup_instructions: 'instructions 1') ExchangeFee.create!(exchange: ex3, enterprise_fee: distributor_fee1) diff --git a/spec/controllers/enterprises_controller_spec.rb b/spec/controllers/enterprises_controller_spec.rb index 8cb55e47a1..77a01e42de 100644 --- a/spec/controllers/enterprises_controller_spec.rb +++ b/spec/controllers/enterprises_controller_spec.rb @@ -20,8 +20,8 @@ describe EnterprisesController do let(:oc2) { create(:simple_order_cycle) } it "displays products for the selected (order_cycle -> outgoing exchange)" do - create(:exchange, order_cycle: oc1, sender: s, receiver: c, variants: [p.master]) - create(:exchange, order_cycle: oc1, sender: c, receiver: d1, variants: [p.master]) + create(:exchange, order_cycle: oc1, sender: s, receiver: c, incoming: true, variants: [p.master]) + create(:exchange, order_cycle: oc1, sender: c, receiver: d1, incoming: false, variants: [p.master]) controller.stub(:current_distributor) { d1 } controller.stub(:current_order_cycle) { oc1 } @@ -33,12 +33,12 @@ describe EnterprisesController do it "does not display other products in the order cycle or in the distributor" do # Given a product that is in this order cycle on a different distributor - create(:exchange, order_cycle: oc1, sender: s, receiver: c, variants: [p.master]) - create(:exchange, order_cycle: oc1, sender: c, receiver: d2, variants: [p.master]) + create(:exchange, order_cycle: oc1, sender: s, receiver: c, incoming: true, variants: [p.master]) + create(:exchange, order_cycle: oc1, sender: c, receiver: d2, incoming: false, variants: [p.master]) # And is also in this distributor in a different order cycle - create(:exchange, order_cycle: oc2, sender: s, receiver: c, variants: [p.master]) - create(:exchange, order_cycle: oc2, sender: c, receiver: d1, variants: [p.master]) + create(:exchange, order_cycle: oc2, sender: s, receiver: c, incoming: true, variants: [p.master]) + create(:exchange, order_cycle: oc2, sender: c, receiver: d1, incoming: false, variants: [p.master]) # When I view the enterprise page for d1 x oc1 controller.stub(:current_distributor) { d1 } diff --git a/spec/controllers/spree/orders_controller_spec.rb b/spec/controllers/spree/orders_controller_spec.rb index 322c4a9ef2..a9118355d6 100644 --- a/spec/controllers/spree/orders_controller_spec.rb +++ b/spec/controllers/spree/orders_controller_spec.rb @@ -1,6 +1,19 @@ require 'spec_helper' describe Spree::OrdersController do + let(:distributor) { double(:distributor) } + + it "redirects home when no distributor is selected" do + spree_get :edit + response.should redirect_to root_path + end + + it "redirects to the shop when no order cycle is selected" do + controller.stub(:current_distributor).and_return(distributor) + spree_get :edit + response.should redirect_to shop_path + end + it "selects distributors" do d = create(:distributor_enterprise) p = create(:product, :distributors => [d]) diff --git a/spec/controllers/user_passwords_controller_spec.rb b/spec/controllers/user_passwords_controller_spec.rb new file mode 100644 index 0000000000..1417fca80e --- /dev/null +++ b/spec/controllers/user_passwords_controller_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' +require 'spree/api/testing_support/helpers' + +describe UserPasswordsController do + + before do + @request.env["devise.mapping"] = Devise.mappings[:spree_user] + ActionMailer::Base.default_url_options[:host] = "test.host" + end + + it "returns errors when no data received" do + spree_post :create, spree_user: {} + response.should be_success + response.should render_template "spree/user_passwords/new" + end + + it "redirects to login when data is valid" do + user = create(:user) + spree_post :create, spree_user: { email: user.email} + response.should be_redirect + end + +end + diff --git a/spec/controllers/user_registrations_controller_spec.rb b/spec/controllers/user_registrations_controller_spec.rb new file mode 100644 index 0000000000..ed47f0c059 --- /dev/null +++ b/spec/controllers/user_registrations_controller_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' +require 'spree/api/testing_support/helpers' + +describe UserRegistrationsController do + + before do + @request.env["devise.mapping"] = Devise.mappings[:spree_user] + end + + describe "via ajax" do + render_views + it "returns errors when registration fails" do + xhr :post, :create, spree_user: {}, :use_route => :spree + response.status.should == 401 + json = JSON.parse(response.body) + json.should == {"email" => ["can't be blank"], "password" => ["can't be blank"]} + end + + it "returns 200 when registration succeeds" do + xhr :post, :create, spree_user: {email: "test@test.com", password: "testy123", password_confirmation: "testy123"}, :use_route => :spree + response.status.should == 200 + json = JSON.parse(response.body) + json.should == {"email" => "test@test.com"} + controller.spree_current_user.email.should == "test@test.com" + end + end + + it "renders new when registration fails" do + spree_post :create, spree_user: {} + response.status.should == 200 + response.should render_template "spree/user_registrations/new" + end + + it "redirects when registration succeeds" do + spree_post :create, spree_user: {email: "test@test.com", password: "testy123", password_confirmation: "testy123"}, :use_route => :spree + response.should be_redirect + assigns[:user].email.should == "test@test.com" + end +end diff --git a/spec/factories.rb b/spec/factories.rb index 2084ff7221..df1dd4d50c 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -7,9 +7,9 @@ FactoryGirl.define do after(:create) do |oc| # Suppliers - ex1 = create(:exchange, :order_cycle => oc, + ex1 = create(:exchange, :order_cycle => oc, :incoming => true, :sender => create(:supplier_enterprise), :receiver => oc.coordinator) - ex2 = create(:exchange, :order_cycle => oc, + ex2 = create(:exchange, :order_cycle => oc, :incoming => true, :sender => create(:supplier_enterprise), :receiver => oc.coordinator) ExchangeFee.create!(exchange: ex1, enterprise_fee: create(:enterprise_fee, enterprise: ex1.sender)) @@ -17,10 +17,10 @@ FactoryGirl.define do enterprise_fee: create(:enterprise_fee, enterprise: ex2.sender)) # Distributors - ex3 = create(:exchange, :order_cycle => oc, + ex3 = create(:exchange, :order_cycle => oc, :incoming => false, :sender => oc.coordinator, :receiver => create(:distributor_enterprise), :pickup_time => 'time 0', :pickup_instructions => 'instructions 0') - ex4 = create(:exchange, :order_cycle => oc, + ex4 = create(:exchange, :order_cycle => oc, :incoming => false, :sender => oc.coordinator, :receiver => create(:distributor_enterprise), :pickup_time => 'time 1', :pickup_instructions => 'instructions 1') ExchangeFee.create!(exchange: ex3, @@ -60,12 +60,12 @@ FactoryGirl.define do after(:create) do |oc, proxy| proxy.suppliers.each do |supplier| - ex = create(:exchange, :order_cycle => oc, :sender => supplier, :receiver => oc.coordinator, :pickup_time => 'time', :pickup_instructions => 'instructions') + ex = create(:exchange, :order_cycle => oc, :sender => supplier, :receiver => oc.coordinator, :incoming => true, :pickup_time => 'time', :pickup_instructions => 'instructions') proxy.variants.each { |v| ex.variants << v } end proxy.distributors.each do |distributor| - ex = create(:exchange, :order_cycle => oc, :sender => oc.coordinator, :receiver => distributor, :pickup_time => 'time', :pickup_instructions => 'instructions') + ex = create(:exchange, :order_cycle => oc, :sender => oc.coordinator, :receiver => distributor, :incoming => false, :pickup_time => 'time', :pickup_instructions => 'instructions') proxy.variants.each { |v| ex.variants << v } end end @@ -75,6 +75,7 @@ FactoryGirl.define do order_cycle { OrderCycle.first || FactoryGirl.create(:simple_order_cycle) } sender { FactoryGirl.create(:enterprise) } receiver { FactoryGirl.create(:enterprise) } + incoming false end factory :enterprise, :class => Enterprise do diff --git a/spec/features/admin/order_cycles_spec.rb b/spec/features/admin/order_cycles_spec.rb index 3708e03930..20e728a09d 100644 --- a/spec/features/admin/order_cycles_spec.rb +++ b/spec/features/admin/order_cycles_spec.rb @@ -223,6 +223,28 @@ feature %q{ end + scenario "editing an order cycle with an exchange between the same enterprise" do + c = create(:distributor_enterprise, is_primary_producer: true) + login_to_admin_section + + # Given two order cycles, one with a mono-enterprise incoming exchange... + oc_incoming = create(:simple_order_cycle, suppliers: [c], coordinator: c) + + # And the other with a mono-enterprise outgoing exchange + oc_outgoing = create(:simple_order_cycle, coordinator: c, distributors: [c]) + + # When I edit the first order cycle, the exchange should appear as incoming + visit edit_admin_order_cycle_path(oc_incoming) + page.should have_selector 'table.exchanges tr.supplier' + page.should_not have_selector 'table.exchanges tr.distributor' + + # And when I edit the second order cycle, the exchange should appear as outgoing + visit edit_admin_order_cycle_path(oc_outgoing) + page.should have_selector 'table.exchanges tr.distributor' + page.should_not have_selector 'table.exchanges tr.supplier' + end + + scenario "updating an order cycle", js: true do # Given an order cycle with all the settings oc = create(:order_cycle) diff --git a/spec/features/admin/payment_method_spec.rb b/spec/features/admin/payment_method_spec.rb index be91f15490..660a187b3d 100644 --- a/spec/features/admin/payment_method_spec.rb +++ b/spec/features/admin/payment_method_spec.rb @@ -20,8 +20,8 @@ feature %q{ click_link 'New Payment Method' fill_in 'payment_method_name', :with => 'Cheque payment method' - - select @distributors[0].name, :from => 'payment_method_distributor_ids' + + select @distributors[0].name, :from => 'payment_method_distributor_ids', visible: false click_button 'Create' flash_message.should == 'Payment Method has been successfully created!' diff --git a/spec/features/chili/enterprises_distributor_info_rich_text_feature_spec.rb b/spec/features/chili/enterprises_distributor_info_rich_text_feature_spec.rb index 3855b80bee..46385cee0f 100644 --- a/spec/features/chili/enterprises_distributor_info_rich_text_feature_spec.rb +++ b/spec/features/chili/enterprises_distributor_info_rich_text_feature_spec.rb @@ -77,7 +77,6 @@ feature "enterprises distributor info as rich text" do # -- Checkout click_button 'Add To Cart' - find('#checkout-link').click visit "/checkout" within 'fieldset#shipping' do page.should have_content 'Chu ge sai yubi dan bisento tobi ashi yubi ge omote.' diff --git a/spec/features/consumer/shopping/checkout_spec.rb b/spec/features/consumer/shopping/checkout_spec.rb index 553f53dd21..211023b5a6 100644 --- a/spec/features/consumer/shopping/checkout_spec.rb +++ b/spec/features/consumer/shopping/checkout_spec.rb @@ -16,8 +16,7 @@ feature "As a consumer I want to check out my cart", js: true do exchange.variants << product.master end - # Run these tests both logged in and logged out! - [:in, :out].each do |auth_state| + [:out].each do |auth_state| describe "logged #{auth_state.to_s}, distributor selected, order cycle selected, product in cart" do let(:user) { create_enterprise_user } before do @@ -44,7 +43,7 @@ feature "As a consumer I want to check out my cart", js: true do it "doesn't show ship address forms when a shipping method wants no address" do choose(sm2.name) - find("#ship_address").visible?.should be_false + find("#ship_address", visible: false).visible?.should be_false end context "When shipping method requires an address" do @@ -54,7 +53,7 @@ feature "As a consumer I want to check out my cart", js: true do it "shows the hidden ship address fields by default" do check "Shipping address same as billing address?" find("#ship_address_hidden").visible?.should be_true - find("#ship_address > div.visible").visible?.should be_false + find("#ship_address > div.visible", visible: false).visible?.should be_false # Check it keeps state click_button "Purchase" @@ -63,7 +62,7 @@ feature "As a consumer I want to check out my cart", js: true do it "shows ship address forms when 'same as billing address' is unchecked" do uncheck "Shipping address same as billing address?" - find("#ship_address_hidden").visible?.should be_false + find("#ship_address_hidden", visible: false).visible?.should be_false find("#ship_address > div.visible").visible?.should be_true # Check it keeps state @@ -77,7 +76,7 @@ feature "As a consumer I want to check out my cart", js: true do check "Shipping address same as billing address?" fill_in "Billing Address", with: "testy" within "#ship_address_hidden" do - find("#order_ship_address_attributes_address1").value.should == "testy" + find("#order_ship_address_attributes_address1", visible: false).value.should == "testy" end end diff --git a/spec/features/consumer/shopping/shopping_spec.rb b/spec/features/consumer/shopping/shopping_spec.rb index 67150f3f62..1b3f7b613c 100644 --- a/spec/features/consumer/shopping/shopping_spec.rb +++ b/spec/features/consumer/shopping/shopping_spec.rb @@ -20,6 +20,7 @@ feature "As a consumer I want to shop with a distributor", js: true do it "shows distributor images" do visit shop_path + find("#tab_about a").click first("distributor img")['src'].should == distributor.logo.url(:thumb) first("#about img")['src'].should == distributor.promo_image.url(:large) end @@ -36,7 +37,7 @@ feature "As a consumer I want to shop with a distributor", js: true do it "shows the suppliers/producers for a distributor" do visit shop_path - click_link "Our Producers" + find("#tab_producers a").click page.should have_content supplier.name end end @@ -67,10 +68,6 @@ feature "As a consumer I want to shop with a distributor", js: true do page.should have_selector "option", text: 'turtles' end - it "shows the About Us by default if no order cycle is selected" do - page.should have_content "Hello, world!" - end - it "doesn't show the table before an order cycle is selected" do page.should_not have_selector("input.button.right", visible: true) end diff --git a/spec/helpers/shared_helper_spec.rb b/spec/helpers/shared_helper_spec.rb index 65d978d86c..d28a6eb7b3 100644 --- a/spec/helpers/shared_helper_spec.rb +++ b/spec/helpers/shared_helper_spec.rb @@ -24,4 +24,36 @@ describe SharedHelper do helper.distributor_link_class(d1).should =~ /empties-cart/ end + describe "finding current producers" do + it "finds producers for the current distribution" do + s = create(:supplier_enterprise) + d = create(:distributor_enterprise) + p = create(:simple_product) + oc = create(:simple_order_cycle, suppliers: [s], distributors: [d], variants: [p.master]) + + helper.stub(:current_order_cycle) { oc } + helper.stub(:current_distributor) { d } + + helper.current_producers.should == [s] + end + + it "returns [] when no order cycle set" do + d = double(:distributor) + + helper.stub(:current_order_cycle) { nil } + helper.stub(:current_distributor) { d } + + helper.current_producers.should == [] + end + + it "returns [] when no distributor set" do + oc = double(:order_cycle) + + helper.stub(:current_order_cycle) { oc } + helper.stub(:current_distributor) { nil } + + helper.current_producers.should == [] + + end + end end diff --git a/spec/javascripts/unit/darkswarm/controllers/sidebar_controller_spec.js.coffee b/spec/javascripts/unit/darkswarm/controllers/sidebar_controller_spec.js.coffee index d9348021d7..0d051a8cfa 100644 --- a/spec/javascripts/unit/darkswarm/controllers/sidebar_controller_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/controllers/sidebar_controller_spec.js.coffee @@ -7,18 +7,16 @@ describe "SidebarCtrl", -> module("Darkswarm") location = path: -> - "/test" + "/login" inject ($controller, $rootScope) -> scope = $rootScope ctrl = $controller 'SidebarCtrl', {$scope: scope, $location: location} scope.$apply() - it 'tracks the active sidebar from the $location', -> - expect(scope.active_sidebar).toEqual "/test" - it 'is active when a location is set', -> - expect(scope.active()).toEqual "active" + expect(scope.active()).toEqual true it 'is inactive no location is set', -> - scope.active_sidebar = null - expect(scope.active()).toEqual null + location.path = -> + null + expect(scope.active()).toEqual false diff --git a/spec/javascripts/unit/darkswarm/order_cycle_spec.js.coffee b/spec/javascripts/unit/darkswarm/order_cycle_spec.js.coffee index 395b51512e..9687e72380 100644 --- a/spec/javascripts/unit/darkswarm/order_cycle_spec.js.coffee +++ b/spec/javascripts/unit/darkswarm/order_cycle_spec.js.coffee @@ -32,3 +32,13 @@ describe 'OrderCycle service', -> $httpBackend.flush() expect(OrderCycle.order_cycle.orders_close_at).toEqual(datestring) + it "tells us when the order cycle closes", -> + OrderCycle.order_cycle.orders_close_at = "test" + expect(OrderCycle.orders_close_at()).toEqual "test" + + it "tells us when no order cycle is selected", -> + OrderCycle.order_cycle = null + expect(OrderCycle.selected()).toEqual false + OrderCycle.order_cycle = {test: "blah"} + expect(OrderCycle.selected()).toEqual true + diff --git a/spec/javascripts/unit/order_cycle_spec.js.coffee b/spec/javascripts/unit/order_cycle_spec.js.coffee index 53f451dd54..0466a5b861 100644 --- a/spec/javascripts/unit/order_cycle_spec.js.coffee +++ b/spec/javascripts/unit/order_cycle_spec.js.coffee @@ -403,8 +403,8 @@ describe 'OrderCycle services', -> coordinator_id: 456 coordinator_fees: [] exchanges: [ - {sender_id: 1, receiver_id: 456} - {sender_id: 456, receiver_id: 2} + {sender_id: 1, receiver_id: 456, incoming: true} + {sender_id: 456, receiver_id: 2, incoming: false} ] it 'initialises order cycle', -> @@ -456,7 +456,7 @@ describe 'OrderCycle services', -> it 'adds the supplier to incoming exchanges', -> OrderCycle.addSupplier('123') expect(OrderCycle.order_cycle.incoming_exchanges).toEqual [ - {enterprise_id: '123', active: true, variants: {}, enterprise_fees: []} + {enterprise_id: '123', incoming: true, active: true, variants: {}, enterprise_fees: []} ] describe 'adding distributors', -> @@ -465,7 +465,7 @@ describe 'OrderCycle services', -> it 'adds the distributor to outgoing exchanges', -> OrderCycle.addDistributor('123') expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [ - {enterprise_id: '123', active: true, variants: {}, enterprise_fees: []} + {enterprise_id: '123', incoming: false, active: true, variants: {}, enterprise_fees: []} ] describe 'removing exchanges', -> @@ -630,12 +630,14 @@ describe 'OrderCycle services', -> expect(OrderCycle.order_cycle.incoming_exchanges).toEqual [ sender_id: 1 enterprise_id: 1 + incoming: true active: true ] expect(OrderCycle.order_cycle.outgoing_exchanges).toEqual [ receiver_id: 2 enterprise_id: 2 + incoming: false active: true ] diff --git a/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb b/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb index 441e28beeb..81cd925569 100644 --- a/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb +++ b/spec/lib/open_food_network/order_cycle_form_applicator_spec.rb @@ -7,15 +7,15 @@ module OpenFoodNetwork coordinator_id = 123 supplier_id = 456 - incoming_exchange = {:enterprise_id => supplier_id, :variants => {'1' => true, '2' => false, '3' => true}, :enterprise_fee_ids => [1, 2]} + incoming_exchange = {:enterprise_id => supplier_id, :incoming => true, :variants => {'1' => true, '2' => false, '3' => true}, :enterprise_fee_ids => [1, 2]} oc = double(:order_cycle, :coordinator_id => coordinator_id, :exchanges => [], :incoming_exchanges => [incoming_exchange], :outgoing_exchanges => []) applicator = OrderCycleFormApplicator.new(oc) applicator.should_receive(:exchange_variant_ids).with(incoming_exchange).and_return([1, 3]) - applicator.should_receive(:exchange_exists?).with(supplier_id, coordinator_id).and_return(false) - applicator.should_receive(:add_exchange).with(supplier_id, coordinator_id, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2]}) + applicator.should_receive(:exchange_exists?).with(supplier_id, coordinator_id, true).and_return(false) + applicator.should_receive(:add_exchange).with(supplier_id, coordinator_id, true, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2]}) applicator.should_receive(:destroy_untouched_exchanges) applicator.go! @@ -25,15 +25,15 @@ module OpenFoodNetwork coordinator_id = 123 distributor_id = 456 - outgoing_exchange = {:enterprise_id => distributor_id, :variants => {'1' => true, '2' => false, '3' => true}, :enterprise_fee_ids => [1, 2], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'} + outgoing_exchange = {:enterprise_id => distributor_id, :incoming => false, :variants => {'1' => true, '2' => false, '3' => true}, :enterprise_fee_ids => [1, 2], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'} oc = double(:order_cycle, :coordinator_id => coordinator_id, :exchanges => [], :incoming_exchanges => [], :outgoing_exchanges => [outgoing_exchange]) applicator = OrderCycleFormApplicator.new(oc) applicator.should_receive(:exchange_variant_ids).with(outgoing_exchange).and_return([1, 3]) - applicator.should_receive(:exchange_exists?).with(coordinator_id, distributor_id).and_return(false) - applicator.should_receive(:add_exchange).with(coordinator_id, distributor_id, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'}) + applicator.should_receive(:exchange_exists?).with(coordinator_id, distributor_id, false).and_return(false) + applicator.should_receive(:add_exchange).with(coordinator_id, distributor_id, false, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'}) applicator.should_receive(:destroy_untouched_exchanges) applicator.go! @@ -43,19 +43,19 @@ module OpenFoodNetwork coordinator_id = 123 supplier_id = 456 - incoming_exchange = {:enterprise_id => supplier_id, :variants => {'1' => true, '2' => false, '3' => true}, :enterprise_fee_ids => [1, 2]} + incoming_exchange = {:enterprise_id => supplier_id, :incoming => true, :variants => {'1' => true, '2' => false, '3' => true}, :enterprise_fee_ids => [1, 2]} oc = double(:order_cycle, :coordinator_id => coordinator_id, - :exchanges => [double(:exchange, :sender_id => supplier_id, :receiver_id => coordinator_id)], + :exchanges => [double(:exchange, :sender_id => supplier_id, :receiver_id => coordinator_id, :incoming => true)], :incoming_exchanges => [incoming_exchange], :outgoing_exchanges => []) applicator = OrderCycleFormApplicator.new(oc) applicator.should_receive(:exchange_variant_ids).with(incoming_exchange).and_return([1, 3]) - applicator.should_receive(:exchange_exists?).with(supplier_id, coordinator_id).and_return(true) - applicator.should_receive(:update_exchange).with(supplier_id, coordinator_id, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2]}) + applicator.should_receive(:exchange_exists?).with(supplier_id, coordinator_id, true).and_return(true) + applicator.should_receive(:update_exchange).with(supplier_id, coordinator_id, true, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2]}) applicator.should_receive(:destroy_untouched_exchanges) applicator.go! @@ -65,19 +65,19 @@ module OpenFoodNetwork coordinator_id = 123 distributor_id = 456 - outgoing_exchange = {:enterprise_id => distributor_id, :variants => {'1' => true, '2' => false, '3' => true}, :enterprise_fee_ids => [1, 2], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'} + outgoing_exchange = {:enterprise_id => distributor_id, :incoming => false, :variants => {'1' => true, '2' => false, '3' => true}, :enterprise_fee_ids => [1, 2], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'} oc = double(:order_cycle, :coordinator_id => coordinator_id, - :exchanges => [double(:exchange, :sender_id => coordinator_id, :receiver_id => distributor_id)], + :exchanges => [double(:exchange, :sender_id => coordinator_id, :receiver_id => distributor_id, :incoming => false)], :incoming_exchanges => [], :outgoing_exchanges => [outgoing_exchange]) applicator = OrderCycleFormApplicator.new(oc) applicator.should_receive(:exchange_variant_ids).with(outgoing_exchange).and_return([1, 3]) - applicator.should_receive(:exchange_exists?).with(coordinator_id, distributor_id).and_return(true) - applicator.should_receive(:update_exchange).with(coordinator_id, distributor_id, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'}) + applicator.should_receive(:exchange_exists?).with(coordinator_id, distributor_id, false).and_return(true) + applicator.should_receive(:update_exchange).with(coordinator_id, distributor_id, false, {:variant_ids => [1, 3], :enterprise_fee_ids => [1, 2], :pickup_time => 'pickup time', :pickup_instructions => 'pickup instructions'}) applicator.should_receive(:destroy_untouched_exchanges) applicator.go! @@ -87,7 +87,7 @@ module OpenFoodNetwork it "destroys untouched exchanges" do coordinator_id = 123 supplier_id = 456 - exchange = double(:exchange, :id => 1, :sender_id => supplier_id, :receiver_id => coordinator_id) + exchange = double(:exchange, :id => 1, :sender_id => supplier_id, :receiver_id => coordinator_id, :incoming => true) oc = double(:order_cycle, :coordinator_id => coordinator_id, @@ -134,11 +134,12 @@ module OpenFoodNetwork exchange = FactoryGirl.create(:exchange, order_cycle: oc) applicator = OrderCycleFormApplicator.new(oc) - applicator.send(:exchange_exists?, exchange.sender_id, exchange.receiver_id).should be_true - applicator.send(:exchange_exists?, exchange.receiver_id, exchange.sender_id).should be_false - applicator.send(:exchange_exists?, exchange.sender_id, 999).should be_false - applicator.send(:exchange_exists?, 999, exchange.receiver_id).should be_false - applicator.send(:exchange_exists?, 999, 888).should be_false + applicator.send(:exchange_exists?, exchange.sender_id, exchange.receiver_id, exchange.incoming).should be_true + applicator.send(:exchange_exists?, exchange.sender_id, exchange.receiver_id, !exchange.incoming).should be_false + applicator.send(:exchange_exists?, exchange.receiver_id, exchange.sender_id, exchange.incoming).should be_false + applicator.send(:exchange_exists?, exchange.sender_id, 999999, exchange.incoming).should be_false + applicator.send(:exchange_exists?, 999999, exchange.receiver_id, exchange.incoming).should be_false + applicator.send(:exchange_exists?, 999999, 888888, exchange.incoming).should be_false end it "adds exchanges" do @@ -146,17 +147,19 @@ module OpenFoodNetwork applicator = OrderCycleFormApplicator.new(oc) sender = FactoryGirl.create(:enterprise) receiver = FactoryGirl.create(:enterprise) + incoming = true variant1 = FactoryGirl.create(:variant) variant2 = FactoryGirl.create(:variant) enterprise_fee1 = FactoryGirl.create(:enterprise_fee) enterprise_fee2 = FactoryGirl.create(:enterprise_fee) applicator.send(:touched_exchanges=, []) - applicator.send(:add_exchange, sender.id, receiver.id, {:variant_ids => [variant1.id, variant2.id], :enterprise_fee_ids => [enterprise_fee1.id, enterprise_fee2.id]}) + applicator.send(:add_exchange, sender.id, receiver.id, incoming, {:variant_ids => [variant1.id, variant2.id], :enterprise_fee_ids => [enterprise_fee1.id, enterprise_fee2.id]}) exchange = Exchange.last exchange.sender.should == sender exchange.receiver.should == receiver + exchange.incoming.should == incoming exchange.variants.sort.should == [variant1, variant2].sort exchange.enterprise_fees.sort.should == [enterprise_fee1, enterprise_fee2].sort @@ -168,6 +171,7 @@ module OpenFoodNetwork applicator = OrderCycleFormApplicator.new(oc) sender = FactoryGirl.create(:enterprise) receiver = FactoryGirl.create(:enterprise) + incoming = true variant1 = FactoryGirl.create(:variant) variant2 = FactoryGirl.create(:variant) variant3 = FactoryGirl.create(:variant) @@ -175,10 +179,10 @@ module OpenFoodNetwork enterprise_fee2 = FactoryGirl.create(:enterprise_fee) enterprise_fee3 = FactoryGirl.create(:enterprise_fee) - exchange = FactoryGirl.create(:exchange, order_cycle: oc, sender: sender, receiver: receiver, variant_ids: [variant1.id, variant2.id], enterprise_fee_ids: [enterprise_fee1.id, enterprise_fee2.id]) + exchange = FactoryGirl.create(:exchange, order_cycle: oc, sender: sender, receiver: receiver, incoming: incoming, variant_ids: [variant1.id, variant2.id], enterprise_fee_ids: [enterprise_fee1.id, enterprise_fee2.id]) applicator.send(:touched_exchanges=, []) - applicator.send(:update_exchange, sender.id, receiver.id, {:variant_ids => [variant1.id, variant3.id], :enterprise_fee_ids => [enterprise_fee2.id, enterprise_fee3.id]}) + applicator.send(:update_exchange, sender.id, receiver.id, incoming, {:variant_ids => [variant1.id, variant3.id], :enterprise_fee_ids => [enterprise_fee2.id, enterprise_fee3.id]}) exchange.reload exchange.variants.sort.should == [variant1, variant3].sort diff --git a/spec/models/enterprises_spec.rb b/spec/models/enterprise_spec.rb similarity index 91% rename from spec/models/enterprises_spec.rb rename to spec/models/enterprise_spec.rb index 98e7a90f7b..5288101752 100644 --- a/spec/models/enterprises_spec.rb +++ b/spec/models/enterprise_spec.rb @@ -106,7 +106,6 @@ describe Enterprise do create(:simple_order_cycle, suppliers: [s], distributors: [d], variants: [p.master], orders_open_at: 1.week.from_now, orders_close_at: 2.weeks.from_now) Enterprise.active_distributors.should be_empty end - end describe "with_distributed_active_products_on_hand" do @@ -160,6 +159,40 @@ describe Enterprise do end end + describe "supplying_variant_in" do + it "finds producers by supply of master variant" do + s = create(:supplier_enterprise) + p = create(:simple_product, supplier: s) + + Enterprise.supplying_variant_in([p.master]).should == [s] + end + + it "finds producers by supply of variant" do + s = create(:supplier_enterprise) + p = create(:simple_product, supplier: s) + v = create(:variant, product: p) + + Enterprise.supplying_variant_in([v]).should == [s] + end + + it "returns multiple enterprises when given multiple variants" do + s1 = create(:supplier_enterprise) + s2 = create(:supplier_enterprise) + p1 = create(:simple_product, supplier: s1) + p2 = create(:simple_product, supplier: s2) + + Enterprise.supplying_variant_in([p1.master, p2.master]).sort.should == [s1, s2].sort + end + + it "does not return duplicates" do + s = create(:supplier_enterprise) + p1 = create(:simple_product, supplier: s) + p2 = create(:simple_product, supplier: s) + + Enterprise.supplying_variant_in([p1.master, p2.master]).should == [s] + end + end + describe "distributing_product" do it "returns enterprises distributing via a product distribution" do d = create(:distributor_enterprise) diff --git a/spec/models/exchange_spec.rb b/spec/models/exchange_spec.rb index 675f7d475a..403fceba87 100644 --- a/spec/models/exchange_spec.rb +++ b/spec/models/exchange_spec.rb @@ -49,9 +49,8 @@ describe Exchange do let(:coordinator) { create(:distributor_enterprise) } let(:distributor) { create(:distributor_enterprise) } let(:oc) { create(:simple_order_cycle, coordinator: coordinator) } - - let(:incoming_exchange) { oc.exchanges.create! sender: supplier, receiver: coordinator } - let(:outgoing_exchange) { oc.exchanges.create! sender: coordinator, receiver: distributor } + let(:incoming_exchange) { oc.exchanges.create! sender: supplier, receiver: coordinator, incoming: true } + let(:outgoing_exchange) { oc.exchanges.create! sender: coordinator, receiver: distributor, incoming: false } it "returns true for incoming exchanges" do incoming_exchange.should be_incoming @@ -78,29 +77,77 @@ describe Exchange do describe "scopes" do let(:supplier) { create(:supplier_enterprise) } - let(:coordinator) { create(:distributor_enterprise) } + let(:coordinator) { create(:distributor_enterprise, is_primary_producer: true) } let(:distributor) { create(:distributor_enterprise) } let(:oc) { create(:simple_order_cycle, coordinator: coordinator) } - let!(:incoming_exchange) { oc.exchanges.create! sender: supplier, receiver: coordinator } - let!(:outgoing_exchange) { oc.exchanges.create! sender: coordinator, receiver: distributor } - - it "finds incoming exchanges" do - Exchange.incoming.should == [incoming_exchange] + it "finds exchanges in a particular order cycle" do + ex = create(:exchange, order_cycle: oc) + Exchange.in_order_cycle(oc).should == [ex] end - it "finds outgoing exchanges" do - Exchange.outgoing.should == [outgoing_exchange] + describe "finding exchanges by direction" do + let!(:incoming_exchange) { oc.exchanges.create! sender: supplier, receiver: coordinator, incoming: true } + let!(:outgoing_exchange) { oc.exchanges.create! sender: coordinator, receiver: distributor, incoming: false } + + it "finds incoming exchanges" do + Exchange.incoming.should == [incoming_exchange] + end + + it "finds outgoing exchanges" do + Exchange.outgoing.should == [outgoing_exchange] + end + + it "correctly determines direction of exchanges between the same enterprise" do + incoming_exchange.update_attributes sender: coordinator, incoming: true + outgoing_exchange.update_attributes receiver: coordinator, incoming: false + Exchange.incoming.should == [incoming_exchange] + Exchange.outgoing.should == [outgoing_exchange] + end + + it "finds exchanges coming from an enterprise" do + Exchange.from_enterprise(supplier).should == [incoming_exchange] + Exchange.from_enterprise(coordinator).should == [outgoing_exchange] + end + + it "finds exchanges going to an enterprise" do + Exchange.to_enterprise(coordinator).should == [incoming_exchange] + Exchange.to_enterprise(distributor).should == [outgoing_exchange] + end + + it "finds exchanges coming from any of a number of enterprises" do + Exchange.from_enterprises([coordinator]).should == [outgoing_exchange] + Exchange.from_enterprises([supplier, coordinator]).sort.should == [incoming_exchange, outgoing_exchange].sort + end + + it "finds exchanges going to any of a number of enterprises" do + Exchange.to_enterprises([coordinator]).should == [incoming_exchange] + Exchange.to_enterprises([coordinator, distributor]).sort.should == [incoming_exchange, outgoing_exchange].sort + end end - it "finds exchanges going to any of a number of enterprises" do - Exchange.to_enterprises([coordinator]).should == [incoming_exchange] - Exchange.to_enterprises([coordinator, distributor]).sort.should == [incoming_exchange, outgoing_exchange].sort - end + describe "finding exchanges supplying to a distributor" do + it "returns incoming exchanges" do + d = create(:distributor_enterprise) + ex = create(:exchange, order_cycle: oc, incoming: true) - it "finds exchanges coming from any of a number of enterprises" do - Exchange.from_enterprises([coordinator]).should == [outgoing_exchange] - Exchange.from_enterprises([supplier, coordinator]).sort.should == [incoming_exchange, outgoing_exchange].sort + oc.exchanges.supplying_to(d).should == [ex] + end + + it "returns outgoing exchanges to the distributor" do + d = create(:distributor_enterprise) + ex = create(:exchange, order_cycle: oc, receiver: d, incoming: false) + + oc.exchanges.supplying_to(d).should == [ex] + end + + it "does not return outgoing exchanges to a different distributor" do + d1 = create(:distributor_enterprise) + d2 = create(:distributor_enterprise) + ex = create(:exchange, order_cycle: oc, receiver: d1, incoming: false) + + oc.exchanges.supplying_to(d2).should be_empty + end end it "finds exchanges with a particular variant" do @@ -167,6 +214,7 @@ describe Exchange do exchange.to_h.should == {'id' => exchange.id, 'order_cycle_id' => oc.id, 'sender_id' => exchange.sender_id, 'receiver_id' => exchange.receiver_id, + 'incoming' => exchange.incoming, 'payment_enterprise_id' => exchange.payment_enterprise_id, 'variant_ids' => exchange.variant_ids.sort, 'enterprise_fee_ids' => exchange.enterprise_fee_ids.sort, 'pickup_time' => exchange.pickup_time, 'pickup_instructions' => exchange.pickup_instructions, @@ -176,6 +224,7 @@ describe Exchange do it "converts to a hash of core attributes only" do exchange.to_h(true).should == {'sender_id' => exchange.sender_id, 'receiver_id' => exchange.receiver_id, + 'incoming' => exchange.incoming, 'payment_enterprise_id' => exchange.payment_enterprise_id, 'variant_ids' => exchange.variant_ids.sort, 'enterprise_fee_ids' => exchange.enterprise_fee_ids.sort, 'pickup_time' => exchange.pickup_time, 'pickup_instructions' => exchange.pickup_instructions} diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index 784d778682..2f84879d40 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -82,7 +82,7 @@ describe OrderCycle do p = create(:product) s = create(:supplier_enterprise) oc = create(:simple_order_cycle) - ex = create(:exchange, order_cycle: oc, sender: s, receiver: oc.coordinator) + ex = create(:exchange, order_cycle: oc, sender: s, receiver: oc.coordinator, incoming: true) ex.variants << p.master p.reload @@ -137,9 +137,9 @@ describe OrderCycle do it "reports its suppliers" do oc = create(:simple_order_cycle) - e1 = create(:exchange, + e1 = create(:exchange, incoming: true, order_cycle: oc, receiver: oc.coordinator, sender: create(:enterprise)) - e2 = create(:exchange, + e2 = create(:exchange, incoming: true, order_cycle: oc, receiver: oc.coordinator, sender: create(:enterprise)) oc.suppliers.sort.should == [e1.sender, e2.sender].sort @@ -148,9 +148,9 @@ describe OrderCycle do it "reports its distributors" do oc = create(:simple_order_cycle) - e1 = create(:exchange, + e1 = create(:exchange, incoming: false, order_cycle: oc, sender: oc.coordinator, receiver: create(:enterprise)) - e2 = create(:exchange, + e2 = create(:exchange, incoming: false, order_cycle: oc, sender: oc.coordinator, receiver: create(:enterprise)) oc.distributors.sort.should == [e1.receiver, e2.receiver].sort @@ -160,7 +160,7 @@ describe OrderCycle do oc = create(:simple_order_cycle) d1 = create(:distributor_enterprise) d2 = create(:distributor_enterprise) - create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: d1) + create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: d1, incoming: false) oc.should have_distributor(d1) oc.should_not have_distributor(d2) @@ -182,11 +182,11 @@ describe OrderCycle do @d1 = create(:enterprise) @d2 = create(:enterprise) - @e0 = create(:exchange, + @e0 = create(:exchange, incoming: true, order_cycle: @oc, sender: create(:enterprise), receiver: @oc.coordinator) - @e1 = create(:exchange, + @e1 = create(:exchange, incoming: false, order_cycle: @oc, sender: @oc.coordinator, receiver: @d1) - @e2 = create(:exchange, + @e2 = create(:exchange, incoming: false, order_cycle: @oc, sender: @oc.coordinator, receiver: @d2) @p0 = create(:simple_product) @@ -281,9 +281,9 @@ describe OrderCycle do @d1 = create(:enterprise) @d2 = create(:enterprise, next_collection_at: '2-8pm Friday') - @e0 = create(:exchange, order_cycle: @oc, sender: create(:enterprise), receiver: @oc.coordinator) - @e1 = create(:exchange, order_cycle: @oc, sender: @oc.coordinator, receiver: @d1, pickup_time: '5pm Tuesday', pickup_instructions: "Come get it!") - @e2 = create(:exchange, order_cycle: @oc, sender: @oc.coordinator, receiver: @d2, pickup_time: nil) + @e0 = create(:exchange, order_cycle: @oc, sender: create(:enterprise), receiver: @oc.coordinator, incoming: true) + @e1 = create(:exchange, order_cycle: @oc, sender: @oc.coordinator, receiver: @d1, incoming: false, pickup_time: '5pm Tuesday', pickup_instructions: "Come get it!") + @e2 = create(:exchange, order_cycle: @oc, sender: @oc.coordinator, receiver: @d2, incoming: false, pickup_time: nil) end it "finds the exchange for a distributor" do @@ -377,7 +377,7 @@ describe OrderCycle do calculator: Spree::Calculator::FlatRate.new(preferred_amount: 2)) product = create(:simple_product) - create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, + create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, incoming: false, enterprise_fees: [enterprise_fee1, enterprise_fee2, enterprise_fee3], variants: [product.master]) order_cycle.fees_for(product.master, distributor).should == 23 @@ -391,7 +391,7 @@ describe OrderCycle do enterprise_fee1 = create(:enterprise_fee, amount: 20, fee_type: "admin", calculator: Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 20)) product = create(:simple_product, price: 10.00) - create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, + create(:exchange, order_cycle: order_cycle, sender: coordinator, receiver: distributor, incoming: false, enterprise_fees: [enterprise_fee1], variants: [product.master]) product.master.price.should == 10.00 diff --git a/spec/models/spree/order_spec.rb b/spec/models/spree/order_spec.rb index 80a65b79cc..18ad347dc2 100644 --- a/spec/models/spree/order_spec.rb +++ b/spec/models/spree/order_spec.rb @@ -151,7 +151,7 @@ describe Spree::Order do it "keeps the order cycle when it is available at the new distributor" do d = create(:distributor_enterprise) oc = create(:simple_order_cycle) - create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: d) + create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: d, incoming: false) subject.order_cycle = oc subject.set_distributor! d @@ -190,7 +190,7 @@ describe Spree::Order do it "keeps the distributor when it is available in the new order cycle" do oc = create(:simple_order_cycle) d = create(:distributor_enterprise) - create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: d) + create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: d, incoming: false) subject.distributor = d subject.set_order_cycle! oc diff --git a/spec/models/spree/product_spec.rb b/spec/models/spree/product_spec.rb index 813a57558f..6743e404e9 100644 --- a/spec/models/spree/product_spec.rb +++ b/spec/models/spree/product_spec.rb @@ -318,11 +318,11 @@ module Spree let(:v_external) { create(:variant, product: p_external) } let!(:ex_in) { create(:exchange, order_cycle: oc, sender: s, receiver: oc.coordinator, - variants: [v1, v2]) } + incoming: true, variants: [v1, v2]) } let!(:ex_out1) { create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: d1, - variants: [v1]) } + incoming: false, variants: [v1]) } let!(:ex_out2) { create(:exchange, order_cycle: oc, sender: oc.coordinator, receiver: d2, - variants: [v2]) } + incoming: false, variants: [v2]) } it "returns variants in the order cycle and distributor" do p1.variants_for(oc, d1).should == [v1] diff --git a/vendor/assets/javascripts/mm-foundation-tpls-0.1.0.min.js b/vendor/assets/javascripts/mm-foundation-tpls-0.1.0.min.js new file mode 100644 index 0000000000..5fa09b885a --- /dev/null +++ b/vendor/assets/javascripts/mm-foundation-tpls-0.1.0.min.js @@ -0,0 +1,9 @@ +/* + * angular-mm-foundation + * http://madmimi.github.io/angular-foundation/ + + * Version: 0.1.0 - 2014-02-05 + * License: MIT + */ +angular.module("mm.foundation",["mm.foundation.tpls","mm.foundation.accordion","mm.foundation.alert","mm.foundation.bindHtml","mm.foundation.buttons","mm.foundation.position","mm.foundation.dropdownToggle","mm.foundation.transition","mm.foundation.modal","mm.foundation.pagination","mm.foundation.tooltip","mm.foundation.popover","mm.foundation.progressbar","mm.foundation.rating","mm.foundation.tabs","mm.foundation.tour","mm.foundation.typeahead"]),angular.module("mm.foundation.tpls",["template/accordion/accordion-group.html","template/accordion/accordion.html","template/alert/alert.html","template/modal/backdrop.html","template/modal/window.html","template/pagination/pager.html","template/pagination/pagination.html","template/tooltip/tooltip-html-unsafe-popup.html","template/tooltip/tooltip-popup.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/progressbar/progressbar.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset.html","template/tour/tour.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]),angular.module("mm.foundation.accordion",[]).constant("accordionConfig",{closeOthers:!0}).controller("AccordionController",["$scope","$attrs","accordionConfig",function(a,b,c){this.groups=[],this.closeOthers=function(d){var e=angular.isDefined(b.closeOthers)?a.$eval(b.closeOthers):c.closeOthers;e&&angular.forEach(this.groups,function(a){a!==d&&(a.isOpen=!1)})},this.addGroup=function(a){var b=this;this.groups.push(a),a.$on("$destroy",function(){b.removeGroup(a)})},this.removeGroup=function(a){var b=this.groups.indexOf(a);-1!==b&&this.groups.splice(this.groups.indexOf(a),1)}}]).directive("accordion",function(){return{restrict:"EA",controller:"AccordionController",transclude:!0,replace:!1,templateUrl:"template/accordion/accordion.html"}}).directive("accordionGroup",["$parse",function(a){return{require:"^accordion",restrict:"EA",transclude:!0,replace:!0,templateUrl:"template/accordion/accordion-group.html",scope:{heading:"@"},controller:function(){this.setHeading=function(a){this.heading=a}},link:function(b,c,d,e){var f,g;e.addGroup(b),b.isOpen=!1,d.isOpen&&(f=a(d.isOpen),g=f.assign,b.$parent.$watch(f,function(a){b.isOpen=!!a})),b.$watch("isOpen",function(a){a&&e.closeOthers(b),g&&g(b.$parent,a)})}}}]).directive("accordionHeading",function(){return{restrict:"EA",transclude:!0,template:"",replace:!0,require:"^accordionGroup",compile:function(a,b,c){return function(a,b,d,e){e.setHeading(c(a,function(){}))}}}}).directive("accordionTransclude",function(){return{require:"^accordionGroup",link:function(a,b,c,d){a.$watch(function(){return d[c.accordionTransclude]},function(a){a&&(b.html(""),b.append(a))})}}}),angular.module("mm.foundation.alert",[]).controller("AlertController",["$scope","$attrs",function(a,b){a.closeable="close"in b}]).directive("alert",function(){return{restrict:"EA",controller:"AlertController",templateUrl:"template/alert/alert.html",transclude:!0,replace:!0,scope:{type:"=",close:"&"}}}),angular.module("mm.foundation.bindHtml",[]).directive("bindHtmlUnsafe",function(){return function(a,b,c){b.addClass("ng-binding").data("$binding",c.bindHtmlUnsafe),a.$watch(c.bindHtmlUnsafe,function(a){b.html(a||"")})}}),angular.module("mm.foundation.buttons",[]).constant("buttonConfig",{activeClass:"active",toggleEvent:"click"}).controller("ButtonsController",["buttonConfig",function(a){this.activeClass=a.activeClass,this.toggleEvent=a.toggleEvent}]).directive("btnRadio",function(){return{require:["btnRadio","ngModel"],controller:"ButtonsController",link:function(a,b,c,d){var e=d[0],f=d[1];f.$render=function(){b.toggleClass(e.activeClass,angular.equals(f.$modelValue,a.$eval(c.btnRadio)))},b.bind(e.toggleEvent,function(){b.hasClass(e.activeClass)||a.$apply(function(){f.$setViewValue(a.$eval(c.btnRadio)),f.$render()})})}}}).directive("btnCheckbox",function(){return{require:["btnCheckbox","ngModel"],controller:"ButtonsController",link:function(a,b,c,d){function e(){return g(c.btnCheckboxTrue,!0)}function f(){return g(c.btnCheckboxFalse,!1)}function g(b,c){var d=a.$eval(b);return angular.isDefined(d)?d:c}var h=d[0],i=d[1];i.$render=function(){b.toggleClass(h.activeClass,angular.equals(i.$modelValue,e()))},b.bind(h.toggleEvent,function(){a.$apply(function(){i.$setViewValue(b.hasClass(h.activeClass)?f():e()),i.$render()})})}}}),angular.module("mm.foundation.position",[]).factory("$position",["$document","$window",function(a,b){function c(a,c){return a.currentStyle?a.currentStyle[c]:b.getComputedStyle?b.getComputedStyle(a)[c]:a.style[c]}function d(a){return"static"===(c(a,"position")||"static")}var e=function(b){for(var c=a[0],e=b.offsetParent||c;e&&e!==c&&d(e);)e=e.offsetParent;return e||c};return{position:function(b){var c=this.offset(b),d={top:0,left:0},f=e(b[0]);f!=a[0]&&(d=this.offset(angular.element(f)),d.top+=f.clientTop-f.scrollTop,d.left+=f.clientLeft-f.scrollLeft);var g=b[0].getBoundingClientRect();return{width:g.width||b.prop("offsetWidth"),height:g.height||b.prop("offsetHeight"),top:c.top-d.top,left:c.left-d.left}},offset:function(c){var d=c[0].getBoundingClientRect();return{width:d.width||c.prop("offsetWidth"),height:d.height||c.prop("offsetHeight"),top:d.top+(b.pageYOffset||a[0].body.scrollTop||a[0].documentElement.scrollTop),left:d.left+(b.pageXOffset||a[0].body.scrollLeft||a[0].documentElement.scrollLeft)}}}}]),angular.module("mm.foundation.dropdownToggle",["mm.foundation.position"]).directive("dropdownToggle",["$document","$location","$position",function(a,b,c){var d=null,e=angular.noop;return{restrict:"CA",scope:{dropdownToggle:"@"},link:function(b,f){var g=angular.element(a[0].querySelector(b.dropdownToggle));b.$watch("$location.path",function(){e()}),g.css("display","none").bind("click",function(){e()}),f.bind("click",function(b){var h=f===d;if(b.preventDefault(),b.stopPropagation(),d&&e(),!h&&!f.hasClass("disabled")&&!f.prop("disabled")){g.css("display","block");var i=c.offset(f),j=c.offset(angular.element(g[0].offsetParent));g.css({left:i.left-j.left+"px",top:i.top-j.top+i.height+"px"}),d=f,e=function(b){b&&(b.preventDefault(),b.stopPropagation()),a.unbind("click",e),g.css("display","none"),e=angular.noop,d=null},a.bind("click",e)}})}}}]),angular.module("mm.foundation.transition",[]).factory("$transition",["$q","$timeout","$rootScope",function(a,b,c){function d(a){for(var b in a)if(void 0!==f.style[b])return a[b]}var e=function(d,f,g){g=g||{};var h=a.defer(),i=e[g.animation?"animationEndEventName":"transitionEndEventName"],j=function(){c.$apply(function(){d.unbind(i,j),h.resolve(d)})};return i&&d.bind(i,j),b(function(){angular.isString(f)?d.addClass(f):angular.isFunction(f)?f(d):angular.isObject(f)&&d.css(f),i||h.resolve(d)}),h.promise.cancel=function(){i&&d.unbind(i,j),h.reject("Transition cancelled")},h.promise},f=document.createElement("trans"),g={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",transition:"transitionend"},h={WebkitTransition:"webkitAnimationEnd",MozTransition:"animationend",OTransition:"oAnimationEnd",transition:"animationend"};return e.transitionEndEventName=d(g),e.animationEndEventName=d(h),e}]),angular.module("mm.foundation.modal",["mm.foundation.transition"]).factory("$$stackedMap",function(){return{createNew:function(){var a=[];return{add:function(b,c){a.push({key:b,value:c})},get:function(b){for(var c=0;c0)}function i(){if(k&&-1==g()){var a=l;j(k,l,150,function(){a.$destroy(),a=null}),k=void 0,l=void 0}}function j(c,d,e,f){function g(){g.done||(g.done=!0,c.remove(),f&&f())}d.animate=!1;var h=a.transitionEndEventName;if(h){var i=b(g,e);c.bind(h,function(){b.cancel(i),g(),d.$apply()})}else b(g,0)}var k,l,m="modal-open",n=f.createNew(),o={};return e.$watch(g,function(a){l&&(l.index=a)}),c.bind("keydown",function(a){var b;27===a.which&&(b=n.top(),b&&b.value.keyboard&&e.$apply(function(){o.dismiss(b.key)}))}),o.open=function(a,b){n.add(a,{deferred:b.deferred,modalScope:b.scope,backdrop:b.backdrop,keyboard:b.keyboard});var f=c.find("body").eq(0),h=g();h>=0&&!k&&(l=e.$new(!0),l.index=h,k=d("
")(l),f.append(k));var i=angular.element("
");i.attr("window-class",b.windowClass),i.attr("index",n.length()-1),i.attr("animate","animate"),i.html(b.content);var j=d(i)(b.scope);n.top().value.modalDomEl=j,f.append(j),f.addClass(m)},o.close=function(a,b){var c=n.get(a).value;c&&(c.deferred.resolve(b),h(a))},o.dismiss=function(a,b){var c=n.get(a).value;c&&(c.deferred.reject(b),h(a))},o.dismissAll=function(a){for(var b=this.getTop();b;)this.dismiss(b.key,a),b=this.getTop()},o.getTop=function(){return n.top()},o}]).provider("$modal",function(){var a={options:{backdrop:!0,keyboard:!0},$get:["$injector","$rootScope","$q","$http","$templateCache","$controller","$modalStack",function(b,c,d,e,f,g,h){function i(a){return a.template?d.when(a.template):e.get(a.templateUrl,{cache:f}).then(function(a){return a.data})}function j(a){var c=[];return angular.forEach(a,function(a){(angular.isFunction(a)||angular.isArray(a))&&c.push(d.when(b.invoke(a)))}),c}var k={};return k.open=function(b){var e=d.defer(),f=d.defer(),k={result:e.promise,opened:f.promise,close:function(a){h.close(k,a)},dismiss:function(a){h.dismiss(k,a)}};if(b=angular.extend({},a.options,b),b.resolve=b.resolve||{},!b.template&&!b.templateUrl)throw new Error("One of template or templateUrl options is required.");var l=d.all([i(b)].concat(j(b.resolve)));return l.then(function(a){var d=(b.scope||c).$new();d.$close=k.close,d.$dismiss=k.dismiss;var f,i={},j=1;b.controller&&(i.$scope=d,i.$modalInstance=k,angular.forEach(b.resolve,function(b,c){i[c]=a[j++]}),f=g(b.controller,i)),h.open(k,{scope:d,deferred:e,content:a[0],backdrop:b.backdrop,keyboard:b.keyboard,windowClass:b.windowClass})},function(a){e.reject(a)}),l.then(function(){f.resolve(!0)},function(){f.reject(!1)}),k},k}]};return a}),angular.module("mm.foundation.pagination",[]).controller("PaginationController",["$scope","$attrs","$parse","$interpolate",function(a,b,c,d){var e=this,f=b.numPages?c(b.numPages).assign:angular.noop;this.init=function(d){b.itemsPerPage?a.$parent.$watch(c(b.itemsPerPage),function(b){e.itemsPerPage=parseInt(b,10),a.totalPages=e.calculateTotalPages()}):this.itemsPerPage=d},this.noPrevious=function(){return 1===this.page},this.noNext=function(){return this.page===a.totalPages},this.isActive=function(a){return this.page===a},this.calculateTotalPages=function(){var b=this.itemsPerPage<1?1:Math.ceil(a.totalItems/this.itemsPerPage);return Math.max(b||0,1)},this.getAttributeValue=function(b,c,e){return angular.isDefined(b)?e?d(b)(a.$parent):a.$parent.$eval(b):c},this.render=function(){this.page=parseInt(a.page,10)||1,this.page>0&&this.page<=a.totalPages&&(a.pages=this.getPages(this.page,a.totalPages))},a.selectPage=function(b){!e.isActive(b)&&b>0&&b<=a.totalPages&&(a.page=b,a.onSelectPage({page:b}))},a.$watch("page",function(){e.render()}),a.$watch("totalItems",function(){a.totalPages=e.calculateTotalPages()}),a.$watch("totalPages",function(b){f(a.$parent,b),e.page>b?a.selectPage(b):e.render()})}]).constant("paginationConfig",{itemsPerPage:10,boundaryLinks:!1,directionLinks:!0,firstText:"First",previousText:"Previous",nextText:"Next",lastText:"Last",rotate:!0}).directive("pagination",["$parse","paginationConfig",function(a,b){return{restrict:"EA",scope:{page:"=",totalItems:"=",onSelectPage:" &"},controller:"PaginationController",templateUrl:"template/pagination/pagination.html",replace:!0,link:function(c,d,e,f){function g(a,b,c,d){return{number:a,text:b,active:c,disabled:d}}var h,i=f.getAttributeValue(e.boundaryLinks,b.boundaryLinks),j=f.getAttributeValue(e.directionLinks,b.directionLinks),k=f.getAttributeValue(e.firstText,b.firstText,!0),l=f.getAttributeValue(e.previousText,b.previousText,!0),m=f.getAttributeValue(e.nextText,b.nextText,!0),n=f.getAttributeValue(e.lastText,b.lastText,!0),o=f.getAttributeValue(e.rotate,b.rotate);f.init(b.itemsPerPage),e.maxSize&&c.$parent.$watch(a(e.maxSize),function(a){h=parseInt(a,10),f.render()}),f.getPages=function(a,b){var c=[],d=1,e=b,p=angular.isDefined(h)&&b>h;p&&(o?(d=Math.max(a-Math.floor(h/2),1),e=d+h-1,e>b&&(e=b,d=e-h+1)):(d=(Math.ceil(a/h)-1)*h+1,e=Math.min(d+h-1,b)));for(var q=d;e>=q;q++){var r=g(q,q,f.isActive(q),!1);c.push(r)}if(p&&!o){if(d>1){var s=g(d-1,"...",!1,!1);c.unshift(s)}if(b>e){var t=g(e+1,"...",!1,!1);c.push(t)}}if(j){var u=g(a-1,l,!1,f.noPrevious());c.unshift(u);var v=g(a+1,m,!1,f.noNext());c.push(v)}if(i){var w=g(1,k,!1,f.noPrevious());c.unshift(w);var x=g(b,n,!1,f.noNext());c.push(x)}return c}}}}]).constant("pagerConfig",{itemsPerPage:10,previousText:"« Previous",nextText:"Next »",align:!0}).directive("pager",["pagerConfig",function(a){return{restrict:"EA",scope:{page:"=",totalItems:"=",onSelectPage:" &"},controller:"PaginationController",templateUrl:"template/pagination/pager.html",replace:!0,link:function(b,c,d,e){function f(a,b,c,d,e){return{number:a,text:b,disabled:c,previous:i&&d,next:i&&e}}var g=e.getAttributeValue(d.previousText,a.previousText,!0),h=e.getAttributeValue(d.nextText,a.nextText,!0),i=e.getAttributeValue(d.align,a.align);e.init(a.itemsPerPage),e.getPages=function(a){return[f(a-1,g,e.noPrevious(),!0,!1),f(a+1,h,e.noNext(),!1,!0)]}}}}]),angular.module("mm.foundation.tooltip",["mm.foundation.position","mm.foundation.bindHtml"]).provider("$tooltip",function(){function a(a){var b=/[A-Z]/g,c="-";return a.replace(b,function(a,b){return(b?c:"")+a.toLowerCase()})}var b={placement:"top",animation:!0,popupDelay:0},c={mouseenter:"mouseleave",click:"click",focus:"blur"},d={};this.options=function(a){angular.extend(d,a)},this.setTriggers=function(a){angular.extend(c,a)},this.$get=["$window","$compile","$timeout","$parse","$document","$position","$interpolate",function(e,f,g,h,i,j,k){return function(e,l,m){function n(a){var b=a||o.trigger||m,d=c[b]||b;return{show:b,hide:d}}var o=angular.extend({},b,d),p=a(e),q=k.startSymbol(),r=k.endSymbol(),s="
';return{restrict:"EA",scope:!0,compile:function(){var a=f(s);return function(b,c,d){function f(){b.tt_isOpen?m():k()}function k(){(!z||b.$eval(d[l+"Enable"]))&&(b.tt_popupDelay?(v=g(p,b.tt_popupDelay,!1),v.then(function(a){a()})):p()())}function m(){b.$apply(function(){q()})}function p(){return b.tt_content?(r(),u&&g.cancel(u),t.css({top:0,left:0,display:"block"}),w?i.find("body").append(t):c.after(t),A(),b.tt_isOpen=!0,b.$digest(),A):angular.noop}function q(){b.tt_isOpen=!1,g.cancel(v),b.tt_animation?u=g(s,500):s()}function r(){t&&s(),t=a(b,function(){}),b.$digest()}function s(){t&&(t.remove(),t=null)}var t,u,v,w=angular.isDefined(o.appendToBody)?o.appendToBody:!1,x=n(void 0),y=!1,z=angular.isDefined(d[l+"Enable"]),A=function(){var a,d,e,f;switch(a=w?j.offset(c):j.position(c),d=t.prop("offsetWidth"),e=t.prop("offsetHeight"),b.tt_placement){case"right":f={top:a.top+a.height/2-e/2,left:a.left+a.width+10};break;case"bottom":f={top:a.top+a.height+10,left:a.left};break;case"left":f={top:a.top+a.height/2-e/2,left:a.left-d-10};break;default:f={top:a.top-e-10,left:a.left}}f.top+="px",f.left+="px",t.css(f)};b.tt_isOpen=!1,d.$observe(e,function(a){b.tt_content=a,!a&&b.tt_isOpen&&q()}),d.$observe(l+"Title",function(a){b.tt_title=a}),d.$observe(l+"Placement",function(a){b.tt_placement=angular.isDefined(a)?a:o.placement}),d.$observe(l+"PopupDelay",function(a){var c=parseInt(a,10);b.tt_popupDelay=isNaN(c)?o.popupDelay:c});var B=function(){y&&(c.unbind(x.show,k),c.unbind(x.hide,m))},C=function(){};d.$observe(l+"Trigger",function(a){B(),C(),x=n(a),angular.isFunction(x.show)?C=b.$watch(function(){return x.show(b,c,d)},function(a){return g(a?p:q)}):x.show===x.hide?c.bind(x.show,f):(c.bind(x.show,k),c.bind(x.hide,m)),y=!0});var D=b.$eval(d[l+"Animation"]);b.tt_animation=angular.isDefined(D)?!!D:o.animation,d.$observe(l+"AppendToBody",function(a){w=angular.isDefined(a)?h(a)(b):w}),w&&b.$on("$locationChangeSuccess",function(){b.tt_isOpen&&q()}),b.$on("$destroy",function(){g.cancel(u),g.cancel(v),B(),C(),s()})}}}}}]}).directive("tooltipPopup",function(){return{restrict:"EA",replace:!0,scope:{content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-popup.html"}}).directive("tooltip",["$tooltip",function(a){return a("tooltip","tooltip","mouseenter")}]).directive("tooltipHtmlUnsafePopup",function(){return{restrict:"EA",replace:!0,scope:{content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-html-unsafe-popup.html"}}).directive("tooltipHtmlUnsafe",["$tooltip",function(a){return a("tooltipHtmlUnsafe","tooltip","mouseenter")}]),angular.module("mm.foundation.popover",["mm.foundation.tooltip"]).directive("popoverPopup",function(){return{restrict:"EA",replace:!0,scope:{title:"@",content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/popover/popover.html"}}).directive("popover",["$tooltip",function(a){return a("popover","popover","click")}]),angular.module("mm.foundation.progressbar",["mm.foundation.transition"]).constant("progressConfig",{animate:!0,max:100}).controller("ProgressController",["$scope","$attrs","progressConfig","$transition",function(a,b,c,d){var e=this,f=[],g=angular.isDefined(b.max)?a.$parent.$eval(b.max):c.max,h=angular.isDefined(b.animate)?a.$parent.$eval(b.animate):c.animate;this.addBar=function(a,b){var c=0,d=a.$parent.$index;angular.isDefined(d)&&f[d]&&(c=f[d].value),f.push(a),this.update(b,a.value,c),a.$watch("value",function(a,c){a!==c&&e.update(b,a,c)}),a.$on("$destroy",function(){e.removeBar(a)})},this.update=function(a,b,c){var e=this.getPercentage(b);h?(a.css("width",this.getPercentage(c)+"%"),d(a,{width:e+"%"})):a.css({transition:"none",width:e+"%"})},this.removeBar=function(a){f.splice(f.indexOf(a),1)},this.getPercentage=function(a){return Math.round(100*a/g)}}]).directive("progress",function(){return{restrict:"EA",replace:!0,transclude:!0,controller:"ProgressController",require:"progress",scope:{},template:'
'}}).directive("bar",function(){return{restrict:"EA",replace:!0,transclude:!0,require:"^progress",scope:{value:"=",type:"@"},templateUrl:"template/progressbar/bar.html",link:function(a,b,c,d){d.addBar(a,b)}}}).directive("progressbar",function(){return{restrict:"EA",replace:!0,transclude:!0,controller:"ProgressController",scope:{value:"=",type:"@"},templateUrl:"template/progressbar/progressbar.html",link:function(a,b,c,d){d.addBar(a,angular.element(b.children()[0]))}}}),angular.module("mm.foundation.rating",[]).constant("ratingConfig",{max:5,stateOn:null,stateOff:null}).controller("RatingController",["$scope","$attrs","$parse","ratingConfig",function(a,b,c,d){this.maxRange=angular.isDefined(b.max)?a.$parent.$eval(b.max):d.max,this.stateOn=angular.isDefined(b.stateOn)?a.$parent.$eval(b.stateOn):d.stateOn,this.stateOff=angular.isDefined(b.stateOff)?a.$parent.$eval(b.stateOff):d.stateOff,this.createRateObjects=function(a){for(var b={stateOn:this.stateOn,stateOff:this.stateOff},c=0,d=a.length;d>c;c++)a[c]=angular.extend({index:c},b,a[c]);return a},a.range=this.createRateObjects(angular.isDefined(b.ratingStates)?angular.copy(a.$parent.$eval(b.ratingStates)):new Array(this.maxRange)),a.rate=function(b){a.value===b||a.readonly||(a.value=b)},a.enter=function(b){a.readonly||(a.val=b),a.onHover({value:b})},a.reset=function(){a.val=angular.copy(a.value),a.onLeave()},a.$watch("value",function(b){a.val=b}),a.readonly=!1,b.readonly&&a.$parent.$watch(c(b.readonly),function(b){a.readonly=!!b})}]).directive("rating",function(){return{restrict:"EA",scope:{value:"=",onHover:"&",onLeave:"&"},controller:"RatingController",templateUrl:"template/rating/rating.html",replace:!0}}),angular.module("mm.foundation.tabs",[]).controller("TabsetController",["$scope",function(a){var b=this,c=b.tabs=a.tabs=[];b.select=function(a){angular.forEach(c,function(a){a.active=!1}),a.active=!0},b.addTab=function(a){c.push(a),(1===c.length||a.active)&&b.select(a)},b.removeTab=function(a){var d=c.indexOf(a);if(a.active&&c.length>1){var e=d==c.length-1?d-1:d+1;b.select(c[e])}c.splice(d,1)}}]).directive("tabset",function(){return{restrict:"EA",transclude:!0,replace:!0,scope:{},controller:"TabsetController",templateUrl:"template/tabs/tabset.html",link:function(a,b,c){a.vertical=angular.isDefined(c.vertical)?a.$parent.$eval(c.vertical):!1,a.justified=angular.isDefined(c.justified)?a.$parent.$eval(c.justified):!1,a.type=angular.isDefined(c.type)?a.$parent.$eval(c.type):"tabs"}}}).directive("tab",["$parse",function(a){return{require:"^tabset",restrict:"EA",replace:!0,templateUrl:"template/tabs/tab.html",transclude:!0,scope:{heading:"@",onSelect:"&select",onDeselect:"&deselect"},controller:function(){},compile:function(b,c,d){return function(b,c,e,f){var g,h;e.active?(g=a(e.active),h=g.assign,b.$parent.$watch(g,function(a,c){a!==c&&(b.active=!!a)}),b.active=g(b.$parent)):h=g=angular.noop,b.$watch("active",function(a){h(b.$parent,a),a?(f.select(b),b.onSelect()):b.onDeselect()}),b.disabled=!1,e.disabled&&b.$parent.$watch(a(e.disabled),function(a){b.disabled=!!a}),b.select=function(){b.disabled||(b.active=!0)},f.addTab(b),b.$on("$destroy",function(){f.removeTab(b)}),b.$transcludeFn=d}}}}]).directive("tabHeadingTransclude",[function(){return{restrict:"A",require:"^tab",link:function(a,b){a.$watch("headingElement",function(a){a&&(b.html(""),b.append(a))})}}}]).directive("tabContentTransclude",function(){function a(a){return a.tagName&&(a.hasAttribute("tab-heading")||a.hasAttribute("data-tab-heading")||"tab-heading"===a.tagName.toLowerCase()||"data-tab-heading"===a.tagName.toLowerCase())}return{restrict:"A",require:"^tabset",link:function(b,c,d){var e=b.$eval(d.tabContentTransclude);e.$transcludeFn(e.$parent,function(b){angular.forEach(b,function(b){a(b)?e.headingElement=b:c.append(b)})})}}}),angular.module("mm.foundation.tour",["mm.foundation.position","mm.foundation.tooltip"]).service("$tour",["$window",function(a){function b(){return parseInt(a.localStorage.getItem("mm.tour.step"),10)}function c(b){d=b,a.localStorage.setItem("mm.tour.step",b)}var d=b(),e={};this.add=function(a,b){e[a]=b},this.has=function(a){return!!e[a]},this.isActive=function(){return d>0},this.current=function(a){return a?void c(d):d},this.start=function(){c(1)},this.next=function(){c(d+1)},this.end=function(){c(0)}}]).directive("stepPopup",["$tour",function(a){return{restrict:"EA",replace:!0,scope:{title:"@",content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/tour/tour.html",link:function(b,c){b.isLastStep=function(){return!a.has(a.current()+1)},b.endTour=function(){c.remove(),a.end()},b.nextStep=function(){c.remove(),a.next()}}}}]).directive("step",["$position","$tooltip","$tour","$window",function(a,b,c,d){function e(a){var b=a[0].getBoundingClientRect();return b.top>=0&&b.left>=0&&b.bottom<=d.innerHeight-80&&b.right<=d.innerWidth}function f(b,f,g){var h=parseInt(g.stepIndex,10);if(c.isActive()&&h&&(c.add(h,g),h===c.current())){if(!e(f)){var i=a.offset(f);d.scrollTo(0,i.top-d.innerHeight/2)}return!0}return!1}return b("step","step",f)}]),angular.module("mm.foundation.typeahead",["mm.foundation.position","mm.foundation.bindHtml"]).factory("typeaheadParser",["$parse",function(a){var b=/^\s*(.*?)(?:\s+as\s+(.*?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+(.*)$/;return{parse:function(c){var d=c.match(b);if(!d)throw new Error("Expected typeahead specification in form of '_modelValue_ (as _label_)? for _item_ in _collection_' but got '"+c+"'.");return{itemName:d[3],source:a(d[4]),viewMapper:a(d[2]||d[1]),modelMapper:a(d[1])}}}}]).directive("typeahead",["$compile","$parse","$q","$timeout","$document","$position","typeaheadParser",function(a,b,c,d,e,f,g){var h=[9,13,27,38,40];return{require:"ngModel",link:function(i,j,k,l){var m,n=i.$eval(k.typeaheadMinLength)||1,o=i.$eval(k.typeaheadWaitMs)||0,p=i.$eval(k.typeaheadEditable)!==!1,q=b(k.typeaheadLoading).assign||angular.noop,r=b(k.typeaheadOnSelect),s=k.typeaheadInputFormatter?b(k.typeaheadInputFormatter):void 0,t=k.typeaheadAppendToBody?b(k.typeaheadAppendToBody):!1,u=b(k.ngModel).assign,v=g.parse(k.typeahead),w=angular.element("
");w.attr({matches:"matches",active:"activeIdx",select:"select(activeIdx)",query:"query",position:"position"}),angular.isDefined(k.typeaheadTemplateUrl)&&w.attr("template-url",k.typeaheadTemplateUrl);var x=i.$new();i.$on("$destroy",function(){x.$destroy()});var y=function(){x.matches=[],x.activeIdx=-1},z=function(a){var b={$viewValue:a};q(i,!0),c.when(v.source(i,b)).then(function(c){if(a===l.$viewValue&&m){if(c.length>0){x.activeIdx=0,x.matches.length=0;for(var d=0;d=n?o>0?(A&&d.cancel(A),A=d(function(){z(a)},o)):z(a):(q(i,!1),y()),p?a:a?void l.$setValidity("editable",!1):(l.$setValidity("editable",!0),a)}),l.$formatters.push(function(a){var b,c,d={};return s?(d.$model=a,s(i,d)):(d[v.itemName]=a,b=v.viewMapper(i,d),d[v.itemName]=void 0,c=v.viewMapper(i,d),b!==c?b:a)}),x.select=function(a){var b,c,d={};d[v.itemName]=c=x.matches[a].model,b=v.modelMapper(i,d),u(i,b),l.$setValidity("editable",!0),r(i,{$item:c,$model:b,$label:v.viewMapper(i,d)}),y(),j[0].focus()},j.bind("keydown",function(a){0!==x.matches.length&&-1!==h.indexOf(a.which)&&(a.preventDefault(),40===a.which?(x.activeIdx=(x.activeIdx+1)%x.matches.length,x.$digest()):38===a.which?(x.activeIdx=(x.activeIdx?x.activeIdx:x.matches.length)-1,x.$digest()):13===a.which||9===a.which?x.$apply(function(){x.select(x.activeIdx)}):27===a.which&&(a.stopPropagation(),y(),x.$digest()))}),j.bind("blur",function(){m=!1});var B=function(a){j[0]!==a.target&&(y(),x.$digest())};e.bind("click",B),i.$on("$destroy",function(){e.unbind("click",B)});var C=a(w)(x);t?e.find("body").append(C):j.after(C)}}}]).directive("typeaheadPopup",function(){return{restrict:"EA",scope:{matches:"=",query:"=",active:"=",position:"=",select:"&"},replace:!0,templateUrl:"template/typeahead/typeahead-popup.html",link:function(a,b,c){a.templateUrl=c.templateUrl,a.isOpen=function(){return a.matches.length>0},a.isActive=function(b){return a.active==b},a.selectActive=function(b){a.active=b},a.selectMatch=function(b){a.select({activeIdx:b})}}}}).directive("typeaheadMatch",["$http","$templateCache","$compile","$parse",function(a,b,c,d){return{restrict:"EA",scope:{index:"=",match:"=",query:"="},link:function(e,f,g){var h=d(g.templateUrl)(e.$parent)||"template/typeahead/typeahead-match.html";a.get(h,{cache:b}).success(function(a){f.replaceWith(c(a.trim())(e))})}}}]).filter("typeaheadHighlight",function(){function a(a){return a.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")}return function(b,c){return c?b.replace(new RegExp(a(c),"gi"),"$&"):b}}),angular.module("template/accordion/accordion-group.html",[]).run(["$templateCache",function(a){a.put("template/accordion/accordion-group.html",'
\n {{heading}}\n
\n
\n')}]),angular.module("template/accordion/accordion.html",[]).run(["$templateCache",function(a){a.put("template/accordion/accordion.html",'
\n')}]),angular.module("template/alert/alert.html",[]).run(["$templateCache",function(a){a.put("template/alert/alert.html","
\n \n ×\n
\n")}]),angular.module("template/modal/backdrop.html",[]).run(["$templateCache",function(a){a.put("template/modal/backdrop.html",'
\n')}]),angular.module("template/modal/window.html",[]).run(["$templateCache",function(a){a.put("template/modal/window.html",'
\n
\n
\n')}]),angular.module("template/pagination/pager.html",[]).run(["$templateCache",function(a){a.put("template/pagination/pager.html",'\n')}]),angular.module("template/pagination/pagination.html",[]).run(["$templateCache",function(a){a.put("template/pagination/pagination.html",'\n')}]),angular.module("template/tooltip/tooltip-html-unsafe-popup.html",[]).run(["$templateCache",function(a){a.put("template/tooltip/tooltip-html-unsafe-popup.html",'\n \n \n\n')}]),angular.module("template/tooltip/tooltip-popup.html",[]).run(["$templateCache",function(a){a.put("template/tooltip/tooltip-popup.html",'\n \n \n\n')}]),angular.module("template/popover/popover.html",[]).run(["$templateCache",function(a){a.put("template/popover/popover.html",'
\n \n
\n

\n

\n
\n
\n')}]),angular.module("template/progressbar/bar.html",[]).run(["$templateCache",function(a){a.put("template/progressbar/bar.html",'\n') +}]),angular.module("template/progressbar/progress.html",[]).run(["$templateCache",function(a){a.put("template/progressbar/progress.html",'
\n')}]),angular.module("template/progressbar/progressbar.html",[]).run(["$templateCache",function(a){a.put("template/progressbar/progressbar.html",'
\n \n
\n')}]),angular.module("template/rating/rating.html",[]).run(["$templateCache",function(a){a.put("template/rating/rating.html",'\n \n\n')}]),angular.module("template/tabs/tab.html",[]).run(["$templateCache",function(a){a.put("template/tabs/tab.html",'
\n {{heading}}\n
\n')}]),angular.module("template/tabs/tabset.html",[]).run(["$templateCache",function(a){a.put("template/tabs/tabset.html",'
\n
\n
\n
\n
\n
\n
\n
\n')}]),angular.module("template/tour/tour.html",[]).run(["$templateCache",function(a){a.put("template/tour/tour.html",'
\n \n
\n

\n

\n Next\n End\n ×\n
\n
\n')}]),angular.module("template/typeahead/typeahead-match.html",[]).run(["$templateCache",function(a){a.put("template/typeahead/typeahead-match.html",'')}]),angular.module("template/typeahead/typeahead-popup.html",[]).run(["$templateCache",function(a){a.put("template/typeahead/typeahead-popup.html","
    \n"+'
  • \n
    \n
  • \n
\n')}]); \ No newline at end of file