1function capitalizeEachWord(str) { 2 return str.split(' ').map(function(word) { 3 return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); 4 }).join(' '); 5} 6 7$(document).ready(function() { 8 // Fetch base URL from meta tag 9 const baseURL = $('meta[name="base-url"]').attr('content'); 10 const csrfToken = $('meta[name="csrf-token"]').attr('content'); 11 const isPartnerIP = $('meta[name="is-partner-ip"]').attr('content'); 12 const isPayPerArticle = $('meta[name="is-pay-per-article"]').attr('content'); 13 14 let currentStep = 1; // Start at the first step 15 let totalSteps = 0; 16 let question = []; // Array to store question IDs 17 let answer = []; // Array to store answers 18 19 // Function to initialize the arrays for question IDs and answers 20 function initializeQuestionnaire(data) { 21 totalSteps = data.length + 1; 22 question = data.map(item => item.id); // Store all question IDs 23 answer = Array((totalSteps - 1)).fill(''); // Initialize the answer array with empty strings 24 } 25 26 // Function to get the current step's answer and update the answer array 27 function storeCurrentStepAnswer() { 28 // Only store answers if we're at step 2 or higher 29 if (currentStep > 1) { // Check if we are at step 2 or higher 30 const currentItem = $('.tjp-feedback-item.active'); 31 const currentQuestionId = question[currentStep - 2]; // Get current question ID (adjust for zero-based index) 32 33 let userAnswer = ''; 34 35 // Check if the current question is a radio button question 36 let selectedRadio = currentItem.find('input[type="radio"]:checked'); 37 if (selectedRadio.length > 0) { 38 userAnswer = selectedRadio.val(); 39 40 // If the selected option is "Others", get the text input value 41 if (userAnswer === "Others") { 42 const otherInput = currentItem.find('input[type="text"][name="question_' + currentQuestionId + '_other"]'); 43 userAnswer = otherInput.val().trim(); // Get value of the "Others" text input 44 } 45 } 46 47 // Check if the current question is a text area question 48 let textAreaInput = currentItem.find('textarea'); 49 if (textAreaInput.length > 0) { 50 userAnswer = textAreaInput.val(); 51 } 52 53 // Store the answer in the array 54 answer[currentStep - 2] = userAnswer || ''; // Adjust index for zero-based array 55 } 56 } 57 58 // Function to handle the "I Don't Want To" action 59 function handleSkipStep() { 60 const validationMessage = $('.tjp-popup-feedback__message'); 61 // Clear any previous validation messages 62 validationMessage.text('').removeClass('active'); 63 answer[currentStep - 1] = ''; // Set answer as empty for "I Don't Want To" 64 } 65 66 // Function to handle AJAX request to the questionnaire list 67 function fetchQuestionnaireList() { 68 $.ajax({ 69 url: baseURL + '/questionnaire/list', // Construct full URL using baseURL 70 type: 'GET', 71 dataType: 'json', 72 success: function(response) { 73 if (!response.error) { 74 const greetings = response.data.greetings; 75 const questions = response.data.questions; 76 totalSteps = questions.length + 1; 77 showFeedback(); 78 clearFeedbackItems(); 79 initializeQuestionnaire(questions); // Initialize arrays for questions/answers 80 generateGreetingItem(greetings); 81 generateFeedbackItems(questions); // Call function to generate HTML 82 updateProgress(); // Initialize progress bar 83 } else { 84 console.error('Error fetching data:', response.message); 85 } 86 }, 87 error: function(xhr, status, error) { 88 console.error('Error fetching questionnaire list:', error); 89 } 90 }); 91 } 92 93 // Function to submit the questionnaire 94 function submitQuestionnaire() { 95 $(".tjp-feedback-btn--next").html(`<i class="fa fa-spinner fa-spin"></i> Processing`); 96 $(".tjp-feedback-btn--next").prop("disabled", true); 97 // Create a new FormData object 98 let formData = new FormData(); 99 100 // Append question and answer pairs to the FormData object 101 question.forEach((q, index) => { 102 formData.append('questionnaire_id[]', q); // Append each question ID 103 formData.append('answer[]', answer[index]); // Append corresponding answer 104 }); 105 formData.append('unique_id', getRandomId()); // Append each question ID 106 107 // Make the AJAX POST request 108 $.ajax({ 109 url: baseURL + '/questionnaires/submit', // Your endpoint URL 110 headers: { 111 'X-CSRF-Token': csrfToken, 112 }, 113 type: 'POST', 114 data: formData, 115 processData: false, // Important to prevent jQuery from processing the data
116 contentType: false, // Important to set this to false for FormData 117 success: function(response) { 118 $(".tjp-feedback-btn--next").html(`Submit`); 119 $(".tjp-feedback-btn--next").prop("disabled", false); 120 localStorage.setItem('questionnaireSubmitted', 'true'); 121 showOutro(); 122 }, 123 error: function(xhr, status, error) { 124 $(".tjp-feedback-btn--next").html(`Submit`); 125 $(".tjp-feedback-btn--next").prop("disabled", false); 126 console.error('Submission failed:', error); 127 } 128 }); 129 } 130 131 // Function to clear only dynamic feedback items (index > 0) 132 function clearFeedbackItems() { 133 $('.tjp-feedback-item').not(':first').remove(); // Remove all feedback items except the first 134 } 135 136 // Function to clear input fields in the current step 137 function clearCurrentStepInputs() { 138 const currentItem = $('.tjp-feedback-item.active'); 139 140 // Clear text inputs 141 currentItem.find('input[type="text"]').val(''); 142 143 // Clear radio buttons 144 currentItem.find('input[type="radio"]').prop('checked', false); 145 146 // Clear text areas 147 currentItem.find('textarea').val(''); 148 } 149 150 function validateCurrentStepInputs() { 151 const currentItem = $('.tjp-feedback-item.active'); // Current active feedback step 152 const validationMessage = $('.tjp-popup-feedback__message'); 153 154 // Clear any previous validation messages 155 validationMessage.text('').removeClass('active'); 156 157 // Check for radio buttons 158 const radioButtons = currentItem.find('input[type="radio"]'); 159 const selectedRadio = currentItem.find('input[type="radio"]:checked'); 160 161 // If there are radio buttons and none are selected, show an error 162 if (radioButtons.length > 0 && selectedRadio.length == 0) { 163 validationMessage.text('Please select one of the options.').addClass('active'); 164 return false; 165 } 166 167 // If "Others" is selected, validate the accompanying text input 168 if (selectedRadio.val() == "Others") { 169 const otherInput = currentItem.find('input[type="text"][name^="question_"][name$="_other"]'); 170 if (otherInput.val().trim() == '') { 171 validationMessage.text('Please fill in the "Others" field.').addClass('active'); 172 return false; 173 } 174 } 175 176 // Check for text area input 177 const textAreaInput = currentItem.find('textarea'); 178 if (textAreaInput.length > 0 && textAreaInput.val().trim() == '') { 179 validationMessage.text('Please fill in the text area.').addClass('active'); 180 return false; 181 } 182 183 const otherRadio = currentItem.find('input[type="radio"][value="Others"]'); 184 // Check for standalone text inputs (e.g., "Others" input without radio button) 185 const textInput = currentItem.find('input[type="text"]'); 186 if (textInput.length > 0 && !otherRadio.length && textInput.val().trim() === '') { 187 validationMessage.text('Please fill in the required text field.').addClass('active'); 188 return false; 189 } 190 191 // If no validation conditions are triggered, return true (valid) 192 return true; 193 } 194 195 function generateGreetingItem(data) { 196 const feedbackContainer = $('.tjp-popup-feedback__content'); // Assuming this is where you want to display it 197 const greetingsHTML = ` 198 <div class="tjp-feedback-item active"> 199 <h1 class="tjp-feedback-title">${data.title}</h1> 200 <div class="tjp-feedback-description"> 201 <p>${data.description}</p> 202 </div> 203 </div> 204 `; 205 feedbackContainer.prepend(greetingsHTML); // Add greetings to the top of the feedback content 206 } 207 208 // Function to generate feedback items based on the response 209 function generateFeedbackItems(data) { 210 var feedbackContainer = $('.tjp-popup-feedback__content'); 211 212 data.forEach(function(item, index) { 213 // Create the feedback item container 214 var feedbackItem = $('<div class="tjp-feedback-item"></div>'); 215 216 // Add the question title 217 var questionTitle = $('<h1 class="tjp-feedback-title"></h1>').text(item.question); 218 feedbackItem.append(questionTitle); 219 220 // Check question type and append relevant input fields 221 if (item.question_type === 'option') {
222 var optionsContainer = $('<div class="tjp-feedback-options"></div>'); 223 224 // Loop through the options and create radio buttons 225 item.options.forEach(function(option, idx) { 226 var optionItem = $('<div class="tjp-feedback-options__item"></div>'); 227 var radioButton = ` 228 <div class="tjp-radio-button"> 229 <input type="radio" id="q${item.id}_option_${idx}" name="question_${item.id}" value="${option}"> 230 <label for="q${item.id}_option_${idx}"> 231 <span class="name">${capitalizeEachWord(option)}</span> 232 </label> 233 </div>`; 234 optionItem.append(radioButton); 235 optionsContainer.append(optionItem); 236 }); 237 feedbackItem.append(optionsContainer); 238 239 } else if (item.question_type === 'option_with_text') { 240 // Handle "option_with_text" question type
241 var optionsContainer = $('<div class="tjp-feedback-options"></div>'); 242 243 // Loop through the options and create radio buttons 244 item.options.forEach(function(option, idx) { 245 if (option.toLowerCase() === "others") { 246 var otherInput = ` 247 <div class="tjp-feedback-options__item tjp-other-option"> 248 <div class="tjp-radio-button"> 249 <input type="radio" id="q${item.id}_other_radio" name="question_${item.id}" value="Others"> 250 <label for="q${item.id}_other_radio"> 251 <span class="name"> </span> 252 </label> 253 <input type="text" id="q${item.id}_other_text" name="question_${item.id}_other" placeholder="Others..."> 254 </div> 255 </div>`; 256 optionsContainer.append(otherInput); 257 258 // Attach event listener for the input text field 259 optionsContainer.on('input', `#q${item.id}_other_text`, function() { 260 let textValue = $(this).val(); 261 262 // Automatically select the "Others" radio button when the user types something 263 if (textValue.trim() !== "") { 264 $(`#q${item.id}_other_radio`).prop('checked', true); 265 } else { 266 $(`#q${item.id}_other_radio`).prop('checked', false); 267 } 268 }); 269 } else { 270 var optionItem = $('<div class="tjp-feedback-options__item"></div>'); 271 var radioButton = ` 272 <div class="tjp-radio-button"> 273 <input type="radio" id="q${item.id}_option_${idx}" name="question_${item.id}" value="${option}"> 274 <label for="q${item.id}_option_${idx}"> 275 <span class="name">${capitalizeEachWord(option)}</span> 276 </label> 277 </div>`; 278 optionItem.append(radioButton); 279 optionsContainer.append(optionItem); 280 } 281 }); 282 feedbackItem.append(optionsContainer); 283 284 } else if (item.question_type === 'text') { 285 // For "text" type, append a textarea 286 var textArea = ` 287 <div class="tjp-feedback-form"> 288 <textarea class="tjp-feedback-form__input" id="q${item.id}_textarea" name="question_${item.id}" rows="6" placeholder="Type here..."></textarea> 289 </div>`; 290 feedbackItem.append(textArea); 291 } 292 293 // Append the generated feedback item to the container 294 feedbackContainer.append(feedbackItem); 295 }); 296 } 297 298 function showFeedback() { 299 $(".tjp-popup-feedback__loader").removeClass("active"); 300 $(".tjp-popup-feedback__content").addClass("active"); 301 $(".tjp-popup-feedback__footer").addClass("active"); 302 } 303 304 function showOutro() { 305 $(".tjp-popup-feedback__loader").removeClass("active"); 306 $(".tjp-popup-feedback__content").removeClass("active"); 307 $(".tjp-popup-feedback__message").removeClass("active"); 308 $(".tjp-popup-feedback__footer").removeClass("active"); 309 $(".tjp-popup-feedback__complete").addClass("active"); 310 } 311 312 function loadFeedback() { 313 $(".tjp-popup-feedback__loader").addClass("active"); 314 $(".tjp-popup-feedback__content").removeClass("active"); 315 $(".tjp-popup-feedback__footer").removeClass("active"); 316 } 317 318 // Update the progress bar width 319 function updateProgress() { 320 if (currentStep <= totalSteps) { 321 let progressPercentage = (currentStep / totalSteps) * 100; 322 $('.tjp-feedback-progress').css('width', progressPercentage + '%'); 323 } 324 } 325 326 // Function to move to the next feedback step 327 function moveToNextFeedback() { 328 $('.tjp-feedback-item.active').removeClass('active').next().addClass('active'); 329 currentStep++; 330 updateProgress(); 331 } 332 333 // Event handler for the "Next" button 334 $('.tjp-popup-feedback').on('click', '.tjp-feedback-btn--next', function() { 335 if (currentStep > 0) { 336 $(".tjp-feedback-btn--skip").addClass("active"); 337 } 338 339 // Validate inputs for the current step 340 if (currentStep <= totalSteps) { 341 if (validateCurrentStepInputs()) { 342 storeCurrentStepAnswer(); // Store current answer 343 344 // Move to the next step 345 moveToNextFeedback(); 346 } 347 } 348 349 // Update button text and visibility 350 if (currentStep == totalSteps) { 351 $(".tjp-feedback-btn--next").html("Submit"); 352 $(".tjp-feedback-btn--skip").hide(); 353 } 354 355 // If currentStep exceeds totalSteps, submit the questionnaire 356 if (currentStep > totalSteps) { 357 submitQuestionnaire(); 358 } 359 }); 360 361 // Event handler for the "I Don't Want To" button 362 $('.tjp-popup-feedback').on('click', '.tjp-feedback-btn--skip', function() { 363 if (currentStep <= totalSteps) { 364 clearCurrentStepInputs(); 365 handleSkipStep(); // Set answer to empty 366 moveToNextFeedback(); // Move to next step 367 } 368 }); 369 370 $(".tjp-feedback-close").click(function() { 371 $(".tjp-popup-feedback").removeClass("active"); 372 }) 373 374 $('.tjp-popup-feedback').on('click', '.tjp-feedback-btn--close', function() { 375 $(".tjp-popup-feedback").removeClass("active"); 376 }); 377 378 379 if (isPartnerIP == '1' && isPayPerArticle == '0') { 380 if (localStorage.getItem('questionnaireSubmitted') !== 'true') { 381 loadFeedback(); 382 fetchQuestionnaireList(); 383 384 setTimeout(() => { 385 $(".tjp-popup-feedback").addClass("active"); 386 }, 3000); 387 } 388 } 389});
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.