caboose-cms 0.4.151 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (166) hide show
  1. checksums.yaml +8 -8
  2. data/app/assets/javascripts/caboose/admin_products.js +79 -0
  3. data/app/assets/javascripts/caboose/application.js +2 -1
  4. data/app/assets/javascripts/caboose/cart.js +168 -0
  5. data/app/assets/javascripts/caboose/checkout.js +151 -0
  6. data/app/assets/javascripts/caboose/checkout_module.js +312 -0
  7. data/app/assets/javascripts/caboose/checkout_step1.js +179 -0
  8. data/app/assets/javascripts/caboose/checkout_step2.js +39 -0
  9. data/app/assets/javascripts/caboose/checkout_step3.js +34 -0
  10. data/app/assets/javascripts/caboose/checkout_step4.js +97 -0
  11. data/app/assets/javascripts/caboose/main.js +99 -8
  12. data/app/assets/javascripts/caboose/product.js +284 -0
  13. data/app/assets/stylesheets/caboose/admin_products.css +86 -0
  14. data/app/assets/templates/caboose/cart/add_to_cart.jst.ejs +7 -0
  15. data/app/assets/templates/caboose/cart/line_items.jst.ejs +41 -0
  16. data/app/assets/templates/caboose/checkout/address.jst.ejs +53 -0
  17. data/app/assets/templates/caboose/checkout/forms/guest.jst.ejs +8 -0
  18. data/app/assets/templates/caboose/checkout/forms/register.jst.ejs +11 -0
  19. data/app/assets/templates/caboose/checkout/forms/signin.jst.ejs +7 -0
  20. data/app/assets/templates/caboose/checkout/line_items.jst.ejs +31 -0
  21. data/app/assets/templates/caboose/checkout/login.jst.ejs +21 -0
  22. data/app/assets/templates/caboose/checkout/payment.jst.ejs +5 -0
  23. data/app/assets/templates/caboose/checkout/shipping.jst.ejs +18 -0
  24. data/app/assets/templates/caboose/product/images.jst.ejs +8 -0
  25. data/app/assets/templates/caboose/product/options.jst.ejs +19 -0
  26. data/app/controllers/caboose/application_controller.rb +29 -1
  27. data/app/controllers/caboose/cart_controller.rb +52 -0
  28. data/app/controllers/caboose/categories_controller.rb +108 -0
  29. data/app/controllers/caboose/checkout_controller.rb +325 -0
  30. data/app/controllers/caboose/orders_controller.rb +439 -0
  31. data/app/controllers/caboose/product_images_controller.rb +38 -0
  32. data/app/controllers/caboose/products_controller.rb +737 -0
  33. data/app/controllers/caboose/reviews_controller.rb +15 -0
  34. data/app/controllers/caboose/variants_controller.rb +218 -0
  35. data/app/controllers/caboose/vendors_controller.rb +73 -0
  36. data/app/helpers/caboose/application_helper.rb +4 -0
  37. data/app/helpers/caboose/categories_helper.rb +82 -0
  38. data/app/helpers/caboose/checkout_helper.rb +20 -0
  39. data/app/helpers/caboose/products_helper.rb +8 -0
  40. data/app/mailers/caboose/orders_mailer.rb +30 -0
  41. data/app/models/caboose/address.rb +26 -0
  42. data/app/models/caboose/category.rb +89 -0
  43. data/app/models/caboose/category_membership.rb +10 -0
  44. data/app/models/caboose/core_plugin.rb +15 -37
  45. data/app/models/caboose/customization_membership.rb +10 -0
  46. data/app/models/caboose/discount.rb +16 -0
  47. data/app/models/caboose/line_item.rb +81 -0
  48. data/app/models/caboose/message.rb +20 -0
  49. data/app/models/caboose/order.rb +191 -0
  50. data/app/models/caboose/order_discount.rb +10 -0
  51. data/app/models/caboose/order_pdf.rb +78 -0
  52. data/app/models/caboose/payment_processors/authorizenet.rb +53 -0
  53. data/app/models/caboose/payment_processors/base.rb +39 -0
  54. data/app/models/caboose/payment_processors/payscape.rb +94 -0
  55. data/app/models/caboose/product.rb +145 -0
  56. data/app/models/caboose/product_image.rb +62 -0
  57. data/app/models/caboose/product_image_variant.rb +10 -0
  58. data/app/models/caboose/review.rb +13 -0
  59. data/app/models/caboose/schema.rb +205 -1
  60. data/app/models/caboose/search_filter.rb +191 -0
  61. data/app/models/caboose/shipping_calculator.rb +81 -0
  62. data/app/models/caboose/states.rb +61 -52
  63. data/app/models/caboose/tax_calculator.rb +23 -0
  64. data/app/models/caboose/tax_line.rb +9 -0
  65. data/app/models/caboose/variant.rb +99 -0
  66. data/app/models/caboose/vendor.rb +22 -0
  67. data/app/views/caboose/cart/index.html.erb +8 -0
  68. data/app/views/caboose/categories/admin_edit.html.erb +79 -0
  69. data/app/views/caboose/categories/admin_index.html.erb +11 -0
  70. data/app/views/caboose/categories/admin_new.html.erb +62 -0
  71. data/app/views/caboose/checkout/_address_form.html.erb +111 -0
  72. data/app/views/caboose/checkout/_billing_form.html.erb +47 -0
  73. data/app/views/caboose/checkout/_cart.html.erb +52 -0
  74. data/app/views/caboose/checkout/_confirm.html.erb +61 -0
  75. data/app/views/caboose/checkout/_order_discount.html.erb +40 -0
  76. data/app/views/caboose/checkout/_shipping_address.html.erb +10 -0
  77. data/app/views/caboose/checkout/_shipping_method.html.erb +2 -0
  78. data/app/views/caboose/checkout/_shipping_method_form.html.erb +21 -0
  79. data/app/views/caboose/checkout/billing.html.erb +11 -0
  80. data/app/views/caboose/checkout/discount.html.erb +11 -0
  81. data/app/views/caboose/checkout/empty.html.erb +2 -0
  82. data/app/views/caboose/checkout/error.html.erb +2 -0
  83. data/app/views/caboose/checkout/index.html.erb +43 -0
  84. data/app/views/caboose/checkout/login.html.erb +2 -0
  85. data/app/views/caboose/checkout/payment.html.erb +79 -0
  86. data/app/views/caboose/checkout/relay.html.erb +23 -0
  87. data/app/views/caboose/checkout/relay_old.html.erb +12 -0
  88. data/app/views/caboose/checkout/relay_postMessage.html.erb +19 -0
  89. data/app/views/caboose/checkout/shipping.html.erb +15 -0
  90. data/app/views/caboose/checkout/step_four.html.erb +93 -0
  91. data/app/views/caboose/checkout/step_one.html.erb +56 -0
  92. data/app/views/caboose/checkout/step_one_old.html.erb +13 -0
  93. data/app/views/caboose/checkout/step_three.html.erb +23 -0
  94. data/app/views/caboose/checkout/step_two.html.erb +52 -0
  95. data/app/views/caboose/checkout/step_two_old.html.erb +14 -0
  96. data/app/views/caboose/checkout/thanks.html.erb +5 -0
  97. data/app/views/caboose/orders/_admin_footer.html.erb +2 -0
  98. data/app/views/caboose/orders/_admin_header.html.erb +31 -0
  99. data/app/views/caboose/orders/_quickbooks_order.html.erb +0 -0
  100. data/app/views/caboose/orders/admin_delete_form.html.erb +21 -0
  101. data/app/views/caboose/orders/admin_edit.html.erb +271 -0
  102. data/app/views/caboose/orders/admin_index.html.erb +89 -0
  103. data/app/views/caboose/orders/admin_new.html.erb +42 -0
  104. data/app/views/caboose/orders/admin_print.html.erb +72 -0
  105. data/app/views/caboose/orders_mailer/customer_new_order.html.erb +1 -0
  106. data/app/views/caboose/orders_mailer/customer_status_updated.html.erb +1 -0
  107. data/app/views/caboose/orders_mailer/fulfillment_new_order.html.erb +1 -0
  108. data/app/views/caboose/orders_mailer/shipping_order_ready.html.erb +1 -0
  109. data/app/views/caboose/products/_admin_footer.html.erb +2 -0
  110. data/app/views/caboose/products/_admin_header.html.erb +32 -0
  111. data/app/views/caboose/products/_sort_options.html.erb +19 -0
  112. data/app/views/caboose/products/admin_add_upcs.html.erb +58 -0
  113. data/app/views/caboose/products/admin_delete_form.html.erb +21 -0
  114. data/app/views/caboose/products/admin_edit_categories.html.erb +73 -0
  115. data/app/views/caboose/products/admin_edit_category_images.html.erb +233 -0
  116. data/app/views/caboose/products/admin_edit_description.html.erb +38 -0
  117. data/app/views/caboose/products/admin_edit_general.html.erb +104 -0
  118. data/app/views/caboose/products/admin_edit_images.html.erb +236 -0
  119. data/app/views/caboose/products/admin_edit_options.html.erb +51 -0
  120. data/app/views/caboose/products/admin_edit_seo.html.erb +37 -0
  121. data/app/views/caboose/products/admin_edit_variant_columns.html.erb +75 -0
  122. data/app/views/caboose/products/admin_edit_variant_sort_order.html.erb +63 -0
  123. data/app/views/caboose/products/admin_edit_variants.html.erb +171 -0
  124. data/app/views/caboose/products/admin_edit_variants_single.html.erb +68 -0
  125. data/app/views/caboose/products/admin_group_variants.html.erb +433 -0
  126. data/app/views/caboose/products/admin_index.html.erb +95 -0
  127. data/app/views/caboose/products/admin_new.html.erb +41 -0
  128. data/app/views/caboose/products/admin_sort.html copy.erb +155 -0
  129. data/app/views/caboose/products/admin_sort.html.erb +254 -0
  130. data/app/views/caboose/products/details.html.erb +438 -0
  131. data/app/views/caboose/products/index.html.erb +46 -0
  132. data/app/views/caboose/products/not_available.html.erb +35 -0
  133. data/app/views/caboose/variants/admin_edit.html.erb +82 -0
  134. data/app/views/caboose/variants/admin_group.html.erb +184 -0
  135. data/app/views/caboose/variants/admin_new.html.erb +59 -0
  136. data/app/views/caboose/vendors/admin_edit.html.erb +24 -0
  137. data/app/views/caboose/vendors/admin_index.html.erb +30 -0
  138. data/app/views/caboose/vendors/admin_new.html.erb +34 -0
  139. data/app/views/layouts/caboose/store/_banner.html.erb +10 -0
  140. data/app/views/layouts/caboose/store/_banner2.html.erb +10 -0
  141. data/app/views/layouts/caboose/store/_footer.html.erb +55 -0
  142. data/app/views/layouts/caboose/store/_header.html.erb +69 -0
  143. data/app/views/layouts/caboose/store/_sidebar.html.erb +27 -0
  144. data/app/views/layouts/caboose/store/application.html.erb +33 -0
  145. data/app/views/layouts/caboose/store/authorize_net.erb +18 -0
  146. data/app/views/layouts/caboose/store/layout_about.html.erb +42 -0
  147. data/app/views/layouts/caboose/store/layout_blog.html.erb +159 -0
  148. data/app/views/layouts/caboose/store/layout_confirm.html.erb +85 -0
  149. data/app/views/layouts/caboose/store/layout_contact.html.erb +38 -0
  150. data/app/views/layouts/caboose/store/layout_default.html.erb +10 -0
  151. data/app/views/layouts/caboose/store/layout_detail.html.erb +114 -0
  152. data/app/views/layouts/caboose/store/layout_order.html.erb +77 -0
  153. data/app/views/layouts/caboose/store/layout_pricing.html.erb +182 -0
  154. data/app/views/layouts/caboose/store/layout_product.html.erb +110 -0
  155. data/app/views/layouts/caboose/store/layout_profile.html.erb +55 -0
  156. data/app/views/layouts/caboose/store/layout_single.html.erb +3 -0
  157. data/app/views/layouts/caboose/store/layout_testimonial.html.erb +110 -0
  158. data/app/views/layouts/caboose/store/layout_testing.html.erb +4 -0
  159. data/config/routes.rb +126 -0
  160. data/lib/caboose.rb +46 -1
  161. data/lib/caboose/engine.rb +39 -1
  162. data/lib/caboose/version.rb +1 -1
  163. data/lib/tasks/caboose.rake +12 -0
  164. metadata +151 -4
  165. data/app/assets/javascripts/caboose/admin_page_edit_content_bak.js +0 -164
  166. data/app/assets/javascripts/caboose/model/#Untitled-1# +0 -2
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YzY1NmViNjUxMTY5MWU3MmQ2Y2Y0Y2EyNjIwYWI3NmJlZTFlZTIwOA==
4
+ ODI1OTIzYTljMTc2ODg4OGFlMzExM2ViMjQ1MDc2ZjlmZTAwOTM0MA==
5
5
  data.tar.gz: !binary |-
6
- NzRkZGIwZGRiZjBiOGVhZDJmMDk5MGIxNjg2MmI0MmEzMmJhZGMzYQ==
6
+ NzUzZjJjMTQ0NDgwZTM2YzBjMzI0NWM5NjQ3YzlmZjM1MjQxZGQ0OA==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- OTY3ZTgzY2JlNzdlMjlmZDJmMWQwYzA0NDVhYzAyZmI0NWIyNzA4MmNjOWFm
10
- MTM5MjZmZDZlMjhjNzFhYjIyZDJmYWY5ZGY1NDU1YjUzODdjOGYyOGM0NTli
11
- OGFkNTdjNmMzYmQzODc1NTEzZjU3OTY2ZjE3NDFiMzEyNWFhNTM=
9
+ MTIwZTE2NmVkMGRlZWIzZmEzZGIwNGNiNWZhM2NhYzZkMWIxMjg0YWNlYjdi
10
+ YjQzYWQ4YzQyMmM4YTkzMTJhOTlkOThlZTc2Y2ZmOTg4Y2ZlN2I0YTUzZmEy
11
+ ODI5ZmJjN2ZmZWJmNjliNDkxNjJhN2U1MTk3NDY4ZWEzZjRjNmU=
12
12
  data.tar.gz: !binary |-
13
- NzBiMGJmYzRiOWJhZjQ0ODA4YTZhOGNkOGU5NGQwMTY0MDZlMWIyOGQxY2Iw
14
- MDFkZDYxOTg4YjM2NDM5MmQ1NmJjN2JkMWYxOWU0NjEzOTEwN2FkNWM3MDJj
15
- OTdlNDJhOTFkNDA5NGYxN2M2NmM3MTlkOTQ3MjM0ZjIwNTRhYmU=
13
+ ZGNiYjI5ZTFhODQzMDEwZWE3Y2I4MjE2ZWY2MTg4Nzk0MjNjNzRiMDIxNGVj
14
+ ZDRlNzhkYzRlYmE5Y2ZiNzY3N2NhZTkyODY1Nzk5M2JjZWVmM2MxYjYxODA4
15
+ MDBhMjAyYTY4ZWU3YWUwOGI4MzhkODc2MmY4NTVjNDgxNWE5MTA=
@@ -0,0 +1,79 @@
1
+
2
+ function delete_product(product_id, confirm)
3
+ {
4
+ if (!confirm)
5
+ {
6
+ var p = $('<p/>')
7
+ .addClass('note error')
8
+ .append("Are you sure you want to delete the product?<br />This can't be undone.")
9
+ .append("<br /><br />")
10
+ .append($("<input/>").attr('type', 'button').val('Yes').click(function() { delete_product(product_id, true); }))
11
+ .append(' ')
12
+ .append($("<input/>").attr('type', 'button').val('No').click(function() { cancel_delete_product(product_id); }));
13
+ modal.autosize(p);
14
+ return;
15
+ }
16
+ modal.autosize("<p class='loading'>Deleting product...</p>");
17
+ $.ajax({
18
+ url: '/admin/products/' + product_id,
19
+ type: 'delete',
20
+ success: function(resp) {
21
+ if (resp.error)
22
+ modal.autosize("<p class='note error'>" + resp.error + "</p>");
23
+ if (resp.redirect)
24
+ window.location = resp.redirect;
25
+ }
26
+ });
27
+ }
28
+
29
+ function cancel_delete_product(product_id)
30
+ {
31
+ var p = $('<p/>').append($("<input/>").attr('type', 'button').val('Delete this product').click(function() { delete_product(product_id); }));
32
+ modal.autosize(p);
33
+ }
34
+
35
+ function add_variant(product_id)
36
+ {
37
+ modal.autosize("<p class='loading'>Adding variant...</p>");
38
+
39
+ $.ajax({
40
+ url: '/admin/products/' + product_id + '/variants',
41
+ type: 'post',
42
+ success: function(resp) {
43
+ if (resp.error) modal.autosize("<p class='note error'>" + resp.error + "</p>");
44
+ if (resp.refresh) window.location.reload(true);
45
+ }
46
+ })
47
+ }
48
+
49
+ function delete_variant(variant_id, confirm)
50
+ {
51
+ if (!confirm)
52
+ {
53
+ var p = $('<p/>')
54
+ .addClass('note error')
55
+ .append("Are you sure you want to delete the variant?<br />This can't be undone.<br /><br />")
56
+ .append($("<input/>").attr('type', 'button').val('Confirm').click(function() { delete_variant(variant_id, true); }))
57
+ .append(' ')
58
+ .append($("<input/>").attr('type', 'button').val('Cancel').click(function() { cancel_delete_variant(variant_id); }));
59
+ modal.autosize(p);
60
+ return;
61
+ }
62
+ modal.autosize("<p class='loading'>Deleting product...</p>");
63
+ $.ajax({
64
+ url: '/admin/variants/' + variant_id,
65
+ type: 'delete',
66
+ success: function(resp) {
67
+ if (resp.error)
68
+ modal.autosize("<p class='note error'>" + resp.error + "</p>");
69
+ if (resp.redirect)
70
+ window.location = resp.redirect;
71
+ }
72
+ });
73
+ }
74
+
75
+ function cancel_delete_variant(variant_id)
76
+ {
77
+ var link = $('<a/>').attr('href','#').click(function(e) { e.preventDefault(); delete_variant(variant_id); });
78
+ modal.autosize(link);
79
+ }
@@ -4,8 +4,9 @@
4
4
  //= require jquery
5
5
  //= require jquery_ujs
6
6
  //= require colorbox-rails
7
+ //= require underscore
7
8
  //= require caboose/jquery.detect
8
9
  //= require caboose/jquery.placeholder
9
10
  //= require caboose/modal_integration
10
11
  //= require caboose/main
11
- //= require_tree ../../templates
12
+ //= require_tree ../../templates
@@ -0,0 +1,168 @@
1
+ //
2
+ // Cart
3
+ //
4
+
5
+ Caboose.Store.Modules.Cart = (function() {
6
+ var self = {
7
+ templates: {
8
+ lineItems: JST['caboose/cart/line_items'],
9
+ addToCart: JST['caboose/cart/add_to_cart']
10
+ }
11
+ };
12
+
13
+ //
14
+ // Initialize
15
+ //
16
+
17
+ self.initialize = function() {
18
+ self.renderAddToCart();
19
+ self.renderItemCount();
20
+ self.$cart = $('#cart');
21
+ if (!self.$cart.length) return false;
22
+ self.$cart.on('click', '#remove-from-cart', self.removeHandler);
23
+ self.$cart.on('keyup', 'input', self.updateHandler);
24
+ self.render();
25
+ };
26
+
27
+ //
28
+ // Set Variant
29
+ //
30
+
31
+ self.setVariant = function(variant) {
32
+ if (self.$addToCart) {
33
+ self.$addToCart.find('input[name=variant_id]').val(variant ? variant.id : "");
34
+ self.$addToCart.trigger('change');
35
+ }
36
+ };
37
+
38
+ //
39
+ // Render
40
+ //
41
+
42
+ self.render = function() {
43
+ $.get('/cart/items', function(response) {
44
+ self.$cart.empty().html(self.templates.lineItems({ order: response.order }));
45
+ self.$cart.removeClass('loading');
46
+ });
47
+ };
48
+
49
+ self.renderAddToCart = function() {
50
+ self.$addToCart = $('#add-to-cart');
51
+ if (!self.$addToCart.length) return false;
52
+ self.$addToCart.empty().html(self.templates.addToCart());
53
+ $('input[name=quantity]', self.$addToCart).on('keyup', self.quantityKeyupHandler);
54
+ $('form', self.$addToCart).on('submit', self.addHandler);
55
+ };
56
+
57
+ self.renderItemCount = function(itemCount) {
58
+ var $link = $('#cart-link, .cart-link');
59
+ if (!$link.length) return false;
60
+
61
+ function setCount(count) {
62
+ if ($link.children('i') && count < 1) {
63
+ $link.children('i').remove();
64
+ } else if ($link.children('i').length) {
65
+ $link.children('i').empty().text(count);
66
+ } else {
67
+ $link.append($('<i/>').text(count));
68
+ }
69
+ };
70
+
71
+ if (itemCount) {
72
+ setCount(itemCount);
73
+ } else {
74
+ $.get('/cart/item-count', function(response) {
75
+ setCount(response.item_count);
76
+ });
77
+ }
78
+ };
79
+
80
+ //
81
+ // Event Handlers
82
+ //
83
+
84
+ self.quantityKeyupHandler = function(event) {
85
+ var $quantity = $(event.target);
86
+ $quantity.val($quantity.val().match(/\d*\.?\d+/));
87
+ };
88
+
89
+ self.addHandler = function(event) {
90
+ event.preventDefault();
91
+ var $form = $(event.target);
92
+
93
+ if ($form.find('input[name=variant_id]').val().trim() == "") {
94
+ alert('Must select all options');
95
+ } else {
96
+ $.ajax({
97
+ type: $form.attr('method'),
98
+ url: $form.attr('action'),
99
+ data: $form.serialize(),
100
+ success: function(response) {
101
+ if (response.success) {
102
+ self.renderItemCount(response.item_count);
103
+ if (self.$addToCart.length) self.$addToCart.trigger('added');
104
+
105
+ if (!self.$addToCart.find('.message').length) {
106
+ self.$addToCart.append($('<p/>').hide().addClass('message').text('Successfully added to cart'));
107
+ self.$addToCart.find('.message').fadeIn();
108
+ Caboose.Store.Modules.Product.$product.trigger('added-to-cart');
109
+
110
+ setTimeout(function() {
111
+ self.$addToCart.find('.message').fadeOut(function() { $(this).remove() });
112
+ }, 1000);
113
+ }
114
+ } else {
115
+ alert(response.errors[0]);
116
+ }
117
+ }
118
+ });
119
+ }
120
+ };
121
+
122
+ self.updateHandler = function(event) {
123
+ var $quantity = $(event.target)
124
+ , $lineItem = $quantity.parents('li').first();
125
+
126
+ $quantity.val($quantity.val().match(/\d*\.?\d+/));
127
+ if ($quantity.val() == "") return false;
128
+
129
+ delay(function() {
130
+ $.ajax({
131
+ type: 'put',
132
+ url: '/cart/items/' + $lineItem.data('id'),
133
+ data: { quantity: $quantity.val() },
134
+ success: function(response) {
135
+ if (response.success) {
136
+ $lineItem.find('.price').empty().text('$' + response.line_item.price);
137
+ if (self.$cart.find('.subtotal').length) self.$cart.find('.subtotal').empty().text('$' + response.order_subtotal);
138
+ } else {
139
+ alert(response.errors[0]);
140
+ }
141
+ }
142
+ });
143
+ }, 1000);
144
+ };
145
+
146
+ self.removeHandler = function(event) {
147
+ var $lineItem = $(event.target).parents('li').first();
148
+
149
+ $.ajax({
150
+ type: 'delete',
151
+ url: '/cart/items/' + $lineItem.data('id'),
152
+ success: function(response) {
153
+ if (response.success) {
154
+ self.render();
155
+ self.renderItemCount(response.item_count);
156
+ }
157
+ }
158
+ });
159
+ };
160
+
161
+ self.redirectHandler = function(event) {
162
+ event.preventDefault();
163
+ window.location = $(event.target).attr('href');
164
+ };
165
+
166
+ return self;
167
+ }).call(Caboose.Store);
168
+
@@ -0,0 +1,151 @@
1
+ //
2
+ // Checkout
3
+ //
4
+ // :: Initialize
5
+ // :: Index
6
+ // :: Shipping
7
+ // :: Payment
8
+
9
+ var CabooseCheckout = function() {
10
+ var self = this;
11
+
12
+ //
13
+ // Initialize
14
+ //
15
+
16
+ self.initialize = function() {
17
+
18
+ // Ensure that a user is logged in
19
+ //if (!Caboose.loggedIn && window.location.pathname.substring(0, 9) == '/checkout') Caboose.login();
20
+ console.log('foo');
21
+
22
+ // Route to correct method
23
+ switch (window.location.pathname) {
24
+ case '/checkout': self.index(); break;
25
+ case '/checkout/shipping': self.shipping(); break;
26
+ case '/checkout/billing': self.billing(); break;
27
+ }
28
+ };
29
+
30
+ //
31
+ // Index
32
+ //
33
+
34
+ self.index = function() {
35
+
36
+ // Ensure use as billing is automatically checked
37
+ $('#use-as-billing').prop('checked', true);
38
+
39
+ // Show/hide the billing form
40
+ $('#use-as-billing').on('change', function(event) {
41
+ if ($('#use-as-billing')[0].checked) {
42
+ $('#billing').hide()
43
+ } else {
44
+ $('#billing').show()
45
+ }
46
+ });
47
+
48
+ // Update address info
49
+ $('form#address').on('submit', function(event) {
50
+ event.preventDefault();
51
+
52
+ $.ajax({
53
+ url: '/checkout/address',
54
+ type: 'put',
55
+ data: $(event.delegateTarget).serialize(),
56
+ success: function(response) {
57
+ if (response.error) $('#message').html("<p class='note error'>" + response.error + "</p>");
58
+ if (response.redirect) window.location = response.redirect;
59
+ }
60
+ });
61
+ });
62
+ };
63
+
64
+ //
65
+ // Shipping
66
+ //
67
+
68
+ self.shipping = function() {
69
+ $('form#shipping-rates').one('submit', function(event) {
70
+ event.preventDefault();
71
+
72
+ var $form = $(event.delegateTarget)
73
+ , code = $('input[name=shipping_method_code]:checked', $form).val()
74
+ , name = $('input#shipping-method-' + code + '-name', $form).val()
75
+ , price = $('input#shipping-method-' + code + '-price', $form).val();
76
+
77
+ $.ajax({
78
+ url: '/checkout/shipping-method',
79
+ type: 'put',
80
+ data: {
81
+ shipping_method: {
82
+ code: code,
83
+ name: name,
84
+ price: price
85
+ }
86
+ },
87
+ success: function(response) {
88
+ if (response.error) $('#message').html("<p class='note error'>" + response.error + "</p>");
89
+ if (response.redirect) window.location = response.redirect;
90
+ }
91
+ });
92
+ });
93
+ };
94
+
95
+ //
96
+ // Billing
97
+ //
98
+
99
+ self.billing = function() {
100
+ window.relay = function(authorized) {
101
+ if (authorized) {
102
+ window.location.replace('/checkout/thanks');
103
+ } else {
104
+ window.location.replace('/checkout/error');
105
+ }
106
+ };
107
+
108
+ $('#billing-expiration-month, #billing-expiration-year').on('change', function(event) {
109
+ $('input[name=billing-cc-exp]').val($('#billing-expiration-month').val() + $('#billing-expiration-year').val());
110
+ });
111
+
112
+ $('#billing-form').one('submit', function(e) {
113
+ e.preventDefault();
114
+
115
+ // TODO this is a temporary fix.. for some reason the one submit event is getting registered multiple times
116
+ if ($('#billing-confirmation').length) return false;
117
+
118
+ $('input[name=billing-cc-exp]').val($('#billing-expiration-month').val() + $('#billing-expiration-year').val());
119
+
120
+ var $form = $(e.delegateTarget)
121
+ , cc_num = $form.find('input[name=billing-cc-number]').val()
122
+ , cc_exp = $form.find('input[name=billing-cc-exp]').val()
123
+ , total = $('input#billing-amount').val()
124
+ , $confirm = $(document.createElement('div'));
125
+
126
+ $confirm.attr('id', 'billing-confirmation');
127
+ $confirm.append( $('</p>').attr('style', 'margin-bottom: 0').html('<strong>Credit Card Number</strong>: xxxx-xxxx-xxxx-' + cc_num.replace(/\-\ /g, '').substr(-4)) );
128
+ $confirm.append( $('</p>').attr('style', 'margin-bottom: 0').html('<strong>Expiration Date</strong>: ' + cc_exp.substr(0, 2) + '/' + cc_exp.substr(2, 2)) );
129
+ $confirm.append( $('</p>').html('<strong>Total</strong>: $' + total) );
130
+ $confirm.append( $('</p>').attr('style', 'margin: 24px 0').html('<a href="/checkout/billing">Edit billing info</a>') );
131
+ $confirm.append( $('</p>').html('<input id="submit-billing" type="button" value="Continue >" />') );
132
+
133
+ $form.after($confirm);
134
+ $form.hide();
135
+
136
+ $confirm.find('#submit-billing').on('click', function(e) {
137
+ e.preventDefault();
138
+ $form.submit();
139
+ $confirm.after( $('</p>').addClass('loading').text('Authorizing transaction..') );
140
+ $confirm.hide();
141
+ });
142
+ });
143
+ };
144
+
145
+ // Init and return
146
+ $(document).ready(self.initialize);
147
+ return self;
148
+ };
149
+
150
+ Caboose.Checkout = new CabooseCheckout();
151
+
@@ -0,0 +1,312 @@
1
+ //
2
+ // Checkout
3
+ //
4
+
5
+ Caboose.Store.Modules.Checkout = (function() {
6
+
7
+ // Steps
8
+ // Step 1: Present non-editable cart and login/register/guest buttons.
9
+ // Step 2: Present shipping address form.
10
+ // Step 3: Present shipping options.
11
+ // Step 4: Present credit card form.
12
+ // Step 5: Thank you.
13
+
14
+ self = {
15
+ templates: {
16
+ address: JST['caboose/checkout/address'],
17
+ login: JST['caboose/checkout/login'],
18
+ payment: JST['caboose/checkout/payment'],
19
+ lineItems: JST['caboose/checkout/line_items'],
20
+ shipping: JST['caboose/checkout/shipping'],
21
+ forms: {
22
+ signin: JST['caboose/checkout/forms/signin'],
23
+ register: JST['caboose/checkout/forms/register'],
24
+ guest: JST['caboose/checkout/forms/guest']
25
+ }
26
+ }
27
+ };
28
+
29
+ //
30
+ // Initialize
31
+ //
32
+
33
+ self.initialize = function() {
34
+ switch (window.location.pathname.replace(/\/$/, "")) {
35
+ case '/checkout':
36
+ case '/checkout/step-one': self.step = 1; break;
37
+ case '/checkout/step-two': self.step = 2; break;
38
+ }
39
+
40
+ self.$checkout = $('#checkout')
41
+ if (!self.$checkout.length) return false;
42
+ self.loggedIn = $('body').data('logged-in');
43
+
44
+ // TODO refactor this
45
+ if (self.loggedIn) {
46
+ $.post('/checkout/attach-user', function(response) {
47
+ self.fetch(self.render);
48
+ });
49
+ } else {
50
+ self.fetch(self.render);
51
+ }
52
+
53
+ self.bindEventHandlers();
54
+ };
55
+
56
+ //
57
+ // Fetch items from the cart
58
+ //
59
+
60
+ self.fetch = function(callback) {
61
+ $.get('/cart/items', function(response) {
62
+ self.order = response.order
63
+
64
+ if (self.step == 2) {
65
+ $.get('/checkout/shipping', function(response) {
66
+ self.shippingRates = response.rates;
67
+ self.selectedRate = response.selected_rate;
68
+ callback();
69
+ });
70
+ } else {
71
+ callback();
72
+ }
73
+ });
74
+ };
75
+
76
+ //
77
+ // Events
78
+ //
79
+
80
+ self.bindEventHandlers = function() {
81
+ self.$checkout.on('click' , '[data-login-action]', self.loginClickHandler);
82
+ self.$checkout.on('submit', '#checkout-login form', self.loginSubmitHandler);
83
+ self.$checkout.on('change', 'input[type=checkbox][name=use_as_billing]', self.useAsBillingHandler);
84
+ self.$checkout.on('click' , '#checkout-continue button', self.continueHandler);
85
+ self.$checkout.on('click' , '#checkout-complete button', self.completeHandler);
86
+ self.$checkout.on('change', '#checkout-shipping select', self.shippingChangeHandler);
87
+ self.$checkout.on('change', '#checkout-payment form#payment select', self.expirationChangeHandler);
88
+ self.$checkout.on('submit', '#checkout-payment form#payment', self.paymentSubmitHandler);
89
+ };
90
+
91
+ $('.login-choices button').removeClass('selected');
92
+ self.loginClickHandler = function(event) {
93
+ $section = self.$login.children('section');
94
+
95
+ switch ($(event.target).data('login-action')) {
96
+ case 'signin' : $section.slideUp(400, function() { $section.empty().html(self.templates.forms.signin() ).slideDown() }); $('#signin_button' ).addClass('selected'); break;
97
+ case 'register': $section.slideUp(400, function() { $section.empty().html(self.templates.forms.register()).slideDown() }); $('#register_button').addClass('selected'); break;
98
+ case 'continue': $section.slideUp(400, function() { $section.empty().html(self.templates.forms.guest() ).slideDown() }); $('#continue_button').addClass('selected'); break;
99
+ };
100
+ };
101
+
102
+ self.loginSubmitHandler = function(event) {
103
+ event.preventDefault();
104
+ var $form = $(event.target);
105
+
106
+ $.ajax({
107
+ type: $form.attr('method'),
108
+ url: $form.attr('action'),
109
+ data: $form.serialize(),
110
+ success: function(response) {
111
+ if (response.error || (response.errors && response.errors.length > 0)) {
112
+ if ($form.find('.message').length) {
113
+ $form.find('.message').empty().addClass('error').text(response.error || response.errors[0]);
114
+ } else {
115
+ $form.append($('<span/>').addClass('message error').text(response.error || response.errors[0]));
116
+ }
117
+ } else {
118
+ if (response.logged_in) {
119
+ self.$login.after($('<p/>').addClass('alert').text('You are now signed in').css('text-align', 'center')).remove();
120
+ $.post('/checkout/attach-user');
121
+ } else {
122
+ self.$login.after($('<p/>').addClass('alert').text('Email successfully saved').css('text-align', 'center')).remove();
123
+ }
124
+ }
125
+
126
+ self.fetch(self.render);
127
+ }
128
+ });
129
+ };
130
+
131
+ self.useAsBillingHandler = function(event) {
132
+ if (event.target.checked) {
133
+ self.$address.find('#billing').hide();
134
+ } else {
135
+ self.$address.find('#billing').show();
136
+ }
137
+ };
138
+
139
+ self.continueHandler = function(event) {
140
+ $form = self.$address.find('form');
141
+
142
+ if (!self.order.email && !self.order.customer_id) {
143
+ alert('Please sign in, register or choose to continue as a guest');
144
+ return false;
145
+ }
146
+
147
+ $.ajax({
148
+ type: $form.attr('method'),
149
+ url: $form.attr('action'),
150
+ data: $form.serialize(),
151
+ success: function(response) {
152
+ if (response.success) {
153
+ window.location = '/checkout/step-two';
154
+ } else {
155
+ $form.find('.message').remove();
156
+ $form.find('#' + response.address + ' h3').append($('<span/>').addClass('message error').text(response.errors[0]));
157
+ }
158
+ }
159
+ });
160
+ };
161
+
162
+ self.shippingChangeHandler = function(event) {
163
+ if (event.target.value == "") return false;
164
+ self.$checkout.addClass('loading');
165
+
166
+ $.ajax({
167
+ url: '/checkout/shipping',
168
+ type: 'put',
169
+ data: { shipping_method_code: event.target.value },
170
+ success: function(response) {
171
+ if (response.success) {
172
+ self.order = response.order;
173
+ self.selectedRate = response.selected_rate;
174
+ self.render();
175
+ }
176
+ }
177
+ });
178
+ };
179
+
180
+ self.expirationChangeHandler = function(event) {
181
+ var $form = $('#checkout-payment #payment')
182
+ , month = $form.find('select[name=month]').val()
183
+ , year = $form.find('select[name=year]').val();
184
+
185
+ $form.find('#expiration').val(month + year);
186
+ };
187
+
188
+ self.completeHandler = function(event) {
189
+ if (self.$payment.length) self.$payment.find('form').submit();
190
+ };
191
+
192
+ self.paymentSubmitHandler = function(event) {
193
+ event.preventDefault();
194
+
195
+ if (!self.order.shipping_method_code) {
196
+ alert('Please choose a shipping method');
197
+ } else {
198
+ self.$checkout.off('submit', '#checkout-payment form#payment').addClass('loading');
199
+ $(event.target).submit();
200
+ }
201
+ };
202
+
203
+ self.relayHandler = function(event) {
204
+ var $iframe = $(event.target)
205
+ , $form = self.$payment.find('form')
206
+ , $response = $iframe.contents().find('#response');
207
+
208
+ if (!$response.length || !$form.length) return false;
209
+ var response = JSON.parse($iframe.contents().find('#response').html());
210
+ console.log(response);
211
+ if (response.success == true) {
212
+ window.location = '/checkout/thanks';
213
+ } else {
214
+ alert(response.message);
215
+ self.render();
216
+ }
217
+ };
218
+
219
+ //
220
+ // Render
221
+ //
222
+
223
+ self.render = function() {
224
+ var renderFunctions = [];
225
+
226
+ if (self.step == 1) {
227
+ renderFunctions.push(self.renderLineItems);
228
+ renderFunctions.push(self.renderLogin);
229
+ renderFunctions.push(self.renderAddress);
230
+ } else {
231
+ renderFunctions.push(self.renderShipping);
232
+ renderFunctions.push(self.renderLineItems);
233
+ renderFunctions.push(self.renderPayment);
234
+ }
235
+
236
+ _.each(renderFunctions, function(renderFunction, index) {
237
+ var finished = index == (renderFunctions.length - 1)
238
+
239
+ renderFunction(function() {
240
+ if (finished) self.$checkout.removeClass('loading');
241
+ });
242
+ });
243
+ };
244
+
245
+ self.renderLineItems = function(callback) {
246
+ self.$lineItems = self.$checkout.find('#checkout-line-items');
247
+ if (!self.$lineItems.length) return false;
248
+ self.$lineItems.empty().html(self.templates.lineItems({ order: self.order }));
249
+ if (callback) callback();
250
+ };
251
+
252
+ self.renderLogin = function(callback) {
253
+ self.$login = self.$checkout.find('#checkout-login');
254
+ if (self.loggedIn) self.$login.remove();
255
+ if (self.loggedIn || !self.$login.length) return false;
256
+ self.$login.html(self.templates.login());
257
+ //if (!self.order.email) self.$login.find('button[data-login-action="signin"]').click();
258
+ if (callback) callback();
259
+ };
260
+
261
+ self.renderAddress = function(callback) {
262
+ self.$address = self.$checkout.find('#checkout-address');
263
+ if (!self.$address.length) return false;
264
+
265
+ self.$address.empty().html(self.templates.address({
266
+ shippingAddress: self.order.shipping_address,
267
+ billingAddress: self.order.billing_address,
268
+ states: window.States
269
+ }));
270
+
271
+ if (callback) callback();
272
+ };
273
+
274
+ self.renderShipping = function(callback) {
275
+ self.$shipping = self.$checkout.find('#checkout-shipping');
276
+ if (!self.$shipping.length) return false;
277
+
278
+ self.$shipping.empty().html(self.templates.shipping({
279
+ rates: self.shippingRates,
280
+ selectedRate: self.selectedRate
281
+ }));
282
+
283
+ if (callback) callback();
284
+ };
285
+
286
+ self.renderPayment = function(callback) {
287
+ self.$payment = self.$checkout.find('#checkout-payment');
288
+ if (!self.$payment.length) return false;
289
+ self.$checkout.addClass('loading');
290
+
291
+ $.get('/checkout/payment', function(response) {
292
+ var serializedForm = self.$payment.find('form').serialize();
293
+ self.$payment.empty().html(self.templates.payment({ form: response }));
294
+
295
+ if (serializedForm.length > 0) {
296
+ _.each(serializedForm.split('&'), function(serializedField) {
297
+ var name = serializedField.split('=')[0]
298
+ , value = serializedField.split('=')[1];
299
+
300
+ self.$payment.find('form [name="' + name + '"]').val(value);
301
+ });
302
+ }
303
+
304
+ self.expirationChangeHandler();
305
+ self.$checkout.removeClass('loading');
306
+ self.$payment.find('iframe').on('load', self.relayHandler);
307
+ });
308
+ };
309
+
310
+ return self
311
+ }).call(Caboose.Store);
312
+