1/** 2 * QSM Progress Bar - Minimized 3 * @package QSM 4 */ 5(function($) { 6 QSMPagination.ProgressBar = { 7 quizObjects: {}, 8 9 init: function() { 10 const self = this; 11 // Look for quiz containers first (new structure) 12 $('.qsm-quiz-container').each(function() { 13 const $container = $(this); 14 const $form = $container.find('.qsm-quiz-form'); 15 const quizId = $container.data('quiz-id'); 16 if (quizId && !self.quizObjects[quizId]) { 17 self.initProgressBar(quizId, $container, $form); 18 } 19 }); 20 21 this.bindEvents(); 22 }, 23 24 initProgressBar: function(quizId, $container, $form, $initial_page = 1) { 25 const data = qmn_quiz_data && qmn_quiz_data[quizId] ? qmn_quiz_data[quizId] : {}; 26 if (!data.progress_bar || Number(data.progress_bar) === 0) return; 27 28 // Look for progress bar in container first, then form (for new structure) 29 let $bar = jQuery('.qsm-progress-bar-' + quizId); 30 31 if (!$bar.length) return; 32 33 // Determine render mode: 'auto' | 'svg' | 'simple' (provided via data-progress-mode) 34 let mode = $bar.data('progress-mode') || 'auto'; 35 36 // Count pages - use same selectors as navigation system 37 const $pages = $container.find('.qsm-page'); 38 const totalPages = Math.max($pages.length, 1); 39 40 // Calculate question pages (exclude first page if present) 41 const hasFirstPage = (Number(data.disable_first_page) !== 1); 42 const questionPages = hasFirstPage ? totalPages - 1 : totalPages; 43 44 this.quizObjects[quizId] = { 45 $container: $container, 46 $form: $form, 47 $bar: $bar, 48 $fill: $bar.find('.qsm-progress-fill'), 49 $text: $bar.find('.progressbar-text'), 50 totalPages: totalPages, 51 questionPages: questionPages, 52 hasFirstPage: hasFirstPage, 53 currentPage: $initial_page, 54 mode: mode, 55 bar: null 56 }; 57 58 jQuery(document).trigger('qsm_init_progressbar_before', [quizId, qmn_quiz_data]); 59 60 // Initialize ProgressBar.js Line only when library is available 61 if (window.ProgressBar && typeof ProgressBar.Line === 'function') { 62 window.qmn_quiz_data[quizId].bar = new ProgressBar.Line('#qsm_progress_bar_' + quizId, { 63 strokeWidth: 2, 64 easing: 'easeInOut', 65 duration: 1400, 66 color: '#3498db', 67 trailColor: '#eee', 68 trailWidth: 1, 69 svgStyle: { width: '100%', height: '100%' }, 70 text: { 71 style: { 72 'position': 'absolute', 73 'right': '10px', 74 'font-size': '13px', 75 'font-weight': 'bold' 76 }, 77 autoStyleContainer: false 78 }, 79 from: { color: '#3498db' }, 80 to: { color: '#ED6A5A' } 81 }); 82 } 83 84 jQuery(document).trigger('qsm_init_progressbar_after', [quizId, qmn_quiz_data]); 85 86 $bar.show(); 87 this.updateProgress(quizId, $initial_page); 88 }, 89 90 bindEvents: function() { 91 const self = this; 92 $(document).on('qsm_go_to_page_before', function(e, quizId, pageNumber) { 93 self.updateProgress(quizId, pageNumber); 94 }); 95 $(document).on('qsm_quiz_initialized', function(e, quizId, instance) { 96 if (!self.quizObjects[quizId]) { 97 // Use container and form from navigation instance 98 self.initProgressBar(quizId, instance.quizContainer, instance.$form); 99 } 100 }); 101 }, 102 103 updateProgress: function(quizId, currentPage) { 104 const currentQuiz = this.quizObjects[quizId]; 105 if (!currentQuiz) { 106 return; 107 } 108 109 // If currentPage is not provided, try to get it from navigation system 110 if (typeof currentPage === 'undefined' && QSMPagination && QSMPagination.Navigation) { 111 const navInstance = QSMPagination.Navigation.quizObjects[quizId]; 112 if (navInstance) { 113 currentPage = navInstance.currentPage; 114 } 115 } 116 117 // Fallback to instance currentPage if still undefined 118 if (typeof currentPage === 'undefined') { 119 currentPage = currentQuiz.currentPage; 120 } 121 122 currentQuiz.currentPage = currentPage; 123 124 // Calculate progress based on question pages only (exclude first page) 125 let effectiveCurrentPage = 0; 126 const effectiveTotalPages = currentQuiz.questionPages; 127 128 if (currentQuiz.hasFirstPage) { 129 // First page exists - only count progress from page 2 onwards 130 if (Number(currentPage) === 1) { 131 // On start page (welcome page) - 0% progress 132 effectiveCurrentPage = 0; 133 } else { 134 // On question pages - calculate progress 135 effectiveCurrentPage = currentPage - 1; 136 } 137 } else { 138 // No first page - all pages are question pages 139 effectiveCurrentPage = currentPage; 140 } 141 142 // Safeguard against division by zero and ensure val
142id percentage 143 let progress = 0; 144 if (effectiveTotalPages > 0) { 145 progress = Math.max(0, Math.min(100, (effectiveCurrentPage / effectiveTotalPages) * 100)); 146 } 147 148 // Ensure progress bar stays visible during all page transitions 149 // This is critical for single-page quizzes where bar might hide on navigation 150 currentQuiz.$bar.show(); 151 // Use SVG ProgressBar.js when in svg mode and instance exists, otherwise fallback to simple width animation 152 let animate_value = progress / 100; 153 if (animate_value <= 1 && window.qmn_quiz_data && qmn_quiz_data[quizId] && qmn_quiz_data[quizId].bar) { 154 qmn_quiz_data[quizId].bar.animate(animate_value); 155 let old_text = currentQuiz.$text.text().replace(' %', ''); 156 let new_text = Math.round(animate_value * 100); 157 if (!old_text || isNaN(parseInt(old_text, 10))) { 158 old_text = 0; 159 } 160 jQuery({ 161 Counter: parseInt(old_text, 10) 162 }).animate({ 163 Counter: new_text 164 }, { 165 duration: 1000, 166 easing: 'swing', 167 step: function () { 168 currentQuiz.$text.text(Math.round(this.Counter) + ' %'); 169 } 170 }); 171 } 172 } 173 174 }; 175 176 $(document).ready(function() { 177 QSMPagination.ProgressBar.init(); 178 }); 179 180})(jQuery);
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.