1 2var domain = window.location.origin; 3var sfPlayerRoot = sfPlayerRoot ? sfPlayerRoot : null; 4var spotifyClientId = 'dfcdf84f37514d2697a1446f820407fb'; 5 6/** 7 * Prevent Double loading of actions and files. 8 */ 9if (!sfPlayerRoot) { 10 11 console.log('Initializing player', ver); 12 var currentUser = null; 13 sfPlayerRoot = '/js/player/'; 14 15 function getAssetURL(asset) { 16 const url = sfPlayerRoot + asset + '?ver=' + ver; 17 return url; 18 } 19 20 function loadScript(url, callback) { 21 var head = document.head; 22 var script = document.createElement('script'); 23 script.type = 'text/javascript'; 24 script.src = url; 25 26 script.onreadystatechange = callback; 27 script.onload = callback; 28 29 head.appendChild(script); 30 } 31 32 function loadStyle(url) { 33 var head = document.head; 34 var style = document.createElement('link'); 35 style.rel = 'stylesheet'; 36 style.href = url; 37 38 head.appendChild(style); 39 } 40 41 function funcExists(functionName) { 42 if (typeof window[functionName] === "function") { 43 return true; 44 } 45 return false; 46 } 47 48 49 /** 50 * After jquery is loaded we can load the rest of the scripts. 51 * 52 * @returns {undefined} 53 */ 54 function loadAfterJQuery() { 55 /** 56 * Load jQuery 57 */ 58 jsf = jQuery; 59 60 loadScript(getAssetURL('api.js', function () { 61 62 })); 63 64 loadScript(getAssetURL('spotify.js'), function () { 65 //init should be done after the user is loaded. 66 }); 67 68 loadScript(getAssetURL('apple.js'), function () { 69 /** 70 * The current-user request can finish before this file 71 * arrives. If the linked account is already in session, 72 * hand its token to the controller now. 73 */ 74 if (typeof sessionController === 'undefined' || typeof sfApple === 'undefined') { 75 return; 76 } 77 var user = sessionController.getCurrentUser(); 78 var account = user && user.hasAppleMusicAccount ? user.hasAppleMusicAccount() : null; 79 if (account) { 80 sfApple.init(account.getAccessToken(), account.getStorefront()); 81 } 82 }); 83 84 loadScript(getAssetURL('video.js'), function () { 85 //init should be done after the user is loaded. 86 }); 87 88 const playerScriptName = playerHtml.replace(/\.html$/, '.js'); 89 loadScript(getAssetURL(playerScriptName), function () { 90 sfPlayer.init(playerHtml); 91 }); 92 93 loadScript(getAssetURL('account.js'), function () { 94 sfAccount.init(); 95 }); 96 97 /** 98 * Match the stylesheet to the requested player HTML so each 99 * skin (player.html / player-2026.html / ...) gets its own CSS. 100 */ 101 var playerCss = playerHtml.replace(/\.html$/, '.css'); 102 loadStyle(getAssetURL('styles/' + playerCss)); 103 104 } 105 106 /** 107 * Load init scripts 108 */ 109 loadScript('https://www.youtube.com/iframe_api'); 110 111 /** 112 * If we do not have JQuery in the page then load it. 113 */ 114 if (!('jQuery' in window)) { 115 loadScript(getAssetURL('scripts/jQuery.js'), loadAfterJQuery); 116 } else { 117 /** 118 * Else just load the dependencies 119 */ 120 loadAfterJQuery(); 121 } 122 123 /** 124 * Subscribe to the user change event of gatsby 125 */ 126 if (funcExists('gatsbySubscribe')) { 127 window.gatsbySubscribe(window.gatsbyPubSub.M_USER_UPDATED, (args) => { 128 console.log(args); 129 currentUser = args; 130 }); 131 } 132 133 /** 134 * Based on https://gist.github.com/benjamingr/0433b52559ad61f6746be786525e97e8 135 * 136 * @param {type} ee 137 * @returns {unresolved} 138 */ 139 function interceptNetworkRequests(ee) { 140 const open = XMLHttpRequest.prototype.open; 141 const send = XMLHttpRequest.prototype.send; 142 143 const isRegularXHR = open.toString().indexOf('native code') !== -1; 144 145 if (isRegularXHR) { 146 XMLHttpRequest.prototype.open = function () { 147 ee.onOpen && ee.onOpen(this, arguments); 148 if (ee.onLoad) { 149 this.addEventListener('load', ee.onLoad.bind(ee)); 150 } 151 if (ee.onError) { 152 this.addEventListener('error', ee.onError.bind(ee)); 153 } 154 return open.apply(this, arguments); 155 }; 156 XMLHttpRequest.prototype.send = function () { 157 ee.onSend && ee.onSend(this, arguments); 158 return send.apply(this, arguments); 159 }; 160 } 161 162 return ee; 163 } 164} 165 166/** 167 * 168 * @param {type} key 169 * @returns {unresolved} 170 */ 171function getURLParams(key, url) { 172 173 /** 174 * Make the URL param safe, meaning all 175 * params should be behind the & symbol. 176 * If this is not added, then the first param 177 * will not be read. 178 */ 179 var url = !url ? window.location.href : url; 180 url = url.replace('?', '?&'); 181 182 /** 183 * Initialize the object and pass the url 184 * @type URLSearchParams 185 */ 186 const sp = new URLSearchParams(url); 187 return sp.has(key) ? sp.get(key) : false;
188} 189 190/** 191 * Shows a message the frontend 192 * 193 * @param {type} msg 194 * @returns {undefined} 195 */ 196function showMessage(msg, isError = false) { 197 ajaxController.showMsg(msg, isError); 198} 199 200/** 201 * For debug, be able to clear our the modal sessions. 202 */ 203function cleanUpModalClosing(){ 204 sessionStorage.setItem('modalSkipAds', ''); 205 sessionStorage.setItem('modalSkipPreview', ''); 206 localStorage.setItem("currentPlayTime", 0); 207 alert('Modal sessions cleared'); 208} 209 210var sfPlayerModalLoadingId = null; 211 212async function hidePlayerModal(which) { 213 if($('#player.player-2026').length <= 0){ 214 /** 215 * Make sure we only show these modals on the new player. 216 */ 217 return; 218 } 219 220 var id = ''; 221 switch (which) { 222 case 'ads': 223 id = ('modalSkipAds'); 224 break; 225 case 'preview': 226 id = ('modalSkipPreview'); 227 break; 228 default: 229 id = which; 230 break; 231 } 232 233 $('#' + id).remove(); 234} 235 236/** 237 * 238 * @param {*} which 239 * @param {*} force 240 * @returns 241 */ 242async function showPlayerModal(which, force = false) { 243 244 if($('#player.player-2026').length <= 0){ 245 /** 246 * Make sure we only show these modals on the new player. 247 */ 248 return; 249 } 250 251 var id = ''; 252 var daysBeforeReasking = 30; 253 switch (which) { 254 case 'ads': 255 id = ('modalSkipAds'); 256 daysBeforeReasking = 14; 257 break; 258 case 'preview': 259 id = ('modalSkipPreview'); 260 daysBeforeReasking = 7; 261 break; 262 default: 263 id = which; 264 break; 265 } 266 267 /** 268 * First check if it was dismissed before. 269 */ 270 try { 271 const lastAsked = sessionStorage.getItem(id); 272 console.log('Last asked for ' + id + ': ' + lastAsked); 273 if (lastAsked && !isNaN(lastAsked) && !force) { 274 const lastAskedTime = new Date(parseInt(lastAsked)); 275 const now = new Date(); 276 const diffTime = Math.abs(now - lastAskedTime); 277 const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); 278 if (diffDays < daysBeforeReasking) { 279 /* 280 * Only this prompt is on cooldown. It used to clear every 281 * player modal on the way out, so asking for one that had 282 * been dismissed would take down whichever other one happened 283 * to be on screen. 284 */ 285 $('#' + id).remove(); 286 return; 287 } 288 } 289 } catch (e) { 290 console.error('Unable to access sessionStorage', e); 291 } 292 293 /** Check if its already shown */ 294 if ($('#' + id).length > 0) { 295 return; 296 } 297 298 /** Avoid duplicate loads while the same modal is already being fetched. */ 299 if (sfPlayerModalLoadingId) { 300 return; 301 } 302 303 /* 304 * One prompt at a time, and whichever got there first keeps its place 305 * until the reader dismisses it. Swapping one for another mid-listen was 306 * the other half of the flicker: these are asked once and then left 307 * alone, not re-decided on every track. 308 */ 309 if ($('.sf-modal-player').length > 0) { 310 return; 311 } 312 313 sfPlayerModalLoadingId = id; 314 try { 315 //const modal = await sfLoadModal(id); 316 await sfAccount.loadModal(id); 317 } finally { 318 sfPlayerModalLoadingId = null; 319 } 320 321 if(isMobile()){ 322 var modals = $('#' + id); 323 const mobile = $('#m-player-modals'); 324 mobile.html(modals); 325 } 326 327} 328 329function isMobile() { 330 return $(window.document).width() < 1024; 331} 332 333function requestAppleMusicToken() { 334 335 const user = sfAccount.getUser(); 336}
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.