PageSourceSearch

https://volumeonetickets.org/js/questionnaire.1788259773.js

js volumeonetickets.org collected 2026-09-25 14:26:05 UTC 27,428 bytes, 737 lines download raw bytes

1// initialize variables
2var gQuestionIndex = 0;
3var gQuestionnaireIndex = 0;
4var atLeastOneOrderQuestion = false;
5var atLeastOneAttendeeQuestion = false;
6var atLeastOneTicketTypeQuestion = false;
7
8//let's set request data to an empty array if it's undefined - saves on undefined checks later on to avoid javascript errors
9if (typeof gRequestData == 'undefined') {
10	gRequestData = [];
11}
12
13if(typeof tsTheme == 'undefined') {
14	tsTheme = 'Classic';
15}
16var questionElementStart = '<span class="question-text">';
17var questionElementEnd = '</span>';
18var questionTextBoxClass = 'form-control';
19var questionTextAreaClass = 'form-control';
20var questionTicketTypePriceNameStart = '<strong>';
21var questionTicketTypePriceNameEnd = '</strong><br>';
22
23// DOCUMENT READY
24$(document).ready(function() {
25
26	if (typeof gManuallyWriteOrderQuestionnaire == 'undefined' && typeof gOrderQuestionnaires != 'undefined') {
27		 writeQuestionnaires(gOrderQuestionnaires, '#orderQuestionnaire', 'questionnaire-order', null, null, null, null, null);
28	}
29
30});
31
32
33// uses variables generated via PHP:
34// gAttendeeQuestionnaires - jsonData of the attendee questionnaire(s)
35
36
37// when a required checkbox is changed, this checks it's siblings
38// and either removes the required attribute from all (if at least one is checked), or adds it to them all if none are
39function questionnaireCheckboxChanged(e) {
40	var atLeastOneChecked = false;
41	var checkboxesToVerify = $(e).closest('.required-checkboxes').find(':checkbox');
42
43	if($(e).is(':checked')) {
44		atLeastOneChecked = true;
45	} else {
46		checkboxesToVerify.each(function() {
47			if($(this).is(':checked')) {
48				atLeastOneChecked = true;
49			}
50		});
51	}
52
53	checkboxesToVerify.each(function() {
54		if(atLeastOneChecked) {
55			$(this).removeAttr('required');
56		} else {
57			$(this).attr('required', 'required');
58		}
59	});
60
61
62}
63
64
65/**
66 * Update Questionnaires(usually called when updating the order summary / calculating total..etc)
67 * shows/hides questions that are ticket-type specific
68 * adds/removes per-attendee questions when quantities are increased/decreased
69 */
70function updateQuestionnaires() {
71	isFrame = window.parent !== window;
72	var quantitiesData = getTicketTypeQuantities();
73	writeOrDeleteAttendeeQuestionnaires(quantitiesData);
74	hasOrderQuestion();
75	hasTicketTypeTiedQuestions(quantitiesData);
76	hasAttendeeQuestion();
77
78	if (!isFrame) {
79		if (atLeastOneOrderQuestion || atLeastOneTicketTypeQuestion) {
80			$('#orderQuestionnaire').slideDown('fast');
81		} else {
82			$('#orderQuestionnaire').slideUp('fast');
83		}
84
85		if (atLeastOneAttendeeQuestion) {
86			$('#attendee-questionnaires-area').slideDown('fast');
87		} else {
88			$('#attendee-questionnaires-area').slideUp('fast');
89		}
90	}
91}
92
93function hasOrderQuestion() {
94	if ($('.questionnaire-order .question').not('.tied-to-ticket-type').not('.tied-to-question-response').length > 0) {
95		atLeastOneOrderQuestion = true;
96		return true;
97	} else {
98		atLeastOneOrderQuestion = false;
99		return false;
100	}
101}
102
103function hasAttendeeQuestion() {
104	if($('#attendee-questionnaires .questionnaire').length) {
105		atLeastOneAttendeeQuestion = true;
106		return true;
107	} else {
108		atLeastOneAttendeeQuestion = false;
109		return false;
110	}
111}
112
113
114
115// gets objects with id:quantity for both ticket types and ticket type prices
116function getTicketTypeQuantities() {
117
118	var ticketTypeId;
119	var ticketTypePriceId;
120	var ticketsPer;
121	var quantity;
122	var questions;
123	var ticketTypesData = {};
124
125	$('[id$="Quantity"]').each(function() {
126		ticketTypeId = $(this).attr('data-ticket-type-id');
127		//for tickets from activity pages, you have to look elsewhere for this info
128		if (typeof ticketTypeId == 'undefined') {
129			ticketTypeId = $(this).siblings('[id$="TicketTypeId"]').val();
130		}
131		ticketTypePriceId = $(this).attr('data-ticket-type-price-id');
132		//for tickets from activity pages, you have to look elsewhere for this info
133		if (typeof ticketTypePriceId == 'undefined') {
134			ticketTypePriceId = $(this).siblings('[id$="TicketTypePriceId"]').val();
135		}
136		ticketsPer = $(this).attr('data-tickets-per');
137		row = $(this).attr('name').match(/\d+/);
138		ticketTypePriceName = $('[name="data[Ticket]['+row+'][ticket_type_name]"]').val();
139
140		quantity = $(this).val();
141		if(quantity == '') {
142			quantity = 0;
143		}
144
145		if(ticketTypesData[ticketTypeId] == undefined) {
146			ticketTypesData[ticketTypeId] = {'quantity':0};
147		}
148		if(ticketTypesData[ticketTypeId][ticketTypePriceId] == undefined) {
149			ticketTypesData[ticketTypeId][ticketTypePriceId] = {'quantity':0, 'ticketsPer':1, 'ticketTypePriceName':ticketTypePriceName};
150		}
151		if(ticketsPer == undefined) {
152			ticketsPer = 1;
153		}
154
155		ticketTypesData[ticketTypeId]['quantity'] += parseInt(quantity)*parseInt(ticketsPer);
156		ticketTypesData[ticketTypeId][ticketTypePriceId]['quantity'] += parseInt(quantity)*parseInt(ticketsPer);
157	});
158	return ticketTypesData;
159}
160
161
162
163
164// writes or deletes attendee questionnaires based on provided {id:quantity, ... object
165function writeOrDeleteAttendeeQuestionnaires(quantitiesData) {
166
167	var existing;
168
169	$.each(quantitiesData, function(ticketTypeId, ticketTypeData) {
170
171		$.each(ticketTypeData, function(ticketTypePriceId, ticketTypePriceData) {
172
173			if (typeof ticketTypePriceData === 'object' && typeof ticketTypePriceData !== null) {
174
175				existing = 0;
176
177				// repeats through existing questionnaires for this ticket type price and delete if over quantity
178				$('.questionnaire-attendee[data-ticket-type-price-id="' + ticketTypePriceId + '"]').each(function() {
179					existing++;
180					if ($(this).attr('data-ticket-type-price-key') >= ticketTypePriceData['quantity']) {
181						$(this).remove();
182					}
183				});
184
185				// if(ticketTypePriceData['quantity'] > 0) {
186				// }
187
188				for(i=existing; i<ticketTypePriceData['quantity']; i++) {
189					writeQuestionnaires(gAttendeeQuestionnaires, '#attendee-questionnaires', 'questionnaire-attendee', ticketTypePriceData['ticketTypePriceName'], ticketTypeId, ticketTypePriceId, i, null);
190				}
191			}
192		});
193
194	});
195}
196
197
198
199
200// repeat through every question that's tied to a specific ticket type and show or hide it based on at least one quantity
201// NOTE: only used for order questionnaires, since attendee questionnaires just won't write the question in the first place
202function hasTicketTypeTiedQuestions(quantitiesData) {
203
204	//repeat through each order-questionnaire
205	var showAtLeastOne = false;
206	$('.questionnaire-order .question.tied-to-ticket-type').each(function() {
207		ticketTypeIds = $(this).attr('data-ticket-type-id').split(',');
208		showMe = false;
209		ticketTypeIds.forEach(function(e){
210			if (typeof quantitiesData[e] !== 'undefined' && quantitiesData[e]['quantity'] > 0) {
211				showMe = true;
212				showAtLeastOne = true;
213			}
214		});
215
216		// show the question if it's questionnaire
217		if (showMe) { //
218			$(this).slideDown('fast').find('[disabled]').removeAttr('disabled');
219		} else {
220			$(this).slideUp('fast').find('input').attr('disabled', 'disabled');
221
222			//find out if there were follow up questions and also hide them
223			nextDiv = $(this).next('div');
224			while (nextDiv.hasClass('tied-to-question-response')) {
225				nextDiv.slideUp('fast').find('input').attr('disabled', 'disabled');
226				nextDiv = nextDiv.next('div');
227			}
228		}
229	});
230
231	if (showAtLeastOne) {
232		atLeastOneTicketTypeQuestion = true;
233		return true;
234	} else {
235		atLeastOneTicketTypeQuestion = false;
236		return false;
237	}
238}
239
240
241
242// Write Questionnaires
243//note: ticketTypeId can be either a string (single id) or an array (multi ids passed with an order specific edit page)
244function writeQuestionnaires(questionnaires, intoElementSelector, questionnaire
244Class, ticketTypePriceName, ticketTypeId, ticketTypePriceId, ticketTypePriceKey, ticketId) {
245
246	if(ticketTypePriceKey == null) {
247		ticketTypePriceKey = 0;
248	}
249
250	if (typeof gRequestData != []) {
251		var hasAnswers = true;
252	} else {
253		var hasAnswers = false;
254	}
255
256	var html = '';
257
258	// open the wrapper element
259	html += '<div class="questionnaire content ' + questionnaireClass + '"';
260
261	if(ticketTypeId !== null && !Array.isArray(ticketTypeId)) {
262		html += ' data-ticket-type-id="' + ticketTypeId + '"';
263	}
264	if(ticketTypePriceId !== null) {
265		html += ' data-ticket-type-price-id="' + ticketTypePriceId + '"';
266	}
267	if(ticketTypePriceKey !== null) {
268		html += ' data-ticket-type-price-key="' + ticketTypePriceKey + '"';
269	}
270	html += '>';
271
272	if(ticketTypePriceName !== null) {
273		html += questionTicketTypePriceNameStart + ticketTypePriceName + ' #' + parseInt(ticketTypePriceKey+1) + questionTicketTypePriceNameEnd;
274	}
275
276	var atLeastOneQuestionWritten = false;
277	var answerNumber = 0;
278	if (typeof ticketId != 'undefined' && ticketId != null) {
279		var tId = ticketId;
280	} else {
281		var tId = ''
282	}
283	$.each(questionnaires, function(i, questionnaire) {
284		var questionnaireIndex = gQuestionnaireIndex;
285		$.each(questionnaire['Question'], function(i, question) {
286			var questionHtml = writeQuestion(question, questionnaire['Questionnaire']['event_id'], ticketTypeId, ticketTypePriceId, questionnaireIndex, tId);
287			if (questionHtml != '') {
288				atLeastOneQuestionWritten = true;
289				html += questionHtml;
290				answerNumber++;
291			}
292		});
293		gQuestionnaireIndex++;
294	});
295
296	// close out the wrapper element
297	html += '</div>';
298
299	if (atLeastOneQuestionWritten) {
300		$(intoElementSelector).append(html);
301
302		// add any callbacks
303		$('.has-follow').on("change", function() {
304
305			// for radio buttons, the 'change' is only triggered on the one selected, not on one that was just automatically unselected by clicking a different one
306			if ($(this).prop('type') == 'radio') {
307				$(this).closest('ul.radio-buttons').find('input').each(function() {
308					checkForFollowUpQuestion(this);
309				});
310
311			} else if ($(this).prop('type') == 'select-one') {
312				$(this).children('option').each(function() {
313					checkForFollowUpQuestion(this);
314				});
315
316			} else {
317				checkForFollowUpQuestion(this);
318			}
319		});
320	}
321}
322
323
324
325
326// Write Question
327// question = question json data
328// eventId = event id
329// selectedTicketTypeId = the type of ticket this question is being written for (used in attendee-specific questionnaires),
330// OR an array of ticket type ids in an order, to know whether to render ticket type specific questions on order questionnaires
331function writeQuestion(question, eventId, selectedTicketTypeId, ticketTypePriceId, questionnaireIndex, ticketId) {
332	//somehow it's getting here with an undefined question occasionally
333	if (typeof question == 'undefined') {
334		return false;
335	}
336
337	//search any gRequestData to see if this question has an answer
338	var hasAnswer = false;
339	var answerItem = [];
340	if (typeof gRequestData['QuestionResponse'] != 'undefined') {
341		$.each(gRequestData['QuestionResponse'], function(idx, value) {
342			if (value['question_id'] == question['id']) {
343				//found an answer, narrow down by ticket id if appropriate
344				if (ticketId != '') {
345					if (value['ticket_id'] != ticketId) {
346						return true; //this is the equivalent of a continue statement in php
347					}
348				}
349				answerItem = value;
350				hasAnswer = true;
351				return false; //this is the equivalent of a break statement in php
352			}
353		});
354	}
355
356	var html = '';
357	var disabled = '';
358	switch (question['response_type']) {
359		case "3":
360		case "4":
361		case "6":
362			var requiredClass = (question['required']) ? 'question_required ' : '';
363			if (typeof questionnaireNotRequired != 'undefined' && questionnaireNotRequired == true) {
364				requiredClass = '';
365			}
366			break;
367		default:
368			var requiredClass = (question['required']) ? 'question_required_text ' : '';
369			if (typeof questionnaireNotRequired != 'undefined' && questionnaireNotRequired == true) {
370				requiredClass = '';
371			}
372		break;
373	}
374
375	// if the question isn't being written for a specific ticket type
376	// or if it is, but matches the questionnaire's ticket type id, continue to write the question
377
378	//first, find out if the ticket type matches - it's buried in the array
379	foundTTID = false;
380	if (typeof question['QuestionsTicketType'] != 'undefined') {
381		for (x=0; x < question['QuestionsTicketType'].length; x++) {
382			if (Array.isArray(selectedTicketTypeId) && selectedTicketTypeId.includes(question['QuestionsTicketType'][x]['ticket_type_id'])) {
383				foundTTID = true;
384			} else if (question['QuestionsTicketType'][x]['ticket_type_id'] === selectedTicketTypeId) {
385				foundTTID = true;
386			}
387		}
388	}
389	if(selectedTicketTypeId == null
390			|| foundTTID //matches ticket type
391			|| (typeof question['QuestionsTicketType'] != 'undefined' && question['QuestionsTicketType'].length === 0)
392			|| typeof question['QuestionsTicketType'] == 'undefined') { //not limited to a ticket type
393
394		var types = { 1: 'text', 2: 'textarea', 3: 'checkboxes', 4:'radiobuttons', 5:'select', 6:'waiver' };
395
396		html += '<div class="' + requiredClass + 'question question-' + gQuestionIndex + ' question-type-' + types[question['response_type']];
397
398		// follow-up question or tied to a specific ticket type
399		if(question['question_option_id'] || question['QuestionsTicketType']?.length > 0) {
400
401			// is a follow-up question
402			if(question['question_option_id']) {
403				html += ' tied-to-question-response" data-question-option-id="' + question['question_option_id'] + '"';
404				disabled = ' disabled="disabled"';
405
406			// is tied to a specific ticket-type
407			} else if (question['QuestionsTicketType']?.length > 0) {
408				// if it's an order questionnaire (ie wasn't in response to the selection of a ticket / attendee questionnaire)
409				// for attendee questionnaires, we don't need to trigger that this QUESTION is tied specifically to a type, since the whole thing is tied
410				if(selectedTicketTypeId == null) {
411					html += ' tied-to-ticket-type"';
412					disabled = ' disabled="disabled"';
413				}
414
415				//need to build a delimited string of all the associated ticket type ids
416				ticketTypeString = '';
417				question['QuestionsTicketType'].forEach(function(e){
418					ticketTypeString += e['ticket_type_id'] + ',';
419				});
420				ticketTypeString = ticketTypeString.slice(0, -1);
421
422				//then use it as the ticket type id data point
423				html += ' data-ticket-type-id="' + ticketTypeString + '"';
424			}
425			html += 'style="display:none;"';
426		}
427		//Add specific ID to support alert if a required checkbox is not filled out
428		if (types[question['response_type']] == 'waiver'){
429			html += '" id="QuestionResponseDiv' + gQuestionIndex + 'QuestionOptionText';
430		}
431		html += '">';
432
433			var required = (question['required']) ? ' required="required"' : '';
434			if (typeof questionnaireNotRequired != 'undefined' && questionnaireNotRequired == true) {
435				required = '';
436			}
437
438			//type specific fields
439			if (selectedTicketTypeId) {
440				html += '<input type="hidden" id="QuestionResponse' + gQuestionIndex + 'TicketTypeId" name="data[QuestionResponse][' + gQuestionIndex + '][ticket_type_id]" value="' + selectedTicketTypeId + '">';
441				html += '<input type="hidden" id="QuestionResponse' + gQuestionIndex + 'TicketTypePriceId" name="data[QuestionResponse][' + gQuestionIndex + '][ticket_type_price_id]" value="' + ticketTypePriceId + '">';
442			}
443			if (ticketId) {
444				html += '<input type="hidden" id="QuestionResponse' + gQuestionIndex + 'TicketId" name="data[QuestionResponse][' + gQuestionIndex + '][ticket_id]" value="' + ticketId + '">';
445			}
446			html += '<input type="hidden" id="QuestionResponse' + gQuestionIndex + 'QuestionnaireIndex" name="data[QuestionResponse][' + gQuestionIndex + '][questionnaire_index]" value="' + questionnaireIndex + '">';
447			html += '<input type="hidden" id="QuestionResponse' + gQuestionIndex + 'EventId" name="data[QuestionResponse][' + gQuestionIndex + '][event_id]" value="' + eventId + '">';
448			html += '<input type="hidden" id="QuestionResponse' + gQuestionIndex + 'QuestionId" name="data[QuestionResponse][' + gQuestionIndex + '][question_id]" value="' + question['id'] + '">';
449			html += '<input type="hidden" id="QuestionResponse' + gQuestionIndex + 'QuestionText" name="data[QuestionResponse][' + gQuestionIndex + '][question_text]" value="' + question['question_text'] + '">';
450			html += '<input type="hidden" id="QuestionResponse' + gQuestionIndex + 'QuestionnaireId" name="data[QuestionResponse][' + gQuestionIndex + '][questionnaire_id]" value="' + question['questionnaire_id'] + '">';
451			html += '<input type="hidden" id="QuestionResponse' + gQuestionIndex + 'ResponseType" name="data[QuestionResponse][' + gQuestionIndex + '][response_type]" value="' + question['response_type'] + '">';
452
453			var attendeeName = question['attendee_name'] ? 1 : 0;
454			var attendeeEmail = question['attendee_email'] ? 1 : 0;
455			html += '<input type="hidden" id="QuestionResponse' + gQuestionIndex + 'AttendeeName" name="data[QuestionResponse][' + gQuestionIndex + '][attendee_name]" value="' + attendeeName + '">';
456			html += '<input type="hidden" id="QuestionResponse' + gQuestionIndex + 'AttendeeEmail" name="data[QuestionResponse][' + gQuestionIndex + '][attendee_email]" value="' + attendeeEmail + '">';
457
458			if (hasAnswer) {
459				answerNumber = 0;
460				//retrieve the id (or could be multiple ids if this is a checkbox)
461				if (typeof answerItem['id'] == 'undefined') {
462					//this answer's actual data is in a further array - potentially multiple answers
463					var idArray = {};
464					var idString = '';
465					$.each(answerItem['question_option_id'], function(x, innerValue){
466						if (typeof innerValue['id'] == 'undefined') {
467							if (x == 'id') {
468								idArray['id'] = innerValue;
469							} else {
470								idArray['option_id'] = innerValue;
471							}
472						} else {
473							idArray[x] = {};
474							idArray[x]['id'] = innerValue['id'];
475							idArray[x]['option_id'] = innerValue['option_id'];
476						}
477					});
478					//now convert to JSON
479					idString = encodeURIComponent(JSON.stringify(idArray));
480				} else {
481					//this answer's data is available right here
482					var idString = answerItem['id'];
483				}
484				html += '<input type="hidden" id="QuestionResponse' + gQuestionIndex + 'Id" name="data[QuestionResponse][' + gQuestionIndex + '][id]" value="' + idString + '">';
485			}
486
487			switch (question['response_type']) {
488
489				case '1': // Text
490					var value = '';
491					if (hasAnswer) {
492						answerStr = answerItem['question_option_text'];
493						var value = answerStr.replace(/["]/g, '&quot;');
494					}
495					html += questionElementStart + question['question_text'] + questionElementEnd;
496					html += '<input type="' + (attendeeEmail == 1 ? 'email' : 'text') + '" id="QuestionResponse' + gQuestionIndex + 'QuestionOptionText" name="data[QuestionResponse][' + gQuestionIndex + '][question_option_text]" value="' + value + '" class="' + questionTextBoxClass + '"' + required + disabled + '>';
497					break;
498
499				case '2': // Textarea
500					var value = '';
501					if (hasAnswer) {
502						answerStr = answerItem['question_option_text'];
503						var value = answerStr.replace(/["]/g, '&quot;');
504					}
505					html += questionElementStart + question['question_text'] + questionElementEnd;
506					html += '<textarea id="QuestionResponse' + gQuestionIndex + 'QuestionOptionText" name="data[QuestionResponse][' + gQuestionIndex + '][question_option_text]" cols="30" rows="6" class="' + questionTextAreaClass + '"' + required + disabled + '>' + value + '</textarea>';
507					break;
508
509				case '6': // Waiver
510					var value = '';
511					if (hasAnswer) {
512						var value = answerItem['question_option_text']
513					}
514					if (required){
515						required += ' oninvalid="checkbox_alert(this, this.parentNode.id)"';
516					}
517					if (question['required'] && tsTheme == 'Classic' && (typeof questionnaireNotRequired == 'undefined' || questionnaireNotRequired != true)) {
518						html += '<div class="checkbox-required" id="QuestionResponseDiv' + gQuestionIndex + 'QuestionOptionText">';
519					}
520					html += '<label>';
521					if (value != '') {
522						html += '<input type="checkbox" id="QuestionResponse' + gQuestionIndex + 'QuestionOptionText" name="data[QuestionResponse][' + gQuestionIndex + '][question_option_text]" checked="checked"' + required + disabled + ' class="mr-2">';
523					} else {
524						html += '<input type="checkbox" id="QuestionResponse' + gQuestionIndex + 'QuestionOptionText" name="data[QuestionResponse][' + gQuestionIndex + '][question_option_text]"' + required + disabled + ' class="mr-2">';
525					}
526					html += '<span class="question-text" for="QuestionResponse' + gQuestionIndex + 'QuestionOptionId">' + question['question_text'] + '</span>';
527
528					html += '</label>';
529					if (typeof question['QuestionOption'][0] != 'undefined') {
530						html += '<div style="font-weight:normal;max-height:150px;overflow:auto;padding-left:20px;">' + question['QuestionOption'][0]['response_text'] + '</div>';
531					}
532					if (question['required'] && tsTheme == 'Classic' && (typeof questionnaireNotRequired == 'undefined' || questionnaireNotRequired != true)) {
533						html += '</div>';
534					}
535					break;
536
537				case '3': // Checkboxes
538				case '4': // Radio Buttons
539				case '5': // Dropdown
540					var value = '';
541					if (hasAnswer) {
542						var value = answerItem['question_option_id']
543					}
544					html += writeQuestionOptions(question, required, disabled, hasAnswer, value);
545					break;
546
547			}
548
549		html += '</div><!-- close question -->';
550		gQuestionIndex++;
551
552		// writes any follow-up questions if needed
553		if (question['has_follow'] == true) {
554			$.each(question['QuestionOption'], function(i, option) {
555				if (option['FollowUpQuestion'] && jQuery.isEmptyObject(option['FollowUpQuestion']) == false) {
556					html += writeQuestion(option['FollowUpQuestion'][0], eventId, selectedTicketTypeId, ticketTypePriceId, questionnaireIndex, ticketId);
557				}
558			});
559		}
560	}
561
562	return html;
563}
564
565
566
567// Write Question Options
568function writeQuestionOptions(question, required, disabled, hasAnswer, value) {
569
570	var optionIndex = 0;
571	var html = '';
572	var onChangeText = '';
573	var optionClass = 'no-follow';
574
575	if (question['has_follow']) {
576		optionClass = 'has-follow';
577	}
578
579	var requiredScrollIntoViewOnInvalid = '';
580	if (required) {
581		requiredScrollIntoViewOnInvalid = ' oninvalid="this.scrollIntoView({behavior: \'smooth\', block: \'center\'});"';
582	}
583	
584	switch(question['response_type']) {
585
586	// Checkbox *******************
587		case '3':
588			html += '<span class="question-text">' + question['question_text'] + '</span>';
589			html += '<ul class="checkboxes';
590			if(required) {
591				html += ' required-checkboxes';
592			}
593			html += '">';
594				$.each(question['QuestionOption'], function(i, option) {
595
596					//check if max selected - get settings based on sel_quantity
597					var optionSettings = checkSelectedQuantity(option, disabled);
598
599					var checked = '';
600					//the value might be an array containing a single id-value pair or an array of multiple id-value pairs
601					if (typeof value['id'] == 'undefined') {
602						$.each(value, function(j, val){
603							if (hasAnswer && val['option_id'] == option['id']) {
604								checked = ' checked="checked" ';
605							}
606						});
607					} else {
608						if (hasAnswer && value['option_id'] == option['id']) {
609							var checked = ' checked="checked" ';
610						}
611					}
612
613					html += '<li' + optionSettings['additionalAttributes'] + '><label>';
614						html += '<input type="checkbox" name="data[QuestionResponse][' + gQuestionIndex + '][question_option_id][]" value="' + optionSettings['quantity'] + '" class="' + optionClass + '"' + checked + required + optionSettings['disabled'] + ' onchange="questionnaire
614CheckboxChanged(this);"' + requiredScrollIntoViewOnInvalid + '>' + option['response_text'] + optionSettings['nameAddon'];
615					html += '</label></li>';
616				});
617			html += '</ul>';
618			break;
619
620
621		// Radio *******************
622		case '4':
623			html += '<span class="question-text">' + question['question_text'] + '</span>';
624			html += '<ul class="radio-buttons';
625			if(required) {
626				html += ' required-radio-buttons';
627			}
628			html += '">';
629				$.each(question['QuestionOption'], function(i, option) {
630
631					//check if max selected - get settings based on sel_quantity
632					var optionSettings = checkSelectedQuantity(option, disabled);
633
634					var checked = '';
635					if (hasAnswer && value['option_id'] == option['id']) {
636						var checked = ' checked="checked" ';
637					}
638
639					html += '<li' + optionSettings['additionalAttributes'] + '><label>';
640						html += '<input type="radio" name="data[QuestionResponse][' + gQuestionIndex + '][question_option_id]" value="' + optionSettings['quantity'] + '" class="' + optionClass + '"'  + checked + required + optionSettings['disabled'] + requiredScrollIntoViewOnInvalid + '>' + option['response_text'] + optionSettings['nameAddon'];
641					html += '</label></li>';
642				});
643			html += '</ul>';
644			break;
645
646
647		// Dropdown ********************
648		case '5':
649			html += '<span class="question-text">' + question['question_text'] + '</span>';
650			html += '<select id="QuestionResponse' + gQuestionIndex + 'QuestionOptionId" name="data[QuestionResponse][' + gQuestionIndex + '][question_option_id]" class="form-control ' + optionClass + '"' + required + disabled + requiredScrollIntoViewOnInvalid + '>';
651				html += '<option value="">Select One</option>';
652				$.each(question['QuestionOption'], function(i, option) {
653
654					//check if max selected - get settings based on sel_quantity
655					var optionSettings = checkSelectedQuantity(option, disabled);
656
657					var checked = '';
658					if (hasAnswer && value['option_id'] == option['id']) {
659						var checked = ' selected="selected" ';
660					}
661
662					html += '<option ' + checked + ' value="' + optionSettings['quantity'] + '"' + optionSettings['disabled'] + optionSettings['additionalAttributes'] + '>' + option['response_text'] + optionSettings['nameAddon'] + '</option>';
663					optionIndex++;
664				});
665			html += '</select>';
666			break;
667	}
668
669	return html;
670}
671
672
673// run when questions with follow-ups have their values changed
674// determines whether to show or hide any follow-ups
675function checkForFollowUpQuestion(object) {
676	var questionOptionId = $(object).val();
677	var showFollowUp = false;
678
679	// gets the 'type' of input (or it's nodeName if it's an 'option')
680	var objectType = $(object).prop('type');
681	if (objectType == undefined) {
682		objectType = $(object).prop('nodeName');
683	}
684
685	//checks if it the calling element is selected
686	switch (objectType) {
687
688		case 'checkbox':
689		case 'radio':
690			showFollowUp = $(object).prop('checked');
691			break;
692
693		case 'OPTION':
694			showFollowUp = $(object).prop('selected');
695			break;
696	}
697
698	if (showFollowUp) {
699		$(object).closest('.questionnaire').find('.tied-to-question-response[data-question-option-id="' + questionOptionId + '"]').slideDown('fast').find('input, select, textarea, option').removeAttr('disabled');
700	} else {
701		$(object).closest('.questionnaire').find('.tied-to-question-response[data-question-option-id="' + questionOptionId + '"]').slide
701Up('fast').find('input, select, textarea, option').attr('disabled', 'disabled');
702	}
703
704}
705
706/**
707 * [checkSelectedQuantity description]
708 * @param  {[type]} option [the complete array for the option that is to be checked for max limit]
709 * @return {[type]}        ['option id', 'disabled status', 'additional style setting', 'additional option text']
710 */
711function checkSelectedQuantity(option, disabled) {
712
713	var toReturn = {
714		'quantity': option['id'],
715		'disabled': '',
716		'additionalAttributes': '',
717		'nameAddon': ''
718	};
719
720	if(disabled == ' disabled="disabled"') {
721		toReturn['disabled'] = disabled;
722	}
723
724	//check if it has a max limit set
725	if(option['max_quantity']){
726		if(option['sel_quantity'] >= option['max_quantity']){
727			var toReturn = {
728				'quantity': '0',
729				'disabled': ' disabled="disabled"',
730				'additionalAttributes': ' style="color:#ccc;"',
731				'nameAddon': '(not available)'
732			};
733		}
734	}
735
736	return toReturn;
737}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.