1/** 2 * BuddyPress Polls - Widget JavaScript 3 * 4 * Handles poll widget functionality including AJAX requests, 5 * progress bar animations, and dynamic updates. 6 * 7 * @package BuddyPress_Polls 8 * @since 4.5.0 9 */ 10 11(function($) { 12 'use strict'; 13 14 /** 15 * Poll Activity Graph Widget Handler 16 * Displays BuddyPress activity poll results with progress bars. 17 */ 18 class PollActivityGraphWidget { 19 constructor(container) { 20 this.container = $(container); 21 this.select = this.container.find('.bpolls-activities-list'); 22 this.resultsContainer = this.container.find('.polls-widget__results-container'); 23 this.config = typeof bpolls_wiget_obj !== 'undefined' ? bpolls_wiget_obj : null; 24 25 if (this.select.length) { 26 this.init(); 27 } 28 } 29 30 init() { 31 this.bindEvents(); 32 // Initial results are already rendered by PHP. 33 // Animate progress bars on initial load. 34 this.animateProgressBars(); 35 } 36 37 bindEvents() { 38 this.select.on('change', (e) => this.handleSelectChange(e)); 39 } 40 41 handleSelectChange(e) { 42 const activityId = $(e.target).val(); 43 if (activityId) { 44 this.loadActivityResults(activityId); 45 this.updateExportLink(activityId); 46 } 47 } 48 49 getTranslation(key, fallback) { 50 if (this.config && this.config.i18n && this.config.i18n[key]) { 51 return this.config.i18n[key]; 52 } 53 return fallback; 54 } 55 56 showLoading() { 57 const loadingHtml = ` 58 <div class="polls-widget__loading"> 59 <div class="polls-widget__spinner"></div> 60 <span class="polls-widget__loading-text">${this.getTranslation('loading', 'Loading...')}</span> 61 </div> 62 `; 63 this.resultsContainer.html(loadingHtml); 64 } 65 66 loadActivityResults(activityId) { 67 this.showLoading(); 68 69 if (!this.config) { 70 this.showError(this.getTranslation('error', 'Configuration not found')); 71 return; 72 } 73 74 $.ajax({ 75 url: this.config.restRoot + 'bpolls/v1/widget-activity-results', 76 type: 'POST', 77 headers: { 'X-WP-Nonce': this.config.restNonce }, 78 data: { 79 activity_id: activityId, 80 security: this.config.ajax_nonce 81 }, 82 success: (response) => { 83 if (response.success && response.data && response.data.html) { 84 this.resultsContainer.html(response.data.html); 85 // Animate the progress bars. 86 this.animateProgressBars(); 87 } else { 88 const message = response.data && response.data.message 89 ? response.data.message 90 : this.getTranslation('error', 'Failed to load results'); 91 this.showError(message); 92 } 93 }, 94 error: () => { 95 this.showError(this.getTranslation('error', 'Request failed')); 96 } 97 }); 98 } 99 100 animateProgressBars() { 101 // Delay to allow DOM update, then animate bars. 102 setTimeout(() => { 103 this.resultsContainer.find('.polls-widget__result-fill').each(function() { 104 const $fill = $(this); 105 const targetWidth = $fill.css('width'); 106 $fill.css('width', '0'); 107 setTimeout(() => { 108 $fill.css('width', targetWidth); 109 }, 50); 110 }); 111 }, 50); 112 } 113 114 updateExportLink(activityId) { 115 const exportBtn = this.container.find('#export-poll-data'); 116 if (exportBtn.length && this.config) { 117 const baseUrl = exportBtn.attr('href').split('&activity_id=')[0]; 118 const nonce = this.config.csv_export_nonce || ''; 119 exportBtn.attr('href', baseUrl + '&activity_id=' + activityId + '&_wpnonce=' + nonce); 120 } 121 } 122 123 showError(message) { 124 const errorHtml = ` 125 <div class="polls-widget__empty"> 126 ${(typeof wbpollWidgetIcons !== 'undefined' && wbpollWidgetIcons.error) ? wbpollWidgetIcons.error : ''} 127 <p class="polls-widget__empty-text">${message}</p> 128 </div> 129 `; 130 this.resultsContainer.html(errorHtml); 131 } 132 } 133 134 /** 135 * Poll Report Widget Handler 136 * Displays standalone poll (CPT) results with progress bars. 137 */ 138 class PollReportWidget { 139 constructor(container) { 140 this.container = $(container); 141 this.select = this.container.find('#poll_seletect'); 142 this.resultsContainer = this.container.find('.all_polll_result'); 143 this.config = typeof wbpollWidgetConfig !== 'undefined' ? wbpollWidgetConfig : null; 144 145 this.init(); 146 } 147 148 init() { 149 this.bindEvents(); 150 // Initial results are already rendered by PHP. 151 // Animate progress bars on initial load. 152 this.animateProgressBars(); 153 } 154 155 bindEvents() { 156 this.select.on('change', (e) => this.handleSelectChange(e)); 157 } 158 159 handleSelectChange(e) { 160 const pollId = $(e.target).val(); 161 if (pollId) { 162 this.loadPollResults(pollId); 163 } 164 } 165 166 getTranslation(key, fallback) { 167 if (this.config && this.config.i18n && this.config.i18n[key]) { 168 return this.config.i18n[key]; 169 } 170 return fallback; 171 } 172 173 showLoading() { 174 const loadingHtml = ` 175 <div class="polls-widget__loading"> 176 <div class="polls-widget__spinner"></div> 177 <span class="polls-widget__loading-text">${this.getTranslation('loading', 'Loading results...')}</span> 178 </div> 179 `; 180 this.resultsContainer.html(loadingHtml); 181 } 182 183 loadPollResults(pollId) { 184 this.showLoading(); 185 186 if (!this.config) { 187 this.showError(this.getTranslation('error', 'Configuration not found')); 188 return; 189 } 190 191 $.ajax({ 192 url: this.config.restRoot + 'wbpoll/v1/widget-results', 193 type: 'POST', 194 headers: { 'X-WP-Nonce': this.config.restNonce }, 195 data: { 196 poll_id: pollId, 197 nonce: this.config.nonce 198 }, 199 success: (response) => { 200 if (response.success && response.data && response.data.html) { 201 this.resultsContainer.html(response.data.html); 202 // Animate the progress bars. 203 this.animateProgressBars(); 204 } else { 205 const message = response.data && response.data.message 206 ? response.data.message 207 : this.getTranslation('error', 'Failed to load results'); 208 this.showError(message); 209 } 210 }, 211 error: () => { 212 this.showError(this.getTranslation('error', 'Request failed')); 213 } 214 }); 215 } 216 217 animateProgressBars() { 218 // Delay to allow DOM update, then animate bars. 219 setTimeout(() => { 220 this.resultsContainer.find('.polls-widget__result-fill').each(function() { 221 const $fill = $(this); 222 const targetWidth = $fill.css('width'); 223 $fill.css('width', '0'); 224 setTimeout(() => { 225 $fill.css('width', targetWidth); 226 }, 50); 227 }); 228 }, 50); 229 } 230 231 showError(message) { 232 const errorHtml = ` 233 <div class="polls-widget__empty"> 234 ${(typeof wbpollWidgetIcons !== 'undefined' && wbpollWidgetIcons.error) ? wbpollWidgetIcons.error : ''} 235 <p class="polls-widget__empty-text">${message}</p> 236 </div> 237 `; 238 this.resultsContainer.html(errorHtml); 239 } 240 } 241
242 /** 243 * Initialize widgets on DOM ready 244 */ 245 $(document).ready(function() { 246 // Initialize Activity Graph Widgets (BuddyPress polls) 247 $('.widget_bp_poll_graph_widget, .polls-widget--activity-graph').each(function() { 248 new PollActivityGraphWidget(this); 249 }); 250 251 // Initialize Poll Report Widgets (Standalone polls) 252 $('.widget_wb_poll_report, .polls-widget--report').each(function() { 253 new PollReportWidget(this); 254 }); 255 }); 256 257 // Expose classes for external use 258 window.PollActivityGraphWidget = PollActivityGraphWidget; 259 window.PollReportWidget = PollReportWidget; 260 261})(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.