1 2 3var srp_isPausedFromPopup = false; 4 5IRON.advancedTriggers = { 6 7 listenersAttached: {}, 8 trackTimesRunned: {}, 9 playerScenarioMap: {}, 10 activeState: { 11 playerID: null, 12 scenarioID: null 13 }, 14 downloadUrls: new Map(), // Store download URLs across calls 15 16 applyScenario(scenario, player = null) { 17 // Check if the scenario should be applied based on user authentication status and roles 18 if (!this.shouldApplyScenario(scenario)) { 19 //console.log(`Skipping scenario ${scenario.name} due to user authentication or role restrictions.`); 20 return; 21 } 22 23 // Get the players affected by this specific scenario 24 IRON.advancedTriggers.players = this.getPlayers(scenario, player); 25 //console.log(`Affected Players for ${scenario.name}:`, IRON.advancedTriggers.players); 26 // Attach scenario to the players but do not add play listener here 27 IRON.advancedTriggers.players.forEach(player => { 28 this.attachScenarioToPlayer(player, scenario); 29 }); 30 }, 31 32 shouldApplyScenario(scenario) { 33 // Check user authentication status and roles 34 const isLoggedIn = this.isUserLoggedIn(); 35 const userRoles = this.getUserRoles(); 36 37 // If `loggedOut` is specified, ensure the user is logged out 38 if (scenario.applyFor.loggedOut === true && isLoggedIn) { 39 return false; 40 } 41 42 // If `loggedIn` is specified, ensure the user is logged in 43 if (scenario.applyFor.loggedIn === true && !isLoggedIn) { 44 return false; 45 } 46 47 // If `roles` is specified, check if the user has at least one of the roles in the array 48 if (scenario.applyFor.roles && !scenario.applyFor.roles.some(role => userRoles.includes(role)) && isLoggedIn) { 49 return false; 50 } 51 52 // Otherwise, apply the scenario 53 return true; 54 }, 55 56 isUserLoggedIn() { 57 // This function should return true if the user is logged in, and false otherwise 58 if (srp_vars.is_logged_in === 'yes') { 59 return true; 60 } 61 return false; 62 }, 63 64 getUserRoles() { 65 // This function should return an array of user roles 66 return srp_vars.user_role; 67 }, 68 69 getPlayers(scenario, playerSelector = '.iron-audioplayer') { 70 playerSelector = playerSelector ?? '.iron-audioplayer'; // If no playerSelector is NULL, default to all players 71 let players = []; 72 // If allPlayers is true, apply to all players 73 if (scenario.applyOn.allPlayers) { 74 players = Array.from(document.querySelectorAll(playerSelector)); 75 this.attachStickyPlayer(scenario, players); 76 } else if (scenario.applyOn.players) { 77 // Apply only to the players specified in `applyOn.players` 78 scenario.applyOn.players.forEach(selector => { 79 const container = document.querySelector(selector); 80 if (container) { 81 const foundPlayers = Array.from(container.querySelectorAll(playerSelector)); 82 players = players.concat(foundPlayers); 83 } 84 }); 85 } 86 // If specificTracks is defined but allPlayers and players are not defined, get all players 87 if (scenario.applyOn.specificTracks && !scenario.applyOn.allPlayers && !scenario.applyOn.players) { 88 players = Array.from(document.querySelectorAll(playerSelector)); 89 this.attachStickyPlayer(scenario, players); 90 } 91 // Exclude players that are descendants of excluded selectors 92 if (scenario.excludeOn && scenario.excludeOn.css_selector) { 93 scenario.excludeOn.css_selector.forEach(selector => { 94 const excludedContainers = document.querySelectorAll(selector); 95 excludedContainers.forEach(container => { 96 const excludedPlayers = container.querySelectorAll(playerSelector); 97 players = players.filter(player => !Array.from(excludedPlayers).includes(player));
98 this.attachStickyPlayer(scenario, players); 99 }); 100 }); 101 } 102 return players; 103 }, 104 105 attachStickyPlayer(scenario, players){ 106 //we attach the sticky player for case where sticky player is loaded from the page settings. 107 const stickyPlayer = document.querySelector('#sonaar-player'); 108 109 if(!players.includes(stickyPlayer)){ //check if its already part of players and do it only once per scenario 110 players.push(stickyPlayer); 111 } 112 113 }, 114 isPlayerExcluded(player, scenario) { 115 // Ensure `player` is a DOM element, not a jQuery object. 116 const playerElement = player instanceof jQuery ? player[0] : player; 117 118 if (scenario.excludeOn && scenario.excludeOn.css_selector) { 119 120 const result = scenario.excludeOn.css_selector.some(selector => { 121 const closestElement = playerElement.closest(selector); 122 return closestElement !== null; // Check if we found a valid element. 123 }); 124 125 return result; 126 } 127 128 return false; 129 }, 130 attachScenarioToPlayer(player, scenario) { 131 const playerID = player.id; 132 const scenarioID = scenario.id; 133 134 // Determine the correct audio element to attach listeners 135 var audioElement = $(".iron-audioplayer").filter(function () { 136 return $(this).data("sticky-player"); 137 }).length ? $('#sonaar-player audio')[0] : sr_setAudioElementInstance($(player)); 138 139 if (typeof elementorFrontend !== 'undefined' && elementorFrontend.isEditMode()) { 140 var audioElement = sr_setAudioElementInstance($(player)); 141 } 142 // Ensure the scenario mapping is initialized before using it 143 if (!this.playerScenarioMap) { 144 this.playerScenarioMap = {}; 145 } 146 147 // Ensure the scenarioID exists in the mapping before accessing its properties 148 if (!this.playerScenarioMap[scenarioID]) { 149 this.playerScenarioMap[scenarioID] = { 150 players: {}, // Use an object to store players and their listeners 151 scenarioData: scenario // Optionally, store scenario data for reference 152 }; 153 } 154 155 156 if (scenario.action_when.downloadButtonClicked) { 157 //Applying download button listener 158 this.attachDownloadButtonListener(player, scenario); 159 return; 160 } 161 162 163 if (scenario.trigger[0].type === 'trim') { 164 165 // Only proceed if either audio_duration or start_time is available 166 if (scenario.audio.audio_duration || scenario.audio.start_time) { 167 168 let formattedDuration; 169 if (scenario.audio.audio_duration) { 170 // Set the duration of the tracks in the player widgets 171 const customTimeDuration = moment.duration(scenario.audio.audio_duration, "seconds"); 172 formattedDuration = moment(customTimeDuration.minutes() + ":" + customTimeDuration.seconds(), "m:s").format("mm:ss"); 173 } else if (scenario.audio.start_time) { 174 // If duration is missing or 0, but start time is available 175 formattedDuration = (currentText) => { 176 const timeParts = currentText.trim().split(':'); 177 const currentMinutes = parseInt(timeParts[0], 10); 178 const currentSeconds = parseInt(timeParts[1], 10); 179 const totalSeconds = currentMinutes * 60 + currentSeconds - scenario.audio.start_time; 180 const adjustedTime = moment.duration(totalSeconds, "seconds"); 181 return moment(adjustedTime.minutes() + ":" + adjustedTime.seconds(), "m:s").format("mm:ss"); 182 }; 183 } 184 185 const updateTrackDuration = (element) => { 186 const targetElement = $(element).find('.sr-playlist-cf--srmp3_cf_length'); 187 let timeNodeFound = false; 188 189 // Iterate through the child nodes to check if a time value exists 190 targetElement.contents().each(function () { 191 if (this.nodeType === 3) { 192 const textValue = this.nodeValue.trim(); 193 if (/\d{1,2}:\d{2}/.test(textValue)) { 194 if (formattedDuration instanceof Function) { 195 this.nodeValue = ` ${formattedDuration(textValue)}`; 196 } else { 197 this.nodeValue = ` ${formattedDuration}`; 198 } 199 timeNodeFound = true; 200 } 201 } 202 }); 203 204 if (!timeNodeFound) { 205 // Ensure the time is added if no time node was found, e.g., for radio stream. WIP 206 } 207 }; 208 209 if (Array.isArray(scenario.applyOn?.specificTracks)) { 210 // Iterate through all the tracks in the player 211 $(player).find('.sr-playlist-item').each((index, trackElement) => { 212 const trackID = $(trackElement).data('post-id'); // Assuming track ID is stored in a data attribute 213 214 // Update only if the track matches the specificTracks list 215 if (scenario.applyOn.specificTracks.includes(parseInt(trackID, 10))) { 216 $(trackElement).data('tracktime', typeof formattedDuration === 'string' ? formattedDuration : formattedDuration($(trackElement).text())); // for waveform in tracklist 217 updateTrackDuration(trackElement); 218 } 219 }); 220 } else { 221 // Update all tracks if no specificTracks are defined 222 $(player).find('.sr-playlist-item').data('tracktime', typeof formattedDuration === 'string' ? formattedDuration : formattedDuration($(player).text())); // for waveform in tracklist 223 updateTrackDuration(player); 224 } 225 226 } 227 } 228 229 230 231 232 233 // Check if the player is already attached to this scenario to prevent duplicates 234 if (!this.playerScenarioMap[scenarioID].players[playerID]) { 235 236 // Create a unique listener function for this player and scenario 237 const onAudioPlay = () => { 238 const playerListeners = this.playerScenarioMap[scenarioID].players[playerID]; 239 const { timeUpdateListener, playListener } = playerListeners; 240 const specificTracks = scenario.applyOn?.specificTracks; 241 242 let currentTrack = player.querySelector('.sr-playlist-item.current'); 243 currentTrack = currentTrack ? currentTrack : null; 244 let clickedTrackID = null; 245 clickedTrackID = this.getCurrentTrackID_Helper(player); 246 247 const sourcePostID = this.getCurrentSourcePostID_Helper(player); 248 const sourcePostIDNumber = parseInt(sourcePostID, 10); // Convert sourcePostID to a number 249 250 scenario.wait = false; 251 //console.log(scenario.name, "clickedTrackID", clickedTrackID, "s
251ourcePostID", sourcePostID, "sourcePostIDNumber", sourcePostIDNumber); 252 253 // Initialize track times run for this scenario and player 254 if (!this.trackTimesRunned[scenarioID]) { 255 this.trackTimesRunned[scenarioID] = {}; 256 } 257 if (!this.trackTimesRunned[scenarioID][playerID]) { 258 this.trackTimesRunned[scenarioID][playerID] = {}; 259 } 260 if (!this.trackTimesRunned[scenarioID][playerID][clickedTrackID]) { 261 this.trackTimesRunned[scenarioID][playerID][clickedTrackID] = 0; 262 } 263 264 if ((scenario.trigger[0].type !== 'watermark') && audioElement.currentTime === 0 && scenario.action_when.reached_value === '0' && scenario.trigger[0].stopPlayer) { 265 const playerNotPlayed = !this.trackTimesRunned[scenarioID][playerID].played; 266 const trackNotPlayed = !(this.trackTimesRunned[scenarioID][playerID][clickedTrackID] >= 1); 267 268 const rememberPlayer = scenario.onceActionFilled.rememberAndDontShowAgainForThisPlayerUntilPageRefresh; 269 const rememberTrack = scenario.onceActionFilled.rememberAndDontShowAgainForThisTrackUntilPageRefresh; 270 271 if ( 272 (rememberPlayer && playerNotPlayed) || 273 (rememberTrack && trackNotPlayed) || 274 (!rememberPlayer && !rememberTrack) 275 ) { 276 // prevent a 0.1s glitch when starting the ads 277 if(!this.isWithinRangeLimitOfAdvancedRules(scenario)){ 278 audioElement.muted = true; 279 audioElement.currentTime = 0; 280 } 281 282 } 283 } 284 285 scenario.lastPlayedTrackIDs = scenario.lastPlayedTrackIDs || {}; 286 287 scenario.lastPlayedTrackIDs[playerID] = scenario.lastPlayedTrackIDs?.[playerID] ? String(scenario.lastPlayedTrackIDs[playerID]) : null; 288 if (scenario.lastPlayedTrackIDs?.[playerID] && scenario.lastPlayedTrackIDs[playerID] !== clickedTrackID) { 289 //console.log("Track changed! Resetting ad flag for all scenarios."); 290 this.detachTimeUpdateListener(audioElement, scenario); 291 this.clearWaterMarkInterval(audioElement); 292 293 } 294 295 if (this.playerScenarioMap[scenarioID].players[playerID].player.id !== this.activeState.playerID) { 296 //console.log("PLAYER changed! Resetting ad flag for all scenarios."); 297 audioElement.muted = false; 298 this.clearWaterMarkInterval(audioElement); 299 this.detachTimeUpdateListener(audioElement, scenario); 300 audioElement.removeEventListener('play', playListener); 301 audioElement.removeEventListener('timeupdate', timeUpdateListener); 302 scenario.lastPlayedTrackIDs[playerID] = null; 303 return; 304 } 305 306 if(scenario.trigger[0].type !== 'watermark' ){ 307 this.detachTimeUpdateListener(audioElement, scenario); 308 } 309 310 audioElement.removeEventListener('timeupdate', timeUpdateListener); 311 312 if (scenario.lastPlayedTrackIDs?.[playerID] !== clickedTrackID) { 313 //This fire only once per current track even if I pause or unpause. 314 scenario.currentTrackAdPlayed = false; 315 316 if(scenario.trigger[0].type === 'trim'){ 317 const shouldMarkActioned = !Array.isArray(specificTracks) || 318 specificTracks.includes(sourcePostIDNumber); 319 320 if (shouldMarkActioned) { 321 this.markTrackAsActioned(scenario, playerID, `track_${clickedTrackID}`, scenario.trigger[0].type, true); 322 323 if (this.hasTrackBeenActioned(scenario, playerID, `track_${clickedTrackID}`, scenario.trigger[0].type, true)) { 324 //console.log("returned by hasTrackBeenActioned onAudioPlay"); 325 return; 326 } 327 } 328 329 if (IRON.audioPlayer.stickyEnable) { 330 scenario.targetVolume = IRON.sonaar.player.volume; 331 }else{ 332 scenario.targetVolume = ($(player).attr('data-volume') == N
332aN) ? 1 : $(player).attr('data-volume'); 333 } 334 audioElement.volume = scenario.targetVolume ; //reset the volume to the original volume 335 336 scenario.fadeInDuration = 2; // Fade in duration in seconds 337 scenario.fadeOutDuration = 2; // Fade out duration in seconds 338 scenario.volumeIncrement = scenario.targetVolume / (scenario.fadeInDuration * 3); // Adjust volume every 100ms 339 340 if(scenario.audio.fade_in === 'true'){ 341 audioElement.volume = 0; 342 } 343 344 if(scenario.audio.start_time){ 345 audioElement.muted = true; 346 audioElement.currentTime = scenario.audio.start_time; 347 } 348 349 } 350 351 } 352 353 // Ensure early return if track is not included in the scenario 354 if (Array.isArray(specificTracks) && sourcePostIDNumber) { 355 if (!specificTracks.includes(sourcePostIDNumber)) { 356 audioElement.muted = false; 357 358 if (scenario.audio.start_time && scenario.lastPlayedTrackIDs?.[playerID] !== clickedTrackID) { 359 audioElement.currentTime = 0; 360 } 361 362 scenario.lastPlayedTrackIDs[playerID] = clickedTrackID; 363 return; // Early exit to maintain original logic 364 } 365 } 366 367 368 scenario.lastPlayedTrackIDs[playerID] = clickedTrackID; 369 370 this.activeState.playerID = player.id; 371 this.activeState.scenarioID = scenarioID; 372 this.activeState.currentTrackID = clickedTrackID; 373 374 this.attachDownloadButtonListener(player, scenario); // looks it attach for the sticky. 375 376 audioElement.addEventListener('timeupdate', timeUpdateListener); 377 378 379 }; 380 // Define a flag outside of the listener or in the relevant scope 381 const onTimeUpdate = () => { 382 383 // Check if scenario requires a time to be tracked 384 if (!scenario.action_when.reached_value && scenario.action_when.reached_value !== 0) { 385 //console.log(`Scenario ${scenarioID} does not require timeupdate listener.`); 386 return; 387 } 388 389 // Missing code integrated here 390 const playerID = player.id; 391 const scenarioID = scenario.id; 392 const trackID = this.activeState.currentTrackID; 393 //let handleCalled = false; 394 395 if(scenario.trigger[0].type === 'trim'){ 396 this.trimmedWaveClick(audioElement, scenario); 397 } 398 399 if (!trackID) { 400 //console.log("No track ID found for the clicked track. = ", trackID); 401 return; 402 } 403 404 const shouldMeetAt = this.getWhenItShouldMeet(audioElement, scenario.action_when.reached_value, scenario.action_when.reached_unit || 'percent'); 405 const currentTime = Math.round((audioElement.currentTime)); 406 407 let timeConditionMet = scenario.trigger[0].required 408 ? currentTime >= shouldMeetAt 409 : currentTime == Math.floor(shouldMeetAt); 410 411 /*console.log( 412 "currentTime =", currentTime, 413 "shouldMeetAt =", shouldMeetAt 414 );*/ 415 416 if (audioElement.paused) { // mostly for continuous player when its paused on page load. 417 return; 418 } 419 420 if (timeConditionMet) { 421 422 if(audioElement.currentTime === 0){ 423 return; 424 } 425 426 if (scenario.currentTrackAdPlayed) { 427 // We dont want to play trigger again when we seek back to the trigger time... 428 429 if (scenario.trigger[0].type === 'watermark') { 430 return; 431 } 432 433 if (scenario.trigger[0].type === 'playAd') { 434 return; 435 } 436 437 if (!scenario.trigger[0].required && scenario.hasTriggered) { 438 return; // Exits if 'required' is false 439 } 440 } 441 442 if(this.trackTimesRunned[scenarioID][playerID].played && scenario.onceActionFilled.rememberAndDontShowAgainForThisPlayerUntilPageRefresh){ 443 //console.log('Returned by rememberAndDontShowAgainForThisPlayerUntilPageRefresh '); 444 return; 445 } 446 447 if(this.trackTimesRunned[scenarioID][playerID][trackID] >= 1 && scenario.onceActionFilled.rememberAndDontShowAgainForThisTrackUntilPageRefresh){ 448 //console.log('Returned by rememberAndDontShowAgainForThisTrackUntilPageRefresh'); 449 return; 450 } 451 scenario.hasTriggered = true; 452 this.trackTimesRunned[scenarioID][playerID].played = true; 453 this.trackTimesRunned[scenarioID][playerID][trackID]++; 454 scenario.currentTrackAdPlayed = true; // Mark the ad as played for this track 455 456 trackElement = player.querySelector('.sr-playlist-item.current'); 457 this.handleActions(scenario, player, trackElement); 458 459 }else{ 460 scenario.hasTriggered = false; 461 } 462 }; 463 464 // Store the player and its listener in the scenario mapping 465 this.playerScenarioMap[scenarioID].players[playerID] = { 466 player: player,
467 scenarioData: scenario, 468 playListener: onAudioPlay, 469 timeUpdateListener: onTimeUpdate 470 }; 471 472 if(sonaar_music.option.enable_continuous_player === "true"){ 473 $("#sonaar-audio").on("loadeddata.continuous", function () { 474 if(playerID !== 'sonaar-player'){ 475 $('#sonaar-audio').off('loadeddata.continuous'); 476 return; 477 } 478 if(IRON.sonaar.player?.selectedPlayer){ 479 $('#sonaar-audio').off('loadeddata.continuous'); 480 return; 481 } 482 483 if (!IRON.advancedTriggers.playerScenarioMap[scenarioID].players[playerID].scenarioData.action_when.downloadButtonClicked) { 484 const playerListeners = IRON.advancedTriggers.playerScenarioMap[scenarioID].players[playerID]; 485 if (playerListeners) { 486 IRON.advancedTriggers.activeState.scenarioID = scenarioID; 487 IRON.advancedTriggers.activeState.playerID = playerID; 488 489 const { timeUpdateListener, playListener } = playerListeners; 490 491 if (playListener) { 492 // recheck the audioElement. perhaps sticky was not loaded yet. 493 audioElement = sr_setAudioElementInstance($(player)); 494 audioElement.removeEventListener('play', playListener); 495 audioElement.addEventListener('play', playListener); 496 if(scenario.trigger[0].type === 'trim'){ 497 onAudioPlay(); //when the sticky load in the page we want to trim it right now, not onPlay 498 } 499 } 500 } 501 } 502 503 }); 504 } 505 506 } 507 508 // Add a click listener to manage the scenario and active player ID 509 const clickListener = (el) => { 510 511 if(IRON.sonaar.player?.selectedPlayer){ // sticky player has been launched by a player widget. so its not a standalone sticky player loaded from page/plugin settings 512 $('#sonaar-player').off('click', clickListener); 513 if(player.id === 'sonaar-player'){ 514 return; 515 } 516 } 517 518 if (!this.playerScenarioMap[scenarioID].players[playerID].scenarioData.action_when.downloadButtonClicked) { 519 520 const playerListeners = this.playerScenarioMap[scenarioID].players[playerID]; 521 522 if (playerListeners) { 523 this.activeState.scenarioID = scenarioID; 524 this.activeState.playerID = playerID; 525 526 const { timeUpdateListener, playListener } = playerListeners; 527 528 if (playListener) { 529 // recheck the audioElement. perhaps sticky was not loaded yet. 530 audioElement = sr_setAudioElementInstance($(player)); 531 audioElement.removeEventListener('play', playListener); 532 audioElement.addEventListener('play', playListener); 533 } 534 } 535 } 536 }; 537 538 // Attach the click listener to the player 539 player.addEventListener('click', clickListener); 540 this.listenersAttached[playerID] = true; 541 }, 542 543 trimmedWaveClick(audioElement, scenario) { 544 $(".sr_selectedPlayer .sonaar_fake_wave, #sonaar-player .sonaar_fake_wave, .mobileProgress").off("click.trimmed"); 545 $(".sr_selectedPlayer .sonaar_fake_wave, #sonaar-player .sonaar_fake_wave, .mobileProgress").on("click.trimmed", function (event) { 546 // Retrieve start time and audio duration from the scenario 547 const startTime = scenario.audio.start_time || 0; 548 const audioDuration = scenario.audio.audio_duration || (audioElement.duration - startTime); 549 550 // Calculate the clicked position's ratio within the wave 551 const clickPosition = event.offsetX / $(this).width(); 552 audioElement.volume = scenario.targetVolume; // make sure we have a volume because fadein/fadeout will reduce it. 553 554 function waitUntilNewTrackIsLoaded() { 555 if (!isNaN(audioElement.duration)) { 556 const effectiveDuration = (audioDuration === 'Infinity') 557 ? audioElement.buffered.end(audioElement.buffered.length - 1) 558 : audioDuration; 559 560 // Calculate the correct seek time within the trimmed segment 561 const seekTime = startTime + (clickPosition * effectiveDuration); 562 563 // Set the audio element's current time, ensuring it stays within the segment bounds 564 audioElement.currentTime = Math.min(seekTime, startTime + effectiveDuration); 565 566 event.preventDefault(); 567 clearInterval(intervalId); 568 } 569 } 570 571 const intervalId = setInterval(waitUntilNewTrackIsLoaded, 50); 572 setTimeout(() => clearInterval(intervalId), 3000); 573 }); 574 575 576 577 audioDuration = scenario.audio.audio_duration || audioElement.duration; 578 579 if(scenario.audio.start_time){ 580 audioElement.muted = false; 581 582 if(!scenario.audio.audio_duration){ 583 audioDuration = audioDuration - scenario.audio.start_time; 584 } 585 586 if((audioElement.currentTime < scenario.audio.start_time) && (audioElement.duration > scenario.audio.start_time)){ 587 audioElement.currentTime = scenario.audio.start_time; 588 } 589 } 590 591 592 if(audioDuration){ 593 let timeToStop = audioDuration; 594 595 if(scenario.audio.start_time){ 596 timeToStop = scenario.audio.start_time + audioDuration; 597 } 598 599 const currentPreviewTime = Math.max(0, audioElement.currentTime - scenario.audio.start_time); 600 const duration = audioDuration; // Ensure correct duration (not timeToStop) 601 602 // Ensure barLength accurately reflects progress within the specified segment 603 let barLength = (currentPreviewTime / duration) * 100; 604 barLength = Math.min(barLength, 100); // Cap the value at 100% 605 606 // Update the preview progress bar 607 $("#sonaar-player .sonaar_wave_cut, .sr_selectedPlayer .album-player .sonaar_wave_cut, .sr_selectedPlayer .current .sonaar_wave_cut").width(barLength + "%"); 608 $("#sonaar-player .progressDot").css("left", barLength + "%"); 609 610 let timeProgress = moment.duration(currentPreviewTime, "seconds"); 611 timeProgress = moment(timeProgress.minutes() + ":" + timeProgress.seconds(), "m:s").format("mm:ss"); 612 $(".sr_selectedPlayer .currentTime").html(timeProgress); 613 IRON.sonaar.player.currentTime = timeProgress; 614 615 const timeLeft = moment.duration(duration - currentPreviewTime, "seconds"); 616 timeleft = "-" + moment(timeLeft.minutes() + ":" + timeLeft.seconds(), "m:s").format("mm:ss"); 617 $(".sr_selectedPlayer .totalTime").html(timeleft); 618 IRON.sonaar.player.totalTime = timeleft; 619 620 // Fade In effect 621 if(scenario.audio.fade_in === 'true'){ 622 if (currentPreviewTime <= scenario.fadeInDuration) { 623 audioElement.volume = Math.min(scenario.targetVolume, audioElement.volume + scenario.volumeIncrement); 624 } 625 } 626 // Fade Out effect 627 if(scenario.audio.fade_out === 'true'){
628 const fadeOutStart = timeToStop - scenario.fadeOutDuration; 629 if (audioElement.currentTime >= fadeOutStart && audioElement.currentTime < timeToStop) { 630 audioElement.volume = Math.max(0, audioElement.volume - scenario.volumeIncrement); 631 } 632 } 633 634 if(audioElement.currentTime >= timeToStop){ 635 if(scenario.wait){ 636 return; 637 } 638 if (isFinite(audioElement.duration)) { 639 audioElement.currentTime = Math.floor(audioElement.duration); // Seek to the end of the track. sometimes we may have actions at 100% 640 } else { 641 IRON.audioPlayer.next($('.sr_selectedPlayer'), audioElement, $('.sr_selectedPlayer').find('.playlist .srp_list')); 642 } 643 scenario.wait = true; 644 } 645 } 646 647 648 }, 649 650 651 getCurrentTrackID_Helper(currentPlayer, buttonEl = null) { 652 let currentTrackElement = null; 653 654 if (buttonEl) { 655 // Ensure you safely get the first element if needed 656 currentTrackElement = buttonEl.closest('.sr-playlist-item')[0] || 657 buttonEl.closest('.swiper-slide')[0]; 658 659 } else { 660 if(currentPlayer.classList.contains('srp_slider_enable')){ 661 currentTrackElement = currentPlayer.querySelector('.swiper-slide.srp_current'); 662 }else{ 663 currentTrackElement = currentPlayer.querySelector('.sr-playlist-item.current'); 664 } 665 666 } 667 let currentTrackID = null; 668 if (currentTrackElement) { 669 const postId = currentTrackElement.dataset.postId; 670 const trackId = currentTrackElement.dataset.trackid; 671 const trackPos = currentTrackElement.dataset.trackPos || 672 currentTrackElement.dataset.slideIndex; 673 674 if (trackId) { 675 currentTrackID = trackId; 676 } else if (postId) { 677 currentTrackID = postId; 678 } else { 679 currentTrackID = trackPos; 680 } 681 } else { 682 const currentTrack = IRON.sonaar.player.list.tracks[IRON.sonaar.player.currentTrack]; 683 if(!currentTrack){ 684 return; 685 } 686 const postId = currentTrack.sourcePostID; 687 const trackId = currentTrack.id; 688 const trackPos = currentTrack.trackPos; 689 690 if (trackId) { 691 currentTrackID = trackId; 692 } else if (postId) { 693 currentTrackID = postId; 694 } else { 695 currentTrackID = trackPos; 696 } 697 } 698 //convert to string 699 currentTrackID = String(currentTrackID); 700 return currentTrackID; 701 }, 702 703 getCurrentSourcePostID_Helper(currentPlayer){ 704 let currentTrackElement = currentPlayer.querySelector('.sr-playlist-item.current'); 705 currentTrackElement = currentTrackElement ? currentTrackElement : null; 706 if (currentTrackElement) { 707 currentTrackID = currentTrackElement.getAttribute('data-post-id'); 708 } else if (IRON && IRON.sonaar && IRON.sonaar.player && IRON.sonaar.player.list) { 709 currentTrackID = IRON.sonaar.player.list.tracks[IRON.sonaar.player.currentTrack]?.sourcePostID || null; 710 } 711 return currentTrackID; 712 }, 713 714 isTrackIsSpecific_Helper(scenario, buttonEl) { 715 const specificTracks = scenario.applyOn?.specificTracks; 716 if (!Array.isArray(specificTracks)) return false; 717 718 let trackID = $(buttonEl).closest('li').data('post-id') 719 ?? IRON.sonaar?.player?.list?.tracks[IRON.sonaar.player.currentTrack]?.sourcePostID; 720 721 if (!trackID) return false; 722 723 const isTrackSpecific = specificTracks.includes(parseInt(trackID, 10)); 724 725 return isTrackSpecific; 726 }, 727 728 attachDownloadButtonListener(player, scenario, resetAll = false) { 729 730 let downloadScenario = null; 731 732 for (const scenarioID in this.playerScenarioMap) { 733 if (this.playerScenarioMap?.[scenarioID]?.scenarioData?.action_when?.downloadButtonClicked) { 734 downloadScenario = scenarioID; 735 //scenario = this.playerScenarioMap[downloadScenario].scenarioData; 736 } 737 } 738 if (!downloadScenario) return; // No scenario handle the downloads so exit the function 739 740 741 const playerID = scenario.applyOn.allPlayers || !scenario.applyOn.players 742 ? 'allPlayers' 743 : scenario.applyOn.players; 744 745 const targetPlayers = scenario.applyOn.players 746 ? scenario.applyOn.players.map(id => `${id} .sr_store_force_dl_bt`) 747 : [`#${player.id} .sr_store_force_dl_bt`]; 748 749 let playerSelector = targetPlayers.join(', '); 750 751 let skip = false; 752 753 if(scenario.onceActionFilled.rememberAndDontShowAgainIfAlreadySubmitted){ 754 skip = this.hasTrackBeenActioned_anywhereOnScenario(); 755 } 756 757 if(resetAll && skip){ //resetAll comes from when we reload attachDownloadButtonListener from the askforemail function 758 playerSelector = '.iron-audioplayer .sr_store_force_dl_bt'; 759 } 760 761 // Use a Map to store download URLs in memory 762 $(playerSelector).each((_, downloadButton) => { 763 const buttonKey = downloadButton; // Use the element as the key 764 765 // Store the original href in the Map if not already stored 766 if (!this.downloadUrls.has(buttonKey)) { 767 this.downloadUrls.set(buttonKey, downloadButton.getAttribute('href')); 768 } 769 770 // Ensure the button belongs to the player or #sonaar-player 771 if (!resetAll && !player.contains(downloadButton) && 772 !document.querySelector('#sonaar-player')?.contains(downloadButton)) { 773 return; 774 } 775 776 const clickedTrackID = this.getCurrentTrackID_Helper(player, $(downloadButton)); 777 const isTrackSpecific = this.isTrackIsSpecific_Helper(scenario, $(downloadButton)); 778 779 780 if ( 781 (isTrackSpecific || !scenario.applyOn.specificTracks) && 782 !this.hasTrackBeenActioned(scenario, playerID, `track_${clickedTrackID}`, 'popup') 783 ) { 784 if (!this.hasTrackBeenActioned(scenario, playerID, `track_${clickedTrackID}`, 'popup', true)) { 785 //console.log("1"); 786 if (skip) { 787 //console.log("2"); 788 downloadButton.setAttribute('href', this.downloadUrls.get(buttonKey)); // Set the href from the stored value in memory (Map) 789 } else { 790 //console.log("3"); 791 downloadButton.removeAttribute('href'); 792 } 793 } 794 }else{ 795 //console.log("4"); 796 downloadButton.setAttribute('href', this.downloadUrls.get(buttonKey)); 797 798 } 799 800 }); 801 802 // Common click event handler function 803 const handleClick = (event) => { 804 const downloadButton = event.currentTarget; 805 const buttonKey = downloadButton; // Use the element as the key 806 const clickedTrackID = this.getCurrentTrackID_Helper(player, $(downloadButton)); 807 const isFromStickyPlayer = downloadButton.closest('#sonaar-player'); 808 const playerToCheck = (IRON.audioPlayer?.activePlayer) ? IRON.audioPlayer.activePlayer : player; 809 810 811 // Store the original href in the Map if not already stored 812 if (!this.downloadUrls.has(buttonKey)) { 813 this.downloadUrls.set(buttonKey, downloadButton.getAttribute('href')); 814 } 815 816 if(this.isPlayerExcluded(playerToCheck, scenario)){ 817 return; 818 } 819 820 if (playerID !== 'allPlayers') { // A css selector is used to target specific players 821 if (IRON.sonaar.player.selectedPlayer && isFromStickyPlayer) { 822 if (!$(playerID[0]).find(IRON.sonaar.player.selectedPlayer).length) { // Check if the sticky player is part of the playerID 823 //console.log("sticky player is not part of the playerID"); 824 return; //it means the sticky player is not part of the playerID 825 } 826 } 827 } 828 829 this.markTrackAsActioned(scenario, playerID, `track_${clickedTrackID}`, 'popup', true); 830 831 const isTrackSpecific = this.isTrackIsSpecific_Helper(scenario, $(downloadButton)); 832 833 if(scenario.onceActionFilled.rememberAndDontShowAgainIfAlreadySubmitted){ 834 skip = this.hasTrackBeenActioned_anywhereOnScenario(); 835 836 837 } 838 839 if ( 840 (isTrackSpecific || !scenario.applyOn.specificTracks) && 841 !this.hasTrackBeenActioned(scenario, playerID, `track_${clickedTrackID}`, 'popup') 842 ) { 843 if (!this.hasTrackBeenActioned(scenario, playerID, `track_${clickedTrackID}`, 'popup', true)) { 844 if (skip) { 845 //console.log("a"); 846 //if (isFromStickyPlayer) return; 847 848 // Set the href from the stored value in memory (Map) 849 downloadButton.setAttribute('href', this.downloadUrls.get(buttonKey)); 850 } else { 851 //console.log("b"); 852 event.preventDefault(); 853 this.handleActions(scenario, player, downloadButton); 854 } 855 }else{ 856 //console.log("c"); 857 downloadButton.setAttribute('href', this.downloadUrls.get(buttonKey)); 858 } 859 } else if (!skip && !downloadButton.closest('#sonaar-player')) { 860 // If the scenario was already triggered, set the href. 861 //console.log("d"); 862 downloadButton.setAttribute('href', this.downloadUrls.get(buttonKey)); 863 }else{ 864 //console.log("e"); 865 downloadButton.setAttribute('href', this.downloadUrls.get(buttonKey)); 866 } 867 }; 868 869 if(!scenario.action_when.downloadButtonClicked) return; 870 871 // Attach event listeners 872 $(document).off('click', playerSelector).on('click', playerSelector, handleClick); 873 874 if(playerSelector == "#sonaar-player .sr_store_force_dl_bt"){ // if player is sticky player, we dont want to attach it again 875 return; 876 } 877 878 $(document).off('click', '#sonaar-player').on('click', '#sonaar-player .sr_store_force_dl_bt', handleClick); 879 }, 880 881 clearWaterMarkInterval(audioElement){ 882 const watermarkAudio = document.getElementById('watermark-audio-element'); 883 884 if (watermarkAudio) { 885 watermarkAudio.pause(); 886 watermarkAudio.currentTime = 0; 887 } 888 audioElement.removeEventListener('play', this.watermark_startPlayback); 889 audioElement.removeEventListener('ended', this.watermark_stopPlayback); 890 891 if (window.WaterMarkInterval){
892 clearInterval(window.WaterMarkInterval); 893 window.WaterMarkInterval = null; 894 } 895 }, 896 897 detachTimeUpdateListener(audioElement, scenario = null) { 898 $(".sr_selectedPlayer .sonaar_fake_wave, #sonaar-player .sonaar_fake_wave, .mobileProgress").off("click.trimmed"); 899 // we detach the ad if it exists so it stops when we switch tracks. 900 const adAudio = document.getElementById('ad-audio-element'); 901 if (adAudio) { 902 adAudio.pause(); 903 adAudio.currentTime = 0; 904 adAudio.removeEventListener('canplay', this.playad_startPlayback); 905 adAudio.removeEventListener('ended', this.playad_adHasEnded) 906 adAudio.removeAttribute('src'); // Remove the source to stop loading 907 } 908 909 if (scenario && scenario.trigger[0].type === 'watermark') { 910 911 const watermarkAudio = document.getElementById('watermark-audio-element'); 912 if (watermarkAudio) { 913 watermarkAudio.pause(); 914 watermarkAudio.currentTime = 0; 915 } 916 } 917 918 if (window.WaterMarkInterval && scenario && scenario.trigger[0].type === 'watermark') { 919 audioElement.removeEventListener('play', this.watermark_startPlayback); 920 audioElement.removeEventListener('ended', this.watermark_stopPlayback); 921 clearInterval(window.WaterMarkInterval); 922 window.WaterMarkInterval = null; 923 } 924 this.hideGlobalMessageOverlay(); 925 926 // Set back the controls to original states if they were locked by the ads 927 this.togglePointerEvents([...$('.iron-audioplayer').get(), document.querySelector('#sonaar-player .player')], false); 928 929 }, 930 931 // Function to hide the message overlay globally 932 hideGlobalMessageOverlay() { 933 //remove ALL .srp-ads-message-overlay 934 $('.srp-ads-message-overlay').remove(); 935 }, 936 937 938 detachTimeUpdateOnNoScenario(player) { 939 //console.log("detachTimeUpdateOnNoScenario"); 940 const audioElement = sr_setAudioElementInstance($(player)); 941 this.activeState.playerID = null; 942 this.activeState.scenarioID = null; 943 audioElement.removeEventListener('pause', this.pauseAdPlayback); 944 this.detachTimeUpdateListener(audioElement); 945 }, 946 947 getWhenItShouldMeet(audioElement, scenarioValue, unit = 'percent') { 948 const duration = isFinite(audioElement.duration) ? audioElement.duration : 0; 949 950 if (duration > 0) { 951 switch (unit) { 952 case 'percent': 953 // Convert the percentage to required time in seconds 954 return (scenarioValue / 100) * duration; 955 case 'seconds': 956 // Directly use the scenario value as seconds 957 return scenarioValue; 958 default: 959 return 0; 960 } 961 } 962 return 0; 963 }, 964 handleActions(scenario, player, clickedElement = null) { 965 //console.log("HANDLE ===================================> ", scenario.trigger[0].type); 966 967 let trackID = null; 968 if ($(clickedElement).hasClass('sr-playlist-item')) { 969 trackID = this.getCurrentTrackID_Helper(player); 970 }else{ 971 trackID = this.getCurrentTrackID_Helper(player, $(clickedElement)); 972 } 973 974 const playerID = scenario.applyOn.allPlayers || !scenario.applyOn.players ? 'allPlayers' : scenario.applyOn.players; 975 976 selectedPlayer = (IRON.audioPlayer?.activePlayer) ? IRON.audioPlayer.activePlayer : $(player); 977 const audioElement = sr_setAudioElementInstance(selectedPlayer); 978 audioElement.muted = false; 979 980 981 982 983 scenario.trigger.forEach(action => { 984 985 if(scenario.onceActionFilled.rememberAndDontShowAgainIfAlreadySubmitted){ 986 skip = this.hasTrackBeenActioned_anywhereOnScenario(); 987 if(skip){ 988 return; 989 } 990 } 991 992 if (trackID && this.hasTrackBeenActioned(scenario, playerID, `track_${trackID}`, action.type)) { 993 return; 994 } 995 996 if (scenario.trigger[0].type !== 'trim' 997 && !scenario.action_when.downloadButtonClicked 998 && this.trackTimesRunned[scenario.id][player.id][trackID] <= 1) { 999 // If track is played for the first time, count it! 1000 this.markTrackAsActioned(scenario, playerID, `track_${trackID}`, scenario.trigger[0].type, true); 1001 } 1002 1003 if(this.hasTrackBeenActioned(scenario, playerID, `track_${trackID}`, action.type, true)){ 1004 //console.log("returned by hasTrackBeenActioned"); 1005 return; 1006 } 1007 1008 /*let chanceToTrigger = '70'; // Can be '25', '50', '75', '33.3', etc. 1009 let percentage = parseFloat(chanceToTrigger) / 100; 1010
1011 const timesRunned = this.getScenarioTimesRunned(scenario); 1012 1013 // Calculate if this run should trigger based on random chance 1014 let uniqueNum = Math.random(); 1015 console.log(uniqueNum, percentage); 1016 const shouldTrigger = uniqueNum < percentage; 1017 1018 if (!shouldTrigger) { 1019 console.log("Returned by shouldTrigger", timesRunned); 1020 return; 1021 } 1022 1023 // Continue with your logic if shouldTrigger is true 1024 console.log("Triggered at", timesRunned);*/ 1025 1026 switch (action.type) { 1027 case 'trim' : 1028 break; 1029 case 'popup': 1030 this.showPopupForm(player, clickedElement, scenario, playerID, trackID); 1031 break; 1032 case 'redirect': 1033 if(scenario.trigger[0].stopPlayer){ 1034 $(player).find('.sr-playlist-item.current .srp_audio_trigger').trigger('click'); 1035 } 1036 1037 if (scenario.trigger[0].target === '_blank') { 1038 window.open(action.url, '_blank'); 1039 } else { 1040 window.location.href = action.url; 1041 } 1042 break; 1043 case 'triggerFunction': //not used 1044 break; 1045 case 'playAd': 1046 this.playAd(scenario, action.url, player); 1047 break; 1048 case 'watermark': 1049 this.watermark(scenario, player, playerID); 1050 break; 1051 case 'scrollToId': 1052 const element = document.querySelector(action.cssId); 1053 if (element) { 1054 if(scenario.trigger[0].stopPlayer){ 1055 $(player).find('.sr-playlist-item.current .srp_audio_trigger').trigger('click'); 1056 } 1057 element.scrollIntoView({ behavior: 'smooth', block: 'start' }); 1058 1059 } 1060 break; 1061 default: 1062 } 1063 1064 if(scenario.onceActionFilled.rememberAndDontShowAgainUntilLocalStorageCleared){ 1065 this.markTrackAsActioned(scenario, playerID, `track_${trackID}`, action.type); 1066 } 1067 }); 1068 }, 1069 1070 setAdVolume(audioElement){ 1071 return audioElement.volume * 0.50; //reduce volume by 50% 1072 }, 1073 1074 playAd(scenario, url, player = null) { 1075 const audioElement = sr_setAudioElementInstance($(player)); 1076 var adAudio = document.getElementById('ad-audio-element'); 1077 1078 1079 let initialVolume = audioElement.volume; 1080 const fadeDuration = 300; 1081 const fadeInterval = 50; 1082 let fadeStep = audioElement.volume / (fadeDuration / fadeInterval); 1083 1084 // Function to fade out and pause the audio 1085 const fadeOutAndPause = () => { 1086 return new Promise((resolve) => { 1087 let fadeIntervalId = setInterval(() => { 1088 if (audioElement.volume > 0) { 1089 audioElement.volume = Math.max(0, audioElement.volume - fadeStep); 1090 } else { 1091 clearInterval(fadeIntervalId); 1092 1093 resolve(); // Notify that the fade-out and pause are done 1094 } 1095 }, fadeInterval); 1096 }); 1097 }; 1098 1099 this.playad_adHasEnded = () => { 1100 if (scenario.trigger[0].lockControl) { 1101 this.togglePointerEvents([player, document.querySelector('#sonaar-player .player')], false); 1102 } 1103 this.hideGlobalMessageOverlay(); 1104 1105 if (scenario.trigger[0].stopPlayer) { 1106 1107 if(scenario.action_when.reached_value == '100'){ 1108 // prevent hearing 0.1 seconds of the end of the track 1109 audioElement.currentTime = audioElement.duration; 1110 } 1111 1112 audioElement.muted = false; 1113 audioElement.play(); 1114 } 1115 }; 1116 1117 this.playad_startPlayback = async () => { 1118 1119 try { 1120 await adAudio.play(); 1121 adAudio.volume = audioElement.volume; 1122 if (scenario.trigger[0].lockControl === 'true') { 1123 this.togglePointerEvents([player, document.querySelector('#sonaar-player .player')], true); 1124 } 1125 1126 if (scenario.trigger[0].message) { 1127 this.displayMessageOverlay(scenario.trigger[0].message, player); 1128 } 1129 } catch (error) { 1130 console.error("Failed to play ad audio:", error); 1131 } 1132 }; 1133 1134 const initializeAdAudio = async () => { 1135 if (scenario.trigger[0].stopPlayer) { 1136 const value = scenario.action_when.reached_value; 1137 if (value !== '0' && value !== '100') { 1138 await fadeOutAndPause(); 1139 } 1140 1141 audioElement.pause(); 1142 audioElement.volume = initialVolume; // Reset volume 1143 } 1144 1145 1146 1147 if (!adAudio) { 1148 adAudio = document.createElement('audio'); 1149 adAudio.id = 'ad-audio-element'; 1150 adAudio.volume = audioElement.volume; 1151 document.body.appendChild(adAudio); 1152 } 1153 1154 adAudio.src = url; 1155 adAudio.preload = 'auto'; 1156 1157 adAudio.addEventListener('ended', this.playad_adHasEnded);
1158 adAudio.addEventListener('canplay', this.playad_startPlayback); 1159 audioElement.addEventListener('pause', this.pauseAdPlayback); 1160 }; 1161 1162 // Initialize and play ad audio 1163 initializeAdAudio(); 1164 1165 1166 }, 1167 1168 displayMessageOverlay(message, player) { 1169 let overlay = document.createElement('div'); 1170 overlay.classList.add('srp-ads-message-overlay'); 1171 overlay.textContent = message; 1172 overlay.style.position = 'absolute'; 1173 overlay.style.top = '0'; 1174 overlay.style.left = '0'; 1175 overlay.style.width = '100%'; 1176 overlay.style.height = '100%'; 1177 overlay.style.background = 'rgba(0, 0, 0, 0.7)'; 1178 overlay.style.color = '#fff'; 1179 overlay.style.display = 'flex'; 1180 overlay.style.justifyContent = 'center'; 1181 overlay.style.alignItems = 'center'; 1182 overlay.style.zIndex = '3'; 1183 1184 player.appendChild(overlay); 1185 }, 1186 watermark(scenario, player = null) { 1187 const audioElement = sr_setAudioElementInstance($(player)); 1188 let watermarkAudio = document.getElementById('watermark-audio-element'); 1189 1190 // Create or get the ad audio element 1191 if (!watermarkAudio) { 1192 watermarkAudio = document.createElement('audio'); 1193 watermarkAudio.id = 'watermark-audio-element'; 1194 document.body.appendChild(watermarkAudio); 1195 watermarkAudio.volume = this.setAdVolume(audioElement); 1196 } 1197 1198 // Set up the ad audio properties 1199 const loopGap = scenario.trigger[0].loopGap * 1000; 1200 1201 1202 watermarkAudio.src = scenario.trigger[0].url; 1203 watermarkAudio.volume = this.setAdVolume(audioElement); 1204 watermarkAudio.autoplay = true; 1205 watermarkAudio.loop = false; // Disable native looping 1206 1207 // Clear any existing interval before setting a new one 1208 if (window.WaterMarkInterval) { 1209 clearInterval(window.WaterMarkInterval); 1210 window.WaterMarkInterval = null; 1211 } 1212 1213 // Function to play ad audio every 10 seconds 1214 this.watermark_startPlayback = () => { 1215 if (window.WaterMarkInterval) { 1216 //console.log("i will return the starAdPlayback function"); 1217 return; 1218 } 1219 // Start a new interval for the watermark audio 1220 window.WaterMarkInterval = setInterval(() => { 1221 1222 if (!this.activeState.scenarioID) return; 1223 1224 watermarkAudio.currentTime = 0; 1225 1226 //console.log('Interval called. play!'); 1227 1228 watermarkAudio.volume = this.setAdVolume(audioElement); 1229 watermarkAudio.play().catch(error => console.error("Failed to play ad audio in interval:", error)); 1230 1231 1232 }, loopGap); 1233 }; 1234 1235 this.watermark_stopPlayback = () => { 1236 watermarkAudio.pause(); 1237 watermarkAudio.currentTime = 0; 1238 if (window.WaterMarkInterval) { 1239 clearInterval(window.WaterMarkInterval); 1240 window.WaterMarkInterval = null; 1241 } 1242 }; 1243 1244 // Attach event listeners to the new audio element 1245 audioElement.addEventListener('play', this.watermark_startPlayback); 1246 audioElement.addEventListener('pause', this.pauseAdPlayback); 1247 audioElement.addEventListener('ended', this.watermark_stopPlayback); 1248 1249 // If the adAudio ends naturally, reset the ad audio position to the start 1250 watermarkAudio.onended = () => { 1251 watermarkAudio.currentTime = 0; 1252 }; 1253 1254 // Start playing the ad audio immediately if the main audio is already playing 1255 if (!audioElement.paused) { 1256 this.watermark_startPlayback(); 1257 } 1258 }, 1259 1260 1261 // Function to stop ad playback 1262 pauseAdPlayback(){ 1263 const adAudio = document.getElementById('ad-audio-element'); 1264 const watermarkAudio = document.getElementById('watermark-audio-element'); 1265 if (adAudio) { 1266 adAudio.pause(); 1267 1268 } 1269 if (watermarkAudio) { 1270 watermarkAudio.pause(); 1271 } 1272 //console.log("pauseAdPlayback --> Music Paused so we should pause the Ad as well. "); 1273 if (window.WaterMarkInterval) { 1274 clearInterval(window.WaterMarkInterval); 1275 window.WaterMarkInterval = null; 1276 } 1277 }, 1278 1279 showPopupForm(player, clickedElement, scenario, playerID = null, trackIdToSave = null) { 1280 // console.log(`Showing popup form for Scenario: '${scenario.name}', Player ID: '${playerID}'`); 1281 srp_isPausedFromPopup = false; 1282 const audioElement = sr_setAudioElementInstance($(player));
1283 clickedElement = $(clickedElement); 1284 1285 if(scenario.trigger[0].popupHook == 'askForEmail'){ 1286 //console.log("Calling PopupAskForEmail..."); 1287 handleAskEmailClick(clickedElement, scenario, playerID, trackIdToSave); 1288 }else if(scenario.trigger[0].popupHook == 'elementorPopup'){ 1289 elementorProFrontend.modules.popup.showPopup( { id: scenario.trigger[0].popupID } ); 1290 }else{ 1291 let content = scenario.trigger[0].popupContent; 1292 content = this.replacePlaceholders(content); 1293 $('#sonaar-modal .sr_popup-body').html(content); 1294 sr_openPopUp(); 1295 } 1296 if(scenario.trigger[0].stopPlayer || scenario.trigger[0].required){ 1297 srp_isPausedFromPopup = true; 1298 audioElement.pause(); //for sticky only 1299 } 1300 }, 1301 replacePlaceholders(content) { 1302 // Access track data from IRON.audioPlayer.trackData 1303 const trackData = IRON.audioPlayer.currentTrackData || {}; 1304 const trackTitle = trackData.trackTitle || ''; 1305 const albumTitle = trackData.albumTitle || ''; 1306 const artistName = trackData.artistName || ''; 1307 const imgCover = trackData.albumArt || ''; 1308 1309 return content 1310 .replace(/{{track_title}}/g, trackTitle) 1311 .replace(/{{album_title}}/g, albumTitle) 1312 .replace(/{{artist_name}}/g, artistName) 1313 .replace(/{{cover_img}}/g, `<img class="srp-trigger-popup-img" style="max-width:70px;" src="${imgCover}" alt="Cover Image">`); 1314 }, 1315 initializeTrackActions() { 1316 if (!localStorage.getItem('srmp3_advanced_triggers')) { 1317 localStorage.setItem('srmp3_advanced_triggers', JSON.stringify({})); 1318 } 1319 }, 1320 getExpirationTimestamp(applyTimeSpan) { 1321 const now = new Date(); 1322 1323 switch (applyTimeSpan) { 1324 case 'minute': 1325 //console.log('Adding 1 minute.'); 1326 return now.setMinutes(now.getMinutes() + 1); 1327 case 'hour': 1328 //console.log('Adding 1 hour.'); 1329 return now.setHours(now.getHours() + 1); 1330 case 'day': 1331 //console.log('Adding 1 day.'); 1332 return now.setDate(now.getDate() + 1); 1333 case 'week': 1334 //console.log('Adding 1 week.'); 1335 return now.setDate(now.getDate() + 7); 1336 case 'month': 1337 //console.log('Adding 1 month.'); 1338 return now.setMonth(now.getMonth() + 1); 1339 default: 1340 return null; 1341 } 1342 }, 1343 1344 isScenarioExpired(scenarioID) { 1345 const advancedActions = JSON.parse(localStorage.getItem('srmp3_advanced_triggers_overall')) || {}; 1346 const expiration = advancedActions?.[`${scenarioID}_expiration`]; 1347 const now = new Date().getTime(); 1348 return expiration && now > expiration; 1349 }, 1350 1351 hasTrackBeenActioned_anywhereOnScenario() { 1352 1353 // Retrieve stored track actions 1354 const trackActions = JSON.parse(localStorage.getItem('srmp3_advanced_triggers')) || {}; 1355 1356 // Iterate over all scenarios in the stored actions 1357 for (const scenarioID in trackActions) { 1358 // Iterate over all players and tracks for each scenario 1359 for (const playerID in trackActions[scenarioID]) { 1360 for (const trackID in trackActions[scenarioID][playerID]) { 1361 for (const actionType in trackActions[scenarioID][playerID][trackID]) { 1362 // If any action is marked as true, return true immediately 1363 if (trackActions[scenarioID][playerID][trackID][actionType] === true) { 1364 return true; 1365 } 1366 } 1367 } 1368 } 1369 } 1370 1371 // If no actions were found, return false 1372 return false; 1373 }, 1374 1375 // Function to check if a specific action has been triggered for a given scenario, player, and track 1376 hasTrackBeenActioned(scenario, playerID, trackID, actionType, advancedCondition = null) { 1377 const scenarioID = scenario.id; 1378 const scenarioModifiedDate = scenario.modified_date.toString(); 1379 const storedDateKey = `${scenarioID}_modified_date`; 1380 const storedDate = localStorage.getItem(storedDateKey); 1381 // Check if the modification date has changed
1382 if (storedDate !== scenarioModifiedDate) { 1383 // Clear specific scenario data in both local storage objects 1384 let advancedActionsOverall = JSON.parse(localStorage.getItem('srmp3_advanced_triggers_overall')) || {}; 1385 delete advancedActionsOverall[`${scenarioID}_times`]; 1386 delete advancedActionsOverall[`${scenarioID}_expiration`]; 1387 localStorage.setItem('srmp3_advanced_triggers_overall', JSON.stringify(advancedActionsOverall)); 1388 1389 let trackActions = JSON.parse(localStorage.getItem('srmp3_advanced_triggers')) || {}; 1390 if (trackActions[scenarioID]) { 1391 delete trackActions[scenarioID]; // Clear only the specific scenario data 1392 } 1393 localStorage.setItem('srmp3_advanced_triggers', JSON.stringify(trackActions)); 1394 1395 // Update local storage with the new modified date 1396 localStorage.setItem(storedDateKey, scenarioModifiedDate); 1397 1398 // Optional: Log reset for debugging 1399 //console.log(`Reset local storage for scenario ID: ${scenarioID} due to date change.`); 1400 } 1401 if (advancedCondition) { 1402 1403 let advancedActions = JSON.parse(localStorage.getItem('srmp3_advanced_triggers_overall')) || {}; 1404 // Check expiration 1405 //const expiration = advancedActions?.[`${scenarioID}_expiration`]; 1406 //console.log(`Expiration: ${expiration ? new Date(expiration).toUTCString() : 'None'}`); 1407 //console.log('Is expired?', this.isScenarioExpired(expiration)); 1408 //const timeLeft = Math.floor((expiration - new Date().getTime()) / 1000); 1409 //console.log(`Time left until expiration: ${timeLeft} seconds`); 1410 1411 if (this.isScenarioExpired(scenarioID)) { 1412 //console.log('Data expired. Removing from localStorage.'); 1413 delete advancedActions[`${scenario.id}_times`]; 1414 delete advancedActions[`${scenario.id}_expiration`]; 1415 localStorage.setItem('srmp3_advanced_triggers_overall', JSON.stringify(advancedActions)); 1416 this.markTrackAsActioned(scenario, playerID, `track_${trackID}`, scenario.trigger[0].type, true); 1417 } 1418 1419 if(this.isWithinRangeLimitOfAdvancedRules(scenario)){ 1420 return true; 1421 }else{ 1422 return false; 1423 } 1424 1425 } else { 1426 const trackActions = JSON.parse(localStorage.getItem('srmp3_advanced_triggers')) || {}; 1427 return trackActions?.[scenarioID]?.[playerID]?.[trackID]?.[actionType] === true; 1428 } 1429 }, 1430 1431 isWithinRangeLimitOfAdvancedRules(scenario){ 1432 let advancedActions = JSON.parse(localStorage.getItem('srmp3_advanced_triggers_overall')) || {}; 1433 1434 const timesMarked = advancedActions?.[`${scenario.id}_times`] || 0; 1435 const { applyAfter = null, applyMaxTimes = null } = scenario.advancedRules; 1436 1437 // Check applyAfter rule 1438 if (applyAfter) { 1439 const withinApplyAfter = timesMarked <= applyAfter; 1440 return withinApplyAfter; 1441 } 1442 1443 // Check applyMaxTimes rule 1444 if (applyMaxTimes) { 1445 const withinMaxTimes = timesMarked > applyMaxTimes; 1446 return withinMaxTimes; 1447 } 1448 1449 return false; 1450 }, 1451 1452 getScenarioTimesRunned(scenario) { 1453 const scenarioID = scenario.id; 1454 const trackActions = JSON.parse(localStorage.getItem('srmp3_advanced_triggers_overall')) || {}; 1455 return trackActions[`${scenarioID}_times`] || 0; 1456 }, 1457 1458 // Function to mark a specific action as triggered 1459 markTrackAsActioned(scenario, playerID, trackID, actionType, advancedCondition = null) { 1460 console.log(`Marking track as actioned. ScenarioID: ${scenario.id}, PlayerID: ${playerID}, TrackID: ${trackID}, ActionType: ${actionType}`); 1461 1462 if (advancedCondition) { 1463 let advancedActions = JSON.parse(localStorage.getItem('srmp3_advanced_triggers_overall')) || {}; 1464 1465 if (!advancedActions[`${scenario.id}_times`]) { 1466 advancedActions[`${scenario.id}_times`] = 0; 1467 } 1468 1469 advancedActions[`${scenario.id}_times`]++; 1470 1471 // Check if expiration already exists 1472 const existingExpiration = advancedActions[`${scenario.id}_expiration`]; 1473 if (!existingExpiration) { 1474 const { applyTimeSpan = 'persistent' } = scenario.advancedRules; 1475 const expirationTimestamp = this.getExpirationTimestamp(applyTimeSpan); 1476 1477 if (expirationTimestamp) { 1478 advancedActions[`${scenario.id}_expiration`] = expirationTimestamp; 1479 } 1480 } 1481 1482 // Save the updated actions to localStorage 1483 localStorage.setItem('srmp3_advanced_triggers_overall', JSON.stringify(advancedActions)); 1484 } else { 1485 let trackActions = JSON.parse(localStorage.getItem('srmp3_advanced_triggers')) || {}; 1486 1487 if (!trackActions[scenario.id]) { 1488 trackActions[scenario.id] = {}; 1489 } 1490 1491 if (!trackActions[scenario.id][playerID]) { 1492 trackActions[scenario.id][playerID] = {}; 1493 } 1494 1495 if (!trackActions[scenario.id]["name"]) { 1496 trackActions[scenario.id]["name"] = scenario.name; 1497 } 1498 1499 if (!trackActions[scenario.id][playerID][trackID]) { 1500 trackActions[scenario.id][playerID][trackID] = {}; 1501 } 1502 1503 trackActions[scenario.id][playerID][trackID][actionType] = true; 1504 1505 // Save the updated track actions back to localStorage 1506 localStorage.setItem('srmp3_advanced_triggers', JSON.stringify(trackActions)); 1507 } 1508 }, 1509 1510 applyAllScenarios(player = null) { 1511 if(srp_advanced_triggers.length){ 1512 this.initializeTrackActions();
1513 srp_advanced_triggers.forEach(scenario => { 1514 this.applyScenario(scenario, player); 1515 }); 1516 1517 if(IRON.advancedTriggers?.players?.length){ 1518 // attach listeners to non-targeted players only if players are defined in the page 1519 this.attachListenerToPlayersWithoutScenario(); 1520 } 1521 } 1522 }, 1523 1524 attachListenerToPlayersWithoutScenario() { 1525 const allPlayers = document.querySelectorAll('.iron-audioplayer'); 1526 allPlayers.forEach(player => { 1527 1528 const playerID = player.id; 1529 const hasScenario = this.listenersAttached[playerID]; 1530 if (hasScenario) return; 1531 // Attach a click listener on the parent player using event delegation 1532 player.addEventListener('click', () => { 1533 //console.log(`Player ${playerID} clicked with no active scenario.`); 1534 this.detachTimeUpdateOnNoScenario(player); 1535 }); 1536 }); 1537 }, 1538 // Reusable function to toggle pointer events 1539 togglePointerEvents(elements, disable) { 1540 //console.log("togglePointerEvents disable = ", elements, disable); 1541 elements.forEach((element, index) => { 1542 if (element) { 1543 // Ensure the element is visible before applying pointer-events 1544 if (getComputedStyle(element).display === 'none') { 1545 //element.style.display = 'block'; // Temporarily make it visible if required 1546 } 1547 1548 element.style.pointerEvents = disable ? 'none' : 'auto'; 1549 } 1550 }); 1551 }, 1552}; 1553 1554document.addEventListener('DOMContentLoaded', function () { 1555 if (typeof elementorFrontend !== 'undefined' && elementorFrontend.isEditMode()) { 1556 $(window).on("elementor/frontend/init", function () { 1557 elementorFrontend.hooks.addAction("frontend/element_ready/music-player.default", function ($scope) { 1558 if (typeof setIronAudioplayers == "function") { 1559 var elementorWidgetID = $scope[0].dataset.id; 1560 IRON.advancedTriggers.applyAllScenarios('.elementor-widget-music-player[data-id="' + elementorWidgetID + '"] .iron-audioplayer'); 1561 } 1562 }); 1563 }); 1564 }else{ 1565 IRON.advancedTriggers.applyAllScenarios(); 1566 } 1567}); 1568
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.