PageSourceSearch

https://kphred.com/wp-content/plugins/sonaar-music-pro/public/js/srp-advanced_triggers.js?ver=5.15

js kphred.com collected 2026-10-09 17:53:31 UTC 71,200 bytes, 1,568 lines download raw bytes

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.