1/** 2 * WP-Polls front end. 3 * 4 * Nothing here is exported. Poll markup asks for behaviour through 5 * data-poll-action / data-poll-id attributes, which are picked up by the 6 * delegated listener at the bottom of this file. 7 */ 8( function() { 9 'use strict'; 10 11 const l10n = window.wpPollsL10n || {}; 12 13 // Matches the fade duration used before. 14 const FADE_DURATION = 400; 15 16 /** 17 * Whether the visitor has asked for less movement. 18 * 19 * The fade is an inline transition, so a stylesheet media query cannot 20 * reach it the way it reaches the spinner and the result bars -- an inline 21 * style wins. Read here instead, and read on every call rather than once at 22 * load, because the setting can change while the page is open. 23 * 24 * @return {boolean} True when motion should be skipped. 25 */ 26 function prefersReducedMotion() { 27 return ( 28 typeof window.matchMedia === 'function' && 29 window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches 30 ); 31 } 32 33 // The Poll Container For A Given Poll 34 function pollContainer( currentPollId ) { 35 return document.getElementById( 'polls-' + currentPollId ); 36 } 37 38 // Show Or Hide The Loading Indicator 39 // The indicator is hidden by a stylesheet rule, so it needs an explicit 40 // display value rather than just clearing the inline one. 41 function setLoading( currentPollId, visible ) { 42 const loading = document.getElementById( 43 'polls-' + currentPollId + '-loading', 44 ); 45 if ( loading ) { 46 loading.style.display = visible ? 'block' : 'none'; 47 } 48 } 49 50 // Fade An Element To The Given Opacity 51 function fadeTo( element, opacity ) { 52 if ( ! element ) { 53 return; 54 } 55 56 // Straight to the end state when motion is unwelcome: the poll still 57 // dims, it just does not travel there. 58 element.style.transition = prefersReducedMotion() 59 ? '' 60 : 'opacity ' + FADE_DURATION + 'ms'; 61 element.style.opacity = opacity; 62 } 63 64 // Make Sure A Poll Is Fully Visible 65 // Only ever clears opacity, never animates towards it. Fading a poll *in* 66 // would have to start it at zero opacity, and browsers freeze animations in 67 // background tabs, which would leave the poll invisible until it is looked at. 68 function show( element ) { 69 if ( element ) { 70 element.style.transition = ''; 71 element.style.opacity = ''; 72 } 73 } 74 75 // When User Vote For Poll 76 function pollVote( currentPollId ) { 77 const form = document.getElementById( 'polls_form_' + currentPollId ); 78 if ( ! form ) { 79 return; 80 } 81 82 const multipleField = document.getElementById( 83 'poll_multiple_ans_' + currentPollId, 84 ); 85 const pollMultipleAns = multipleField 86 ? parseInt( multipleField.value, 10 ) 87 : 0; 88 89 const selected = []; 90 const choices = form.querySelectorAll( 91 'input[type="checkbox"], input[type="radio"], option', 92 ); 93 Array.prototype.forEach.call( choices, function( choice ) { 94 // Inputs expose 'checked', options expose 'selected'. 95 if ( choice.checked || choice.selected ) { 96 selected.push( choice.value ); 97 } 98 } ); 99 100 if ( pollMultipleAns > 0 ) { 101 if ( selected.length === 0 ) { 102 alert( l10n.text_valid ); 103 } else if ( selected.length > pollMultipleAns ) { 104 alert( l10n.text_multiple + ' ' + pollMultipleAns ); 105 } else { 106 pollProcess( currentPollId, selected.join( ',' ) ); 107 } 108 return; 109 } 110 111 const pollAnswerId = selected.length 112 ? parseInt( selected[ selected.length - 1 ], 10 ) 113 : 0; 114 if ( pollAnswerId > 0 ) { 115 pollProcess( currentPollId, pollAnswerId ); 116 } else { 117 alert( l10n.text_valid ); 118 } 119 } 120 121 // Send A Poll Request And Swap In The Markup That Comes Back 122 function pollRequest( currentPollId, view, extraFields ) { 123 const nonceField = document.getElementById( 124 'poll_' + currentPollId + '_nonce', 125 ); 126 127 const body = new URLSearchParams(); 128 body.append( 'action', 'polls' ); 129 body.append( 'view', view ); 130 body.append( 'poll_id', currentPollId ); 131 body.append( 132 'poll_' + currentPollId + '_nonce', 133 nonceField ? nonceField.value : '', 134 ); 135 if ( extraFields ) { 136 Object.keys( extraFields ).forEach( function( name ) { 137 body.append( name, extraFields[ name ] ); 138 } ); 139 } 140 141 fadeTo( pollContainer( currentPollId ), 0 ); 142 setLoading( currentPollId, true ); 143 144 fetch( l10n.ajax_url, { 145 method: 'POST', 146 credentials: 'include', 147 cache: 'no-cache', 148 body, 149 } ) 150 .then( function( response ) { 151 return response.text(); 152 } ) 153 .then( function( data ) { 154 pollProcessSuccess( currentPollId, data ); 155 } ) 156 .catch( function() { 157 // Leave the poll readable rather than stuck faded out behind a spinner.
158 setLoading( currentPollId, false ); 159 show( pollContainer( currentPollId ) ); 160 } ); 161 } 162 163 // Process Poll (User Click "Vote" Button) 164 function pollProcess( currentPollId, pollAnswerId ) { 165 const fields = {}; 166 fields[ 'poll_' + currentPollId ] = pollAnswerId; 167 pollRequest( currentPollId, 'process', fields ); 168 } 169 170 // Poll's Result (User Click "View Results" Link) 171 function pollResult( currentPollId ) { 172 pollRequest( currentPollId, 'result' ); 173 } 174 175 // Poll's Voting Booth (User Click "Vote" Link) 176 function pollBooth( currentPollId ) { 177 pollRequest( currentPollId, 'booth' ); 178 } 179 180 // Poll Process Successfully 181 function pollProcessSuccess( currentPollId, data ) { 182 const container = pollContainer( currentPollId ); 183 if ( container ) { 184 const parsed = document.createElement( 'div' ); 185 parsed.innerHTML = data; 186 container.replaceWith.apply( 187 container, 188 Array.prototype.slice.call( parsed.childNodes ), 189 ); 190 } 191 192 setLoading( currentPollId, false ); 193 194 // The old poll was faded out and has now been thrown away, so all that is 195 // left to do is make sure the markup that replaced it is visible. 196 show( pollContainer( currentPollId ) ); 197 } 198 199 // On Click Events 200 document.addEventListener( 'click', function( event ) { 201 const target = event.target; 202 if ( ! target || typeof target.closest !== 'function' ) { 203 return; 204 } 205 206 const trigger = target.closest( '[data-poll-action]' ); 207 if ( ! trigger ) { 208 return; 209 } 210 211 const pollId = trigger.getAttribute( 'data-poll-id' ); 212 213 // The result and booth templates use anchors, so stop the browser 214 // from following the placeholder href while the request runs. 215 if ( trigger.tagName === 'A' ) { 216 event.preventDefault(); 217 } 218 219 switch ( trigger.getAttribute( 'data-poll-action' ) ) { 220 case 'vote': 221 pollVote( pollId ); 222 break; 223 case 'result': 224 pollResult( pollId ); 225 break; 226 case 'booth': 227 pollBooth( pollId ); 228 break; 229 } 230 } ); 231}() );
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.