artfully_ose 1.2.0.pre.27 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +8 -8
  2. data/README.md +4 -6
  3. data/app/assets/javascripts/application.js +39 -0
  4. data/app/assets/javascripts/boxoffice.js +558 -0
  5. data/app/assets/javascripts/custom/kits-config.js +33 -0
  6. data/app/assets/javascripts/sales-console.js +29 -24
  7. data/app/assets/javascripts/store/store.js +11 -0
  8. data/app/assets/stylesheets/application.sass +66 -52
  9. data/app/assets/stylesheets/bootstrap-overrides.css +1 -1
  10. data/app/assets/stylesheets/boxoffice.css.scss +258 -0
  11. data/app/assets/stylesheets/pages/bootstrap-responsive.css +1109 -0
  12. data/app/assets/stylesheets/sass/store.sass +52 -62
  13. data/app/assets/stylesheets/storefront.css +1 -1
  14. data/app/concerns/itemable.rb +1 -1
  15. data/app/concerns/pdf_generation.rb +1 -11
  16. data/app/concerns/search_by_dates.rb +20 -0
  17. data/app/controllers/events_controller.rb +1 -0
  18. data/app/controllers/export_controller.rb +11 -1
  19. data/app/controllers/imports_controller.rb +2 -3
  20. data/app/controllers/member_cards_controller.rb +2 -3
  21. data/app/controllers/orders_controller.rb +42 -0
  22. data/app/controllers/organizations_controller.rb +1 -0
  23. data/app/controllers/passes_reports_controller.rb +21 -1
  24. data/app/controllers/regular_donation_kits_controller.rb +32 -0
  25. data/app/controllers/store/donations_controller.rb +19 -1
  26. data/app/controllers/store/orders_controller.rb +15 -5
  27. data/app/helpers/artfully_ose_helper.rb +28 -18
  28. data/app/models/database_views/item_view.rb +41 -0
  29. data/app/models/event.rb +10 -2
  30. data/app/models/ext/integrations.rb +5 -0
  31. data/app/models/fee_strategy.rb +47 -1
  32. data/app/models/imports/events_import.rb +6 -7
  33. data/app/models/item.rb +4 -3
  34. data/app/models/job/order_processor.rb +4 -3
  35. data/app/models/kit.rb +18 -0
  36. data/app/models/kits/regular_donation_kit.rb +51 -9
  37. data/app/models/member.rb +4 -3
  38. data/app/models/membership.rb +1 -2
  39. data/app/models/membership_sale_search.rb +2 -20
  40. data/app/models/membership_type.rb +1 -2
  41. data/app/models/order.rb +29 -1
  42. data/app/models/order_handler.rb +4 -3
  43. data/app/models/organization.rb +17 -9
  44. data/app/models/pass.rb +18 -17
  45. data/app/models/pass_sale_search.rb +18 -0
  46. data/app/models/pass_type.rb +7 -2
  47. data/app/models/passes_report.rb +117 -0
  48. data/app/models/sale_search.rb +3 -21
  49. data/app/models/user.rb +1 -2
  50. data/app/serializers/show_serializer.rb +15 -0
  51. data/app/views/contributions/index.html.haml +2 -2
  52. data/app/views/events/_header.html.haml +1 -0
  53. data/app/views/events/_ticket_type_fields.html.haml +31 -15
  54. data/app/views/events/edit.html.haml +6 -1
  55. data/app/views/imports/_export_links.html.haml +3 -0
  56. data/app/views/imports/donations/_pending.html.haml +24 -6
  57. data/app/views/imports/events/_pending.html.haml +16 -5
  58. data/app/views/imports/index.html.haml +6 -4
  59. data/app/views/imports/people/_pending.html.haml +20 -25
  60. data/app/views/layouts/storefront.html.haml +9 -5
  61. data/app/views/order_mailer/confirmation_for.html.haml +11 -5
  62. data/app/views/order_mailer/confirmation_for_refund.html.haml +6 -2
  63. data/app/views/order_mailer/confirmation_for_refund.text.haml +4 -3
  64. data/app/views/orders/membership.html.haml +15 -19
  65. data/app/views/orders/passes.html.haml +97 -0
  66. data/app/views/orders/sales.html.haml +2 -2
  67. data/app/views/organizations/_form.html.haml +4 -37
  68. data/app/views/pass_types/index.html.haml +1 -1
  69. data/app/views/passes_reports/index.html.haml +79 -1
  70. data/app/views/people/show.html.haml +16 -15
  71. data/app/views/regular_donation_kits/edit.html.haml +140 -0
  72. data/app/views/store/checkouts/thanks.html.haml +14 -6
  73. data/app/views/store/donations/index.html.haml +19 -2
  74. data/app/views/store/events/calendar.html.haml +1 -0
  75. data/app/views/store/events/index.html.haml +1 -0
  76. data/app/views/store/events/show.html.haml +1 -0
  77. data/app/views/store/events/single_show.html.haml +2 -0
  78. data/app/views/store/orders/show.html.haml +10 -10
  79. data/app/views/store/passes/index.html.haml +2 -2
  80. data/app/views/store/shared/_donate_form.html.haml +31 -12
  81. data/app/views/store/shared/_small_donate_form.html.haml +22 -0
  82. data/app/views/users/invitations/edit.html.haml +3 -42
  83. data/config/locales/en.yml +3 -1
  84. data/config/routes.rb +4 -1
  85. data/db/migrate/20140603200735_add_subtitle_to_events.rb +5 -0
  86. data/lib/artfully_ose/version.rb +1 -1
  87. data/spec/factories/kit_factories.rb +5 -4
  88. metadata +25 -13
  89. data/app/assets/javascripts/box-office.js +0 -262
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YjI4M2ZlZDEzM2Y0ZTI2ZjkzMTQxMjUwNDViZGIyMzU2NmFlMjMzYg==
4
+ YzQ0Mzk5NzQ5YWJhNDFmODY4Y2IwOGQ3NTA3NjM0NWM1ZTU5ODA4Zg==
5
5
  data.tar.gz: !binary |-
6
- NzA3YmIzZmMzZmE1NGM3Y2ZkZTdjMGE3MDJhZmFlMjNiNzU4MTZiMQ==
6
+ NmIzYzg4MmIzY2E2N2RmZmJhNDg2MTg4YWJkMjk3MGNiNzJlYmY5ZQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- YWRlYjE5OGFiM2MzOWQzYTM1MTBjYzgwMmJkYjcyODUzOWQ0OGUxNDc1ZTM1
10
- OTRmMmMxYmYzMjRhZWEwNDExNzI0ZjNhMGZkNjgyYzk4YTdkYzkyMjNlZGE4
11
- ODVjMTM5MDgyOWYxNzAwNTE5MzgyYTNhZjBiNDA4YzI2MTczYzM=
9
+ Mjg5NjUwZjUzZWUxNGI4NzA0NTk1MzgxMjFjYjE0OTNiMmI3OGY3OTFjYTBj
10
+ NTM0ZWNlNjA4MDU2NGJhYzNkMzNhNDhkNDA3NDYxNDQ5MDIzNDAwZTNmNjA1
11
+ YTcyNjZkN2MyN2MxZTRlN2JiY2FhZjQxYTk2ZTcyZjU2ZWM3M2M=
12
12
  data.tar.gz: !binary |-
13
- NWMzMWU5MjQyYzUwNTg4Zjk5ODc5ZjRkZDJmYTAxZTQ4NjE3NmYyMmVhYTUx
14
- YmY4ODM0MjFlY2YxMjRhZTk0ODY0MjA1NTEzY2FkZmU3NmYzZmFjM2YyYTEy
15
- ZGEzM2I3ZGMwODJiY2QyODI5OWFkNzllZThlODc3MTc2M2MzMTM=
13
+ MjRlZDM3N2VkZjEzZGViMDMwNGE1Y2NmZDM3ZDE5NjIzMDM0MDU2MGRhZTUx
14
+ NjFhYTA2MWE3YTg4MzkxNDA4NDYzZjBjYzkzN2IxZTIyNjExYTMxNjE0M2Zl
15
+ NTBlNzM5OTk1OWI3OWM4MjJlYzg2NjMyMmE1YTVjYTk3NmFiMDc=
data/README.md CHANGED
@@ -6,6 +6,10 @@ Welcome to Artful.ly OSE
6
6
 
7
7
  Artful.ly Open Source Edition is the core code of the Artful.ly app downloadable for free and ready for you to roll up your sleeves and build a custom management system for your arts business. The Artful.ly OSE framework gives software developers a base from which they can add features, integrate with other apps, and much more!
8
8
 
9
+ ## A Note About Contributing
10
+
11
+ Because of a variety of factors, we cannot accept pull requests on this repo. We're working to get this resolved.
12
+
9
13
  ## Before You Begin
10
14
 
11
15
  You'll want the following running on your machine:
@@ -20,9 +24,3 @@ Artful.ly Open Source Engine is a Rails Engine. **It is not intended to be moun
20
24
  ## Branch strategy
21
25
 
22
26
  Master is the current development branch. Every effort is made to make master tidy and working at all times but it is the edge and problems do happen. For any stable production applications, you'll *absolutely* want to be pointing to a published, stable gem. A list can be found [on Rubygems](https://rubygems.org/gems/artfully_ose)
23
-
24
- ## Contributors
25
-
26
- Gary Moore - Lead Developer at Fractured Atlas
27
-
28
- Clinton Judy - Open Source Lead at Fractured Atlas
@@ -629,3 +629,42 @@ function togglePrintPreview(){
629
629
  printStyles.attr("media","all");
630
630
  }
631
631
  }
632
+
633
+ $('.btn-delete-preset-amount').live('click',function(){
634
+ $(this).closest('tr').remove();
635
+ return false;
636
+ });
637
+
638
+ $( ".btn-add-preset-amount" ).click(function() {
639
+ $('#fixed_gift_error').empty();
640
+ if($.isNumeric($('#input-preset-amount').val())) {
641
+ $('#table-preset-amount tr').last().after(
642
+ '<tr>' +
643
+ '<td>$' +
644
+ $('#input-preset-amount').val() +
645
+ '<input name=\"donation_preset[]\" type=\"hidden\" value=\"' + $('#input-preset-amount').val() +'">' +
646
+ '</td>' +
647
+ '<td><button class="btn-delete-preset-amount">Delete</button></td>' +
648
+ '</tr>');
649
+ } else {
650
+ $('#fixed_gift_error').append($(document.createElement('div'))
651
+ .addClass('error')
652
+ .addClass('alert-error')
653
+ .html('<span>Please enter a number!</span>'));
654
+ }
655
+ $('#input-preset-amount').val("");
656
+ return false;
657
+ });
658
+
659
+ $( '#widget_type_donations' ).click(function() {
660
+ $("#preset-amount").show();
661
+ $("#widget-event").hide();
662
+ });
663
+ $( '#widget_type_event' ).click(function() {
664
+ $("#preset-amount").hide();
665
+ $("#widget-event").show();
666
+ });
667
+ $( '#widget_type_both' ).click(function() {
668
+ $("#preset-amount").show();
669
+ $("#widget-event").show();
670
+ });
@@ -0,0 +1,558 @@
1
+ //= require store/jquery.validate
2
+ //= require_self
3
+
4
+ $(function() {
5
+ sliderOffset = $('.navbar').width()+100;
6
+ $('#boxoffice').css({right:-sliderOffset});
7
+ $('#doorlist').css({left:-sliderOffset});
8
+
9
+ setPageHeight();
10
+
11
+ // moving between door list and box office
12
+ $('.page-nav li a').click(function() {
13
+ switchBetweenBoxofficeAndDoorlist($(this).attr('href'));
14
+ return false;
15
+ });
16
+
17
+ // focus on search bar for doorlist
18
+ $('input#search').focus();
19
+
20
+ // set doorlist progress bar
21
+ updateProgressBar();
22
+
23
+ // clicking + or - buttons for tickets
24
+ $('a.add').click(function() {
25
+ var field = $(this).prev();
26
+ var currentVal = parseInt(field.val());
27
+ if (isNaN(currentVal)) { currentVal = 0 };
28
+ var newVal = currentVal + 1;
29
+ var limit = parseInt(field.attr('data-remaining'));
30
+ if (newVal <= limit) {
31
+ $(this).siblings('.ticket-type').find('.remaining').text(limit-newVal);
32
+ field.val(newVal).trigger('change');
33
+ }
34
+ });
35
+ $('a.subtract').click(function() {
36
+ var field = $(this).next();
37
+ var currentVal = parseInt(field.val());
38
+ if (isNaN(currentVal)) { currentVal = 1 };
39
+ var newVal = currentVal - 1;
40
+ var limit = parseInt(field.attr('data-remaining'));
41
+ var remain = limit-newVal;
42
+ if (newVal >= 0) {
43
+ field.val(newVal).trigger('change');
44
+ if (remain >=0) {
45
+ $(this).siblings('.ticket-type').find('.remaining').text();
46
+ }
47
+ }
48
+ });
49
+
50
+ // typing in a ticket quantity
51
+ $('.ticket-quantity').keyup(function() {
52
+ var field = $(this);
53
+ var val = parseInt(field.val());
54
+ var limit = parseInt(field.attr('data-remaining'));
55
+ if (isNaN(val) || val < 0) {
56
+ field.val(0);
57
+ $(this).select();
58
+ } else {
59
+ if (val > limit) { val = limit };
60
+ field.val(val);
61
+ $(this).siblings('.ticket-type').find('.remaining').text(limit-val);
62
+ }
63
+ });
64
+ // highlight the whole input field
65
+ $('.ticket-quantity').click(function () {
66
+ $(this).select();
67
+ });
68
+
69
+ // discount codes are not case-sensitive
70
+ $('#order-discount').keyup(function() {
71
+ $('#order-discount').val($('#order-discount').val().toUpperCase());
72
+ });
73
+
74
+ UPDATING_TOTAL_REQUEST = null;
75
+ $(".ticket :input, #order-discount, #order-donation").bind("keyup change", function(e) {
76
+ var spinner = $('#order-total .loading');
77
+ var total = $('#order-total .message');
78
+ spinner.show();
79
+ total.hide();
80
+
81
+ // if a previous request is still being processed
82
+ // abort and don't listen for response
83
+ if(UPDATING_TOTAL_REQUEST) UPDATING_TOTAL_REQUEST.abort();
84
+
85
+ // wait 1 second after change
86
+ delay(function(){
87
+
88
+ var params = $('.boxoffice-form').serialize();
89
+ delete params['commit']; // dont actually purchase
90
+
91
+ UPDATING_TOTAL_REQUEST = $.post( $('.boxoffice-form').attr('action'), params)
92
+ .done(function(response) {
93
+ updateTotal(response);
94
+ })
95
+ .fail(function(jqXHR, textStatus, errorThrown) {
96
+ if(textStatus != 'abort') total.text('An error has occured. Please try again.');
97
+ })
98
+ .always(function(data, textStatus, errorThrown) {
99
+ if(textStatus != 'abort') {
100
+ total.show();
101
+ spinner.hide();
102
+ UPDATING_TOTAL_REQUEST = null;
103
+ }
104
+ });
105
+
106
+ }, 1000 );
107
+ });
108
+
109
+ // select payment type
110
+ $(".payment-select").on("click", "a.btn", function() {
111
+ $('.payment-fields').hide();
112
+ $('label span.required').hide();
113
+
114
+ if($(this).hasClass('active')) {
115
+ $('.payment-select a.btn').removeClass('active');
116
+ $('#payment_method').val('');
117
+
118
+ } else {
119
+ $('.payment-select a.btn').removeClass('active');
120
+ $(this).addClass('active');
121
+ var payment = $(this).attr('data-payment-select');
122
+ $('#payment_method').val(payment);
123
+
124
+ if(payment == 'credit'){
125
+ $('#credit-fields').show();
126
+ $('#credit_card_number').focus();
127
+ $('label span.email-required').show();
128
+ $('label span.name-required').show();
129
+ $('label span.contact-required').show();
130
+ } else if(payment == 'comp') {
131
+ $('label span.name-required').show();
132
+ } else if(payment == 'check') {
133
+ $('#check-fields').show();
134
+ }
135
+ }
136
+
137
+ $('#payment_method').trigger('change') // trigger change so form validness can be checked
138
+ });
139
+
140
+ $('#doorlist.page').on('click', 'tr.buyer', function() {
141
+ $('tr.buyer').removeClass('active');
142
+ $(this).addClass('active');
143
+ $('.buyer-details').hide();
144
+ $('#buyer-details').html($(this).find('.buyer-details-hidden').html());
145
+ // set height to height of page so it has it's own scroll
146
+ offset = parseInt($('#buyer-details').css('top'), 10) + $('#buyer-details .content').position().top;
147
+ $('#buyer-details .content').css({height: ($(window).height() - offset) });
148
+ $('#buyer-details').fadeIn(100);
149
+ });
150
+
151
+ $('#doorlist.page').on('click', '.door-list-checkbox', function() {
152
+ var checkbox = $(this);
153
+ buyerId = checkbox.attr('data-buyer-id');
154
+ ticketId = checkbox.attr('data-ticket-id');
155
+ checked = checkbox.is(':checked');
156
+ collection = checked ? 'validated' : 'unvalidated';
157
+
158
+ // check the duplicated checkbox
159
+ $("[data-ticket-id="+ticketId+"]").attr('checked', checked);
160
+
161
+ // set the visual marker at the beginning of the row
162
+ var icon = $("tr.buyer[data-buyer-id="+buyerId+"]").find('.remaining-ticket-indicator')
163
+ available_tickets = $(":checkbox[data-buyer-id="+buyerId+"]:not(:checked)").length;
164
+ unavailable_tickets = $(":checkbox[data-buyer-id="+buyerId+"]:checked").length;
165
+
166
+ icon.removeClass(); // remove current icon
167
+ icon.addClass('remaining-ticket-indicator');
168
+
169
+ if(available_tickets == 0) {
170
+ icon.addClass('fa fa-circle');
171
+ } else if(unavailable_tickets == 0) {
172
+ icon.addClass('fa fa-circle-o');
173
+ } else {
174
+ icon.addClass('fa fa-adjust');
175
+ }
176
+
177
+ updateProgressBar();
178
+
179
+ $.ajax({
180
+ type: "PUT",
181
+ url: '/tickets/'+ticketId+'/'+collection+'.json'
182
+ });
183
+ });
184
+
185
+ $('tr.buyer:first').click();
186
+
187
+ // search doorlist
188
+ $("#page-holder").on("keyup", "#live_search", function(){
189
+ // Retrieve the input field text
190
+ var filter = $(this).val();
191
+
192
+ // Loop through the buyers list
193
+ $("tr.buyer").each(function(){
194
+ // If the list item does not contain the text phrase hide it
195
+ if ($(this).attr('data-search-blob').search(new RegExp(filter, "i")) < 0) {
196
+ $(this).hide();
197
+ } else {
198
+ $(this).show();
199
+ }
200
+ });
201
+
202
+ // if only 1 buyer remains, activate it
203
+ if ($("tr.buyer:visible").length === 1) {
204
+ $("tr.buyer:visible").click();
205
+ // dismiss ios keyboard
206
+ // $('input:focus').blur();
207
+ }
208
+ });
209
+
210
+ $("#user-info #person_first_name").autocomplete({
211
+ minLength: 1,
212
+ source: function(request, response) {
213
+ $.getJSON("/people?utf8=%E2%9C%93&commit=Search", { search: request.term }, function(people) {
214
+ $('#person_first_name').popover('destroy');
215
+ var responsePeople = new Array();
216
+
217
+ $.each(people, function (i, person) {
218
+ person = cleanJsonPerson(person)
219
+ responsePeople[i] = "<li data-person-id='"+person.id+"' data-person-email='"+person.email+"'>";
220
+ responsePeople[i] += "<h4><span class='first-name'>"+person.first_name+"</span> <span class='last-name'>"+person.last_name+"</span></h4>";
221
+ if(person.company_name) { responsePeople[i] += "<span class='company-name'>"+person.company_name+"</span>" }
222
+ responsePeople[i] += "</li>";
223
+ });
224
+
225
+ if(responsePeople.length) {
226
+ $('#person_first_name').popover({
227
+ content: responsePeople,
228
+ html: true,
229
+ title: 'Existing people',
230
+ position: 'left',
231
+ trigger: 'manual',
232
+ container:'#existing-user-popover'
233
+ });
234
+ $('#person_first_name').popover('show');
235
+ }
236
+
237
+ response(null)
238
+ });
239
+ }
240
+ });
241
+
242
+ $('#existing-user-popover').on('click', 'li', function() {
243
+ person = $(this);
244
+ $('#person_first_name').val(person.find('.first-name').text());
245
+ $('#person_last_name').val(person.find('.last-name').text());
246
+ $('#person_email').val(person.attr('data-person-email'));
247
+ $('#person_id').val(person.attr('data-person-id'));
248
+ $('#person_first_name').popover('destroy');
249
+ validateForm();
250
+ return false;
251
+ });
252
+
253
+ // If they enter a custom email address (or change what was autocompleted, we have to clear the person id)
254
+ $("#user-info input").change( function () {
255
+ $("input#person_id").val("");
256
+ $('#person_first_name').popover('destroy');
257
+ })
258
+
259
+ // handle swipe from a magtek card reader
260
+ $('#credit_card_number').keyup(function() {
261
+ var val = $(this).val();
262
+ if (val.slice(0,2) === '%B') {
263
+ length = 0;
264
+
265
+ // there is a delay the data being entered in the field after a swipe
266
+ // make sure we wait until it is all there
267
+ do {
268
+ val = $(this).val();
269
+ setTimeout(length = val.length, 100);
270
+ } while (length < val.length);
271
+
272
+ // shamlessly lifted from
273
+ // https://github.com/fracturedatlas/swiper/
274
+ tracks = val.match(/^%(.*)\?;(.*)\?$/);
275
+ if (tracks) {
276
+ raw_track1 = tracks[1];
277
+ track1_groups = raw_track1.match(/^(.)(\d*)\^([^\/]*)\/(.*)\^(..)(..)(.*)$/);
278
+ $('#credit_card_number').val(track1_groups[2]);
279
+
280
+ month = track1_groups[6];
281
+ if (month[0] === '0') { month = month[1] }; // removing leading zero for select
282
+ $('#credit_card_month').val(month);
283
+
284
+ year = track1_groups[5];
285
+ if (year.length === 2) { year = '20'+year }; // add '20' for select
286
+ $('#credit_card_year').val(year);
287
+ }
288
+ }
289
+ });
290
+
291
+ // disable submit button until form is valid
292
+ $('.boxoffice-form input').change(function() {
293
+ validateForm();
294
+ });
295
+
296
+ $('.boxoffice-form').validate({
297
+ submitHandler: function(form) {
298
+ processForm(form);
299
+ },
300
+ highlight: function(element, errorClass) {
301
+ $(element).closest('.control-group').addClass(errorClass);
302
+ },
303
+ unhighlight: function(element, errorClass) {
304
+ $(element).closest('.control-group').removeClass(errorClass);
305
+ },
306
+ ignore: ".ignore-validation",
307
+ messages: {
308
+ total: null,
309
+ payment_method: 'Please select a payment method.',
310
+ },
311
+ rules: {
312
+ 'total': {
313
+ required: true
314
+ },
315
+ 'payment_method': {
316
+ required: function(element) {
317
+ return (
318
+ parseFloat($('#total').val()) > 0.0
319
+ )
320
+ }
321
+ },
322
+ 'person[first_name]': {
323
+ required: function(element) {
324
+ return (
325
+ $('#payment_method').val() === 'credit' ||
326
+ $('#payment_method').val() === 'comp'
327
+ )
328
+ }
329
+ },
330
+ 'person[last_name]': {
331
+ required: function(element) {
332
+ return (
333
+ $('#payment_method').val() === 'credit' ||
334
+ $('#payment_method').val() === 'comp'
335
+ )
336
+ }
337
+ },
338
+ 'person[email]': {
339
+ email: true,
340
+ required: function(element) {
341
+ return (
342
+ $('#payment_method').val() === 'credit' &&
343
+ !$.trim($('#person_phone').val()).length // empty?
344
+ )
345
+ }
346
+ },
347
+ 'person[phone]': {
348
+ required: function(element) {
349
+ return (
350
+ $('#payment_method').val() === 'credit' &&
351
+ !$.trim($('#person_email').val()).length // empty?
352
+ )
353
+ }
354
+ },
355
+ 'credit_card[number]': {
356
+ creditcard: true,
357
+ required: function(element) {
358
+ return (
359
+ $('#payment_method').val() === 'credit'
360
+ )
361
+ }
362
+ },
363
+ }
364
+ });
365
+ });
366
+
367
+ // called after a successful purchase
368
+ function clearBoxOfficeForm() {
369
+ $(":input:not([type=button],[type=submit],[type=checkbox],button,[name=authenticity_token])").val('');
370
+ $('#auto_check_in[type=checkbox]').prop('checked', false);
371
+ $('input.ticket-quantity').val('0');
372
+ $('.payment-select a.btn').removeClass('active');
373
+ $('#payment_method').val('');
374
+ $('#order-total .message').text('$0.00 Total');
375
+ $('.discount .message').hide();
376
+ $('#person_first_name').popover('destroy');
377
+ $('#checkout-now-button').attr('disabled','disabled');
378
+ };
379
+
380
+ // called after valid form is submitted
381
+ function processForm(form) {
382
+ var f = $(form);
383
+ var modalHeader = $('#submit-confirmation .modal-header h3');
384
+ var modalBody = $('#submit-confirmation .modal-body .content');
385
+ var spinner = $('#submit-confirmation .modal-body .loading');
386
+ modalHeader.text('Processing...');
387
+ modalBody.text('');
388
+ spinner.show();
389
+ $('.modal-close').hide();
390
+
391
+ $('#submit-confirmation').modal({keyboard:false, backdrop:'static'});
392
+
393
+ $.post( f.attr('action'), f.serialize())
394
+ .done(function(response) {
395
+ var message = '';
396
+ if (response['message']) { message += response['message'] };
397
+ if (response['error']) { message += response['error'] };
398
+
399
+ if(response['sale_made']) {
400
+ modalHeader.text('Purchase Complete');
401
+ clearBoxOfficeForm();
402
+ updateRemainingTickets(response['ticket_types']);
403
+ updateDoorlist(response['buyer']['id']);
404
+ } else {
405
+ modalHeader.text('Error');
406
+ }
407
+ modalBody.text(message);
408
+ })
409
+ .fail(function() {
410
+ modalHeader.text('An error has occured.');
411
+ modalBody.text('Please try your purchase again.');
412
+ })
413
+ .always(function() {
414
+ spinner.hide();
415
+ $('.modal-close').show();
416
+ });
417
+ };
418
+
419
+ // called after valid form is submitted
420
+ function updateRemainingTickets(ticket_types) {
421
+ $.each( ticket_types, function( key, ticket ) {
422
+ var row = $('.ticket[data-ticket-id='+ticket.id+']');
423
+ var input = row.find('input');
424
+
425
+ $('.remaining[data-ticket-id='+ticket.id+']').text(ticket.available);
426
+ row.attr('data-remaining', ticket.available);
427
+ input.attr('data-remaining', ticket.available);
428
+
429
+ if(ticket.available > 0) {
430
+ input.removeAttr('disabled');
431
+ row.removeClass('unavailable');
432
+ } else {
433
+ // TODO see if these are in your current cart before disabling
434
+ // input.attr('disabled', 'disabled');
435
+ // row.addClass('unavailable');
436
+ }
437
+ });
438
+ };
439
+
440
+ // utility
441
+ function cleanJsonPerson(jsonPerson) {
442
+ jsonPerson.first_name = ( jsonPerson.first_name == null ? "" : jsonPerson.first_name );
443
+ jsonPerson.last_name = ( jsonPerson.last_name == null ? "" : jsonPerson.last_name );
444
+ jsonPerson.email = ( jsonPerson.email == null ? "" : jsonPerson.email );
445
+ jsonPerson.company_name = ( jsonPerson.company_name == null ? "" : jsonPerson.company_name );
446
+ return jsonPerson
447
+ };
448
+
449
+ function updateSelectedPerson(personId, personFirstName, personLastName, personEmail) {
450
+ $("input#person_id").val(personId);
451
+ $("input#person_first_name").val(personFirstName);
452
+ $("input#person_last_name").val(personLastName);
453
+ $("input#person_email").val(personEmail);
454
+ };
455
+
456
+
457
+ // when a ticket, donation, or discount code is changed
458
+ function updateTotal(response) {
459
+ var total = $('#order-total .message');
460
+ total.text(response['total']*0.01);
461
+ total.formatCurrency({symbol:'$'});
462
+ total.text(total.text() + ' Total');
463
+
464
+ // invalidate form if no tickets and total 0
465
+ if ((response['tickets'].length > 0) || (response['total'] > 0)) {
466
+ $('input#total').val(response['total']);
467
+ } else {
468
+ $('input#total').val('');
469
+ }
470
+ // trigger change so form validness can be checked;
471
+ $('input#total').trigger('change');
472
+
473
+ // update "remaining tickets"
474
+ updateRemainingTickets(response['ticket_types']);
475
+
476
+ // handle discount
477
+ handleDiscount(response)
478
+ };
479
+
480
+ function handleDiscount(response) {
481
+ var discountMessage = $('.discount .message');
482
+ discountMessage.show();
483
+ if(response['discount_error']) {
484
+ // something incorrect with discount code
485
+ discountMessage.text(response['discount_error']);
486
+ } else if(response['discount_amount'] > 0) {
487
+ // success, discount greater than 0
488
+ discountMessage.text(response['discount_amount']*-0.01);
489
+ discountMessage.formatCurrency({symbol:'$'});
490
+ discountMessage.text(discountMessage.text() + ' discount');
491
+ } else if($.trim($('#order-discount').val()).length) {
492
+ // success but discount < 0 and something in discount input
493
+ discountMessage.text('No valid discount found.');
494
+ } else {
495
+ discountMessage.hide();
496
+ }
497
+ }
498
+
499
+ // change doorlist progress bar
500
+ function updateProgressBar() {
501
+ all_available_tickets = $("#door-list-table :checkbox:not(:checked)").length;
502
+ all_unavailable_tickets = $("#door-list-table :checkbox:checked").length;
503
+ total_tickets = all_available_tickets+all_unavailable_tickets;
504
+ width = ((all_unavailable_tickets/total_tickets) * 100)+'%';
505
+ $('.progress .bar').css({width:width});
506
+ $('.progress .bar').text(all_unavailable_tickets+'/'+total_tickets+' checked in');
507
+ };
508
+
509
+ // animate transition
510
+ function switchBetweenBoxofficeAndDoorlist(selectedId) {
511
+ $('.page-nav li').removeClass('active');
512
+ $('[href='+selectedId+']').parent('li').addClass('active');
513
+
514
+ if(selectedId=='#doorlist') {
515
+ $('#boxoffice').animate({right:-sliderOffset}, 200)
516
+ $('#doorlist').animate({left:0}, 200)
517
+ } else {
518
+ $('#doorlist').animate({left:-sliderOffset}, 200)
519
+ $('#boxoffice').animate({right:0}, 200)
520
+ }
521
+ }
522
+
523
+ function updateDoorlist(buyerId) {
524
+ $('#doorlist.page').fadeOut();
525
+ jQuery.get($('#doorlist.page').attr('data-partial-href'), function(data) {
526
+ $('#doorlist.page').html(data);
527
+ updateProgressBar();
528
+ setPageHeight();
529
+ $('#doorlist.page').fadeIn();
530
+ $('tr.buyer[data-buyer-id='+buyerId+']').click();
531
+ })
532
+ }
533
+
534
+ // height of the page has to be set manually
535
+ // because the boxoffice and doorlist pages are positioned absolute (so they can slide)
536
+ function setPageHeight() {
537
+ $('#page-holder').css({
538
+ height: Math.max($('#boxoffice').height(), $('#doorlist').height()) + 300
539
+ });
540
+ }
541
+
542
+ function validateForm() {
543
+ if($('.boxoffice-form').valid()) {
544
+ $('#checkout-now-button').removeAttr('disabled');
545
+ $('#checkout-now-button').addClass('btn-success');
546
+ } else {
547
+ $('#checkout-now-button').attr('disabled','disabled');
548
+ $('#checkout-now-button').removeClass('btn-success');
549
+ }
550
+ }
551
+
552
+ var delay = (function(){
553
+ var timer = 0;
554
+ return function(callback, ms){
555
+ clearTimeout (timer);
556
+ timer = setTimeout(callback, ms);
557
+ };
558
+ })();