caboose-cms 0.5.15 → 0.5.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +9 -9
- data/app/assets/javascripts/caboose/cart2.js +98 -0
- data/app/assets/javascripts/caboose/model/index_table.js +82 -30
- data/app/assets/javascripts/caboose/model/model_binder.js +4 -4
- data/app/assets/stylesheets/caboose/admin.css +1 -0
- data/app/assets/stylesheets/caboose/admin_main.css +0 -42
- data/app/assets/stylesheets/caboose/cart.scss +41 -0
- data/app/assets/stylesheets/caboose/checkout.css.scss +2 -3
- data/app/assets/stylesheets/caboose/message_boxes.scss +49 -0
- data/app/assets/templates/caboose/cart/add_to_cart.jst.ejs +1 -1
- data/app/controllers/caboose/block_types_controller.rb +3 -1
- data/app/controllers/caboose/cart_controller.rb +30 -23
- data/app/controllers/caboose/checkout_controller.rb +11 -1
- data/app/controllers/caboose/products_controller.rb +28 -14
- data/app/controllers/caboose/stackable_groups_controller.rb +107 -0
- data/app/models/caboose/line_item.rb +20 -6
- data/app/models/caboose/order.rb +2 -1
- data/app/models/caboose/order_package.rb +103 -0
- data/app/models/caboose/product.rb +2 -1
- data/app/models/caboose/schema.rb +45 -3
- data/app/models/caboose/shipping_calculator.rb +101 -60
- data/app/models/caboose/shipping_package.rb +80 -0
- data/app/models/caboose/site.rb +3 -1
- data/app/models/caboose/stackable_group.rb +17 -0
- data/app/models/caboose/tax_calculator.rb +1 -2
- data/app/models/caboose/variant.rb +2 -5
- data/app/views/caboose/cart/index.html.erb +19 -6
- data/app/views/caboose/checkout/_cart.html.erb +49 -52
- data/app/views/caboose/checkout/empty.html.erb +9 -2
- data/app/views/caboose/checkout/step_one.html.erb +2 -3
- data/app/views/caboose/checkout/step_three.html.erb +28 -10
- data/app/views/caboose/checkout/step_two.html.erb +2 -4
- data/app/views/caboose/pages/admin_edit_content.html.erb +1 -1
- data/app/views/caboose/products/admin_index.html.erb +12 -11
- data/app/views/caboose/sites/admin_edit.html.erb +33 -6
- data/app/views/caboose/stackable_groups/admin_index.html.erb +43 -0
- data/config/routes.rb +15 -6
- data/lib/caboose/version.rb +1 -1
- data/lib/tasks/caboose.rake +7 -1
- metadata +36 -14
@@ -3,6 +3,7 @@ module Caboose
|
|
3
3
|
self.table_name = 'store_products'
|
4
4
|
self.primary_key = 'id'
|
5
5
|
|
6
|
+
belongs_to :stackable_group, :class_name => 'Caboose::StackableGroup'
|
6
7
|
belongs_to :vendor, :class_name => 'Caboose::Vendor'
|
7
8
|
has_many :customizations, :class_name => 'Caboose::Product', :through => :customization_memberships
|
8
9
|
has_many :customization_memberships, :class_name => 'Caboose::CustomizationMembership'
|
@@ -66,7 +67,7 @@ module Caboose
|
|
66
67
|
:vendor => self.vendor,
|
67
68
|
:categories => self.categories,
|
68
69
|
:variants => self.variants,
|
69
|
-
:images => self.product_images
|
70
|
+
:images => self.product_images
|
70
71
|
})
|
71
72
|
end
|
72
73
|
|
@@ -242,12 +242,12 @@ class Caboose::Schema < Caboose::Utilities::Schema
|
|
242
242
|
],
|
243
243
|
Caboose::LineItem => [
|
244
244
|
[ :order_id , :integer ],
|
245
|
+
[ :order_package_id , :integer ],
|
245
246
|
[ :variant_id , :integer ],
|
246
247
|
[ :parent_id , :integer ],
|
247
248
|
[ :quantity , :integer , :default => 0 ],
|
248
249
|
[ :status , :string ],
|
249
|
-
[ :tracking_number , :string ],
|
250
|
-
#[ :unit_price , :numeric ],
|
250
|
+
[ :tracking_number , :string ],
|
251
251
|
[ :price , :numeric , :default => 0 ],
|
252
252
|
[ :notes , :text ],
|
253
253
|
[ :custom1 , :string ],
|
@@ -311,6 +311,12 @@ class Caboose::Schema < Caboose::Utilities::Schema
|
|
311
311
|
[ :order_id , :integer ],
|
312
312
|
[ :discount_id , :integer ]
|
313
313
|
],
|
314
|
+
Caboose::OrderPackage => [
|
315
|
+
[ :order_id , :integer ],
|
316
|
+
[ :shipping_package_id , :integer ],
|
317
|
+
[ :status , :string ],
|
318
|
+
[ :tracking_number , :string ]
|
319
|
+
],
|
314
320
|
Caboose::Page => [
|
315
321
|
[ :site_id , :integer ],
|
316
322
|
[ :parent_id , :integer ],
|
@@ -396,7 +402,8 @@ class Caboose::Schema < Caboose::Utilities::Schema
|
|
396
402
|
[ :date_available , :datetime ],
|
397
403
|
[ :custom_input , :text ],
|
398
404
|
[ :sort_order , :integer ],
|
399
|
-
[ :featured , :boolean , :default => false ]
|
405
|
+
[ :featured , :boolean , :default => false ],
|
406
|
+
[ :stackable_group_id , :integer ]
|
400
407
|
],
|
401
408
|
Caboose::ProductImage => [
|
402
409
|
[ :product_id , :integer ],
|
@@ -449,6 +456,16 @@ class Caboose::Schema < Caboose::Utilities::Schema
|
|
449
456
|
[ :name , :string ],
|
450
457
|
[ :value , :text ]
|
451
458
|
],
|
459
|
+
Caboose::ShippingPackage => [
|
460
|
+
[ :carrier , :string ],
|
461
|
+
[ :service_code , :string ],
|
462
|
+
[ :service_name , :string ],
|
463
|
+
[ :length , :decimal ],
|
464
|
+
[ :width , :decimal ],
|
465
|
+
[ :height , :decimal ],
|
466
|
+
[ :volume , :decimal ],
|
467
|
+
[ :price , :decimal ]
|
468
|
+
],
|
452
469
|
Caboose::Site => [
|
453
470
|
[ :name , :string ],
|
454
471
|
[ :description , :text ],
|
@@ -469,6 +486,15 @@ class Caboose::Schema < Caboose::Utilities::Schema
|
|
469
486
|
[ :authentication , :string ], # :plain, :login, :cram_md5.
|
470
487
|
[ :enable_starttls_auto , :boolean , { :default => true }]
|
471
488
|
],
|
489
|
+
Caboose::StackableGroup => [
|
490
|
+
[ :name , :string ],
|
491
|
+
[ :extra_length , :decimal ],
|
492
|
+
[ :extra_width , :decimal ],
|
493
|
+
[ :extra_height , :decimal ],
|
494
|
+
[ :max_length , :decimal ],
|
495
|
+
[ :max_width , :decimal ],
|
496
|
+
[ :max_height , :decimal ]
|
497
|
+
],
|
472
498
|
Caboose::User => [
|
473
499
|
[ :first_name , :string ],
|
474
500
|
[ :last_name , :string ],
|
@@ -502,6 +528,7 @@ class Caboose::Schema < Caboose::Utilities::Schema
|
|
502
528
|
[ :length , :decimal ],
|
503
529
|
[ :width , :decimal ],
|
504
530
|
[ :height , :decimal ],
|
531
|
+
[ :volume , :decimal ],
|
505
532
|
[ :cylinder , :boolean ],
|
506
533
|
[ :option1 , :string ],
|
507
534
|
[ :option2 , :string ],
|
@@ -687,6 +714,21 @@ class Caboose::Schema < Caboose::Utilities::Schema
|
|
687
714
|
:slug => 'products'
|
688
715
|
})
|
689
716
|
end
|
717
|
+
|
718
|
+
if !Caboose::ShippingPackage.where(:carrier => 'USPS', :service_code => "29").exists?
|
719
|
+
Caboose::ShippingPackage.create(:carrier => 'USPS', :service_code => "29", :service_name => "USPS Priority Mail 1-Day Padded Flat Rate Envelope", :price=>610 , :volume => 180)
|
720
|
+
end
|
721
|
+
if !Caboose::ShippingPackage.where(:carrier => 'USPS', :service_code => "28").exists?
|
722
|
+
Caboose::ShippingPackage.create(:carrier => 'USPS', :service_code => "28", :service_name => "USPS Priority Mail 1-Day Small Flat Rate Box" , :price=>595 , :length => 8.625 , :width => 5.375 , :height => 1.625 , :volume => 75.334)
|
723
|
+
end
|
724
|
+
if !Caboose::ShippingPackage.where(:carrier => 'USPS', :service_code => "17").exists?
|
725
|
+
Caboose::ShippingPackage.create(:carrier => 'USPS', :service_code => "17", :service_name => "USPS Priority Mail 1-Day Medium Flat Rate Box" , :price=>1265 , :length => 11 , :width => 8.5 , :height => 5.5 , :volume => 514.25)
|
726
|
+
Caboose::ShippingPackage.create(:carrier => 'USPS', :service_code => "17", :service_name => "USPS Priority Mail 1-Day Medium Flat Rate Box" , :price=>1265 , :length => 13.625 , :width => 11.875 , :height => 3.375 , :volume => 546.06)
|
727
|
+
end
|
728
|
+
if !Caboose::ShippingPackage.where(:carrier => 'USPS', :service_code => "22").exists?
|
729
|
+
Caboose::ShippingPackage.create(:carrier => 'USPS', :service_code => "22", :service_name => "USPS Priority Mail 1-Day Large Flat Rate Box" , :price=>1790 , :length => 12 , :width => 12 , :height => 5.5 , :volume => 792 )
|
730
|
+
Caboose::ShippingPackage.create(:carrier => 'USPS', :service_code => "22", :service_name => "USPS Priority Mail 1-Day Large Flat Rate Box" , :price=>1790 , :length => 23.6875 , :width => 11.75 , :height => 3 , :volume => 834.98)
|
731
|
+
end
|
690
732
|
|
691
733
|
end
|
692
734
|
end
|
@@ -3,72 +3,48 @@ include ActiveMerchant::Shipping
|
|
3
3
|
|
4
4
|
module Caboose
|
5
5
|
class ShippingCalculator
|
6
|
+
|
6
7
|
def self.rates(order)
|
7
|
-
return [] if Caboose::
|
8
|
-
|
9
|
-
total = 0.0
|
10
|
-
weight = 0.0
|
11
|
-
|
12
|
-
order.line_items.each do |li|
|
13
|
-
total = total + (li.variant.shipping_unit_value.nil? ? 0 : li.variant.shipping_unit_value)
|
14
|
-
weight = weight + (li.variant.weight || 0)
|
15
|
-
end
|
16
|
-
|
17
|
-
length = 0
|
18
|
-
width = 0
|
19
|
-
height = 0
|
20
|
-
|
21
|
-
if total <= 5
|
22
|
-
length = 15.5
|
23
|
-
width = 9.5
|
24
|
-
height = 6
|
25
|
-
elsif total <= 10
|
26
|
-
length = 12
|
27
|
-
width = 8
|
28
|
-
height = 5
|
29
|
-
else
|
30
|
-
length = 20
|
31
|
-
width = 16
|
32
|
-
height = 14
|
33
|
-
end
|
8
|
+
return [] if Caboose::store_shipping.nil?
|
34
9
|
|
10
|
+
ss = Caboose::store_shipping
|
35
11
|
origin = Location.new(
|
36
|
-
:country =>
|
37
|
-
:state =>
|
38
|
-
:city =>
|
39
|
-
:zip =>
|
40
|
-
)
|
41
|
-
|
12
|
+
:country => ss[:origin][:country],
|
13
|
+
:state => ss[:origin][:state],
|
14
|
+
:city => ss[:origin][:city],
|
15
|
+
:zip => ss[:origin][:zip]
|
16
|
+
)
|
42
17
|
destination = Location.new(
|
43
|
-
:country =>
|
18
|
+
:country => ss[:origin][:country],
|
44
19
|
:state => order.shipping_address.state,
|
45
20
|
:city => order.shipping_address.city,
|
46
21
|
:postal_code => order.shipping_address.zip
|
47
|
-
)
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
22
|
+
)
|
23
|
+
ups = ss[:ups] ? UPS.new(:key => ss[:ups][:key], :login => ss[:ups][:username], :password => ss[:ups][:password], :origin_account => ss[:ups][:origin_account]) : nil
|
24
|
+
usps = ss[:usps] ? USPS.new(:login => ss[:usps][:username]) : nil
|
25
|
+
|
26
|
+
all_rates = []
|
27
|
+
order.packages.each do |op|
|
28
|
+
sp = op.shipping_package
|
29
|
+
package = op.activemerchant_package
|
30
|
+
rates = []
|
31
|
+
if ups
|
32
|
+
resp = ups.find_rates(origin, destination, package)
|
33
|
+
resp.rates.sort_by(&:price).each do |rate|
|
34
|
+
next if rate.service_code != sp.service_code
|
35
|
+
rates << { :carrier => 'UPS', :service_code => rate.service_code, :service_name => rate.service_name, :price => rate.total_price.to_d / 100 }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
if usps
|
39
|
+
resp = usps.find_rates(origin, destination, package)
|
40
|
+
resp.rates.sort_by(&:price).each do |rate|
|
41
|
+
next if rate.service_code != sp.service_code
|
42
|
+
rates << { :carrier => 'USPS', :service_code => rate.service_code, :service_name => rate.service_name, :total_price => rate.total_price.to_d / 100 }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
all_rates << [op, rates]
|
46
|
+
end
|
47
|
+
return all_rates
|
72
48
|
end
|
73
49
|
|
74
50
|
def self.rate(order)
|
@@ -76,6 +52,71 @@ module Caboose
|
|
76
52
|
self.rates(order).each { |rate| return rate if rate[:service_code] == order.shipping_method_code }
|
77
53
|
return nil
|
78
54
|
end
|
55
|
+
|
56
|
+
# Calculates the packages required for all the items in the order
|
57
|
+
#def self.packages_for_order(order)
|
58
|
+
#
|
59
|
+
# # Make sure all the items in the order have attributes set
|
60
|
+
# order.line_items.each do |li|
|
61
|
+
# v = li.variant
|
62
|
+
# Caboose.log("Error: variant #{v.id} has a zero weight") and return false if v.weight.nil? || v.weight == 0
|
63
|
+
# next if v.volume && v.volume > 0
|
64
|
+
# Caboose.log("Error: variant #{v.id} has a zero length") and return false if v.length.nil? || v.length == 0
|
65
|
+
# Caboose.log("Error: variant #{v.id} has a zero width" ) and return false if v.width.nil? || v.width == 0
|
66
|
+
# Caboose.log("Error: variant #{v.id} has a zero height") and return false if v.height.nil? || v.height == 0
|
67
|
+
# v.volume = v.length * v.width * v.height
|
68
|
+
# v.save
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# # Reorder the items in the order by volume
|
72
|
+
# h = {}
|
73
|
+
# order.line_items.each do |li|
|
74
|
+
# (1..li.quantity).each do |i|
|
75
|
+
# v = li.variant
|
76
|
+
# h[v.volume] = v
|
77
|
+
# end
|
78
|
+
# end
|
79
|
+
# variants = h.sort_by{ |k,v| k }.collect{ |x| x[1] }
|
80
|
+
#
|
81
|
+
# all_packages = ShippingPackage.reorder(:price).all
|
82
|
+
# packages = []
|
83
|
+
#
|
84
|
+
# # Now go through each variant and fit it in a new or existing package
|
85
|
+
# variants.each do |v|
|
86
|
+
#
|
87
|
+
# # See if the item will fit in any of the existing packages
|
88
|
+
# it_fits = false
|
89
|
+
# packages.each do |h|
|
90
|
+
# it_fits = h.shipping_package.fits(h.variants, v)
|
91
|
+
# if it_fits
|
92
|
+
# h.variants << v
|
93
|
+
# break
|
94
|
+
# end
|
95
|
+
# end
|
96
|
+
# next if it_fits
|
97
|
+
#
|
98
|
+
# # Otherwise find the cheapest package the item will fit into
|
99
|
+
# all_packages.each do |p|
|
100
|
+
# if p.fits(v)
|
101
|
+
# packages << StdClass.new('shipping_package' => p, 'variants' => [v])
|
102
|
+
# break
|
103
|
+
# end
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# return packages
|
109
|
+
#
|
110
|
+
# #arr = []
|
111
|
+
# #packages.each do |h|
|
112
|
+
# # p = h.package
|
113
|
+
# # weight = 0.0
|
114
|
+
# # h.variants.each{ |v| weight = weight + v.weight }
|
115
|
+
# # weight = weight * 0.035274
|
116
|
+
# # arr << Package.new(weight, [p.length, p.width, p.height], :units => :imperial)
|
117
|
+
# #end
|
118
|
+
# #return arr
|
119
|
+
#
|
120
|
+
#end
|
79
121
|
end
|
80
122
|
end
|
81
|
-
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'box_packer'
|
2
|
+
|
3
|
+
module Caboose
|
4
|
+
class ShippingPackage < ActiveRecord::Base
|
5
|
+
self.table_name = 'store_shipping_packages'
|
6
|
+
attr_accessible :id, :carrier, :service_code, :service_name, :length, :width, :height, :volume, :price
|
7
|
+
|
8
|
+
def fits(variants)
|
9
|
+
|
10
|
+
arr = variants.is_a?(Array) ? variants : [variants]
|
11
|
+
rigid = []
|
12
|
+
floppy = []
|
13
|
+
arr.each do |v|
|
14
|
+
if v.length && v.length > 0 && v.width && v.width > 0 && v.height && v.height > 0
|
15
|
+
rigid << v
|
16
|
+
else
|
17
|
+
floppy << v
|
18
|
+
end
|
19
|
+
end
|
20
|
+
rigid_volume = 0.0
|
21
|
+
floppy_volume = 0.0
|
22
|
+
rigid.each { |v| rigid_volume = rigid_volume + v.volume }
|
23
|
+
floppy.each{ |v| floppy_volume = floppy_volume + v.volume }
|
24
|
+
return false if (rigid_volume + floppy_volume) > self.volume
|
25
|
+
rigid_boxes = self.boxes(rigid)
|
26
|
+
|
27
|
+
it_fits = false
|
28
|
+
BoxPacker.container [self.length, self.width, self.height] do
|
29
|
+
rigid_boxes.each{ |arr| add_item arr }
|
30
|
+
count = pack!
|
31
|
+
it_fits = true if count == rigid_boxes.count
|
32
|
+
end
|
33
|
+
return it_fits
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
# Gets the 3d dimensions of the variants after they're stacked
|
38
|
+
def boxes(rigid_variants)
|
39
|
+
stackable = {}
|
40
|
+
nonstackable = []
|
41
|
+
rigid_variants.each do |v|
|
42
|
+
sgid = v.product.stackable_group_id
|
43
|
+
if sgid
|
44
|
+
stackable[sgid] = [] if stackable[sgid].nil?
|
45
|
+
stackable[sgid] << v
|
46
|
+
else
|
47
|
+
nonstackable << [v.length, v.width, v.height]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
stackable.each do |sgid, arr|
|
52
|
+
sg = arr[0].product.stackable_group
|
53
|
+
l = 0.0
|
54
|
+
w = 0.0
|
55
|
+
h = 0.0
|
56
|
+
arr.each do |v|
|
57
|
+
if l+sg.extra_length >= sg.max_length || w+sg.extra_width >= sg.max_width || h+sg.extra_height >= sg.max_height
|
58
|
+
nonstackable << [l, w, h]
|
59
|
+
l = 0.0
|
60
|
+
w = 0.0
|
61
|
+
h = 0.0
|
62
|
+
end
|
63
|
+
if l == 0.0
|
64
|
+
l = v.length
|
65
|
+
w = v.width
|
66
|
+
h = v.height
|
67
|
+
else
|
68
|
+
l = l + sg.extra_length
|
69
|
+
w = w + sg.extra_width
|
70
|
+
h = h + sg.extra_height
|
71
|
+
end
|
72
|
+
end
|
73
|
+
nonstackable << [l, w, h] if l > 0
|
74
|
+
end
|
75
|
+
|
76
|
+
return nonstackable
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
data/app/models/caboose/site.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
|
2
2
|
class Caboose::Site < ActiveRecord::Base
|
3
3
|
self.table_name = "sites"
|
4
|
-
|
4
|
+
|
5
|
+
has_many :block_type_site_memberships, :class_name => 'Caboose::BlockTypeSiteMembership', :dependent => :delete_all
|
6
|
+
has_many :block_types, :through => :block_type_site_memberships
|
5
7
|
has_many :site_memberships, :class_name => 'Caboose::SiteMembership', :dependent => :delete_all
|
6
8
|
has_many :domains, :class_name => 'Caboose::Domain', :dependent => :delete_all
|
7
9
|
has_many :post_categories, :class_name => 'Caboose::PostCategory'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Caboose
|
2
|
+
class StackableGroup < ActiveRecord::Base
|
3
|
+
self.table_name = 'store_stackable_groups'
|
4
|
+
|
5
|
+
has_many :products
|
6
|
+
attr_accessible :id,
|
7
|
+
:name,
|
8
|
+
:extra_length,
|
9
|
+
:extra_width,
|
10
|
+
:extra_height,
|
11
|
+
:max_length,
|
12
|
+
:max_width,
|
13
|
+
:max_height
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -76,11 +76,8 @@ module Caboose
|
|
76
76
|
|
77
77
|
def as_json(options={})
|
78
78
|
self.attributes.merge({
|
79
|
-
:images =>
|
80
|
-
|
81
|
-
else
|
82
|
-
[self.product.product_images.first]
|
83
|
-
end
|
79
|
+
:images => self.product_images.any? ? self.product_images : [self.product.product_images.first],
|
80
|
+
:title => "#{self.product.title} (#{self.options.join(', ')})"
|
84
81
|
})
|
85
82
|
end
|
86
83
|
|
@@ -1,8 +1,21 @@
|
|
1
|
-
<div class="
|
2
|
-
<div id=
|
3
|
-
</div>
|
1
|
+
<div class="constrain">
|
2
|
+
<div id='cart'></div>
|
3
|
+
</div>
|
4
|
+
|
5
|
+
<% content_for :caboose_css do %>
|
6
|
+
<%= stylesheet_link_tag 'caboose/message_boxes' %>
|
7
|
+
<%= stylesheet_link_tag 'caboose/model_binder' %>
|
8
|
+
<%= stylesheet_link_tag 'caboose/cart' %>
|
9
|
+
<% end %>
|
4
10
|
|
5
11
|
<% content_for :caboose_js do %>
|
6
|
-
<%= javascript_include_tag 'caboose/
|
7
|
-
<%= javascript_include_tag 'caboose/
|
8
|
-
|
12
|
+
<%= javascript_include_tag 'caboose/model/all' %>
|
13
|
+
<%= javascript_include_tag 'caboose/cart2' %>
|
14
|
+
<script type='text/javascript'>
|
15
|
+
|
16
|
+
$(document).ready(function() {
|
17
|
+
var controller = new Cart();
|
18
|
+
});
|
19
|
+
|
20
|
+
</script>
|
21
|
+
<% end %>
|