PageSourceSearch

https://cafemontaigne.com/wp-content/plugins/ab-audio-sync/ab-audio-sync.js?ver=7.1.2

js cafemontaigne.com collected 2026-09-24 21:30:46 UTC 13,239 bytes, 367 lines download raw bytes

1var DEBUG = false;
2var audio_groups = [];
3
4var SEL_GROUP =         '.audio-sync-group';
5var SEL_BUFFERING_LBL = '.audio-sync-buffering-label';
6var SEL_PLAYING_LBL =   '.audio-sync-playing-label';
7var SEL_AUDIO_A =       '.audio-sync-a';
8var SEL_AUDIO_B =       '.audio-sync-b';
9var SEL_PLAY_BTN =      '.audio-btn-sync-play';
10var SEL_AUDIO_A_BTN =   '.audio-btn-sync-a';
11var SEL_AUDIO_B_BTN =   '.audio-btn-sync-b';
12
13var CLS_BUFFERING = 'audio-sync-buffering';
14var CLS_BUFFERED =  'audio-sync-buffered';
15var CLS_PLAYING =   'audio-sync-playing';
16var CLS_PLAYING_A = 'audio-sync-playing-a';
17var CLS_PLAYING_B = 'audio-sync-playing-b';
18
19var DATASET_AUDIO_VOLUME =   'audioSyncVolume';
20var DATASET_TOGGLE_TIMER =   'audioSyncToggleTimer';
21var DATASET_BUFFERING_TEXT = 'audioSyncBufferingText';
22var DATASET_BUFFERED_TEXT =  'audioSyncBufferedText';
23var DATASET_PLAYING_A_TEXT = 'audioSyncPlayingAText';
24var DATASET_PLAYING_B_TEXT = 'audioSyncPlayingBText';
25
26// Entry point & sanitization
27jQuery(document).ready(function() {
28  // Banner
29  debug('  ___      _______    ___            _ _         _____                    \n' +
30        ' / _ \\    / / ___ \\  / _ \\          | (_)       /  ___|                   \n' +
31        '/ /_\\ \\  / /| |_/ / / /_\\ \\_   _  __| |_  ___   \\ `--. _   _ _ __   ___   \n' +
32        '|  _  | / / | ___ \\ |  _  | | | |/ _` | |/ _ \\   `--. \\ | | | \'_ \\ / __| \n' +
33        '| | | |/ /  | |_/ / | | | | |_| | (_| | | (_) | /\\__/ / |_| | | | | (__   \n' +
34        '\\_| |_/_/   \\____/  \\_| |_/\\__,_|\\__,_|_|\\___/  \\____/ \\__, |_| |_|\\___|  \n' +
35        '                                                        __/ |             \n' +
36        '                                                       |___/              \n');
37
38  // Find all audio groups
39  var groups = jQuery(SEL_GROUP);
40  debug('Audio groups found: ' + groups.length);
41
42  jQuery.each(groups, function(gid, group) {
43    debug('groups[' + gid + ']: ' + group);
44
45    // Find the play button(s)
46    var btnPlay = jQuery(group).find(SEL_PLAY_BTN);
47    if( btnPlay === null || btnPlay.length <= 0 ) {
48      debug('Group[' + gid + '] does not have a play button!');
49      return;
50    }
51
52    // Find the "A" button
53    var btnA = jQuery(group).find(SEL_AUDIO_A_BTN);
54    if( btnA === null || btnA.length <= 0 ) {
55      debug('Group[' + gid + '] does not have an "A" button');
56    }
57
58    // Find the "B" button
59    var btnB = jQuery(group).find(SEL_AUDIO_B_BTN);
60    if( btnB === null || btnB.length <= 0 ) {
61      debug('Group[' + gid + '] does not have a "B" button');
62    }
63
64    // Find the "A" audio track
65    var audioA = jQuery(group).find(SEL_AUDIO_A);
66    if( audioA === null || audioA.length <= 0 ) {
67      debug('Group[' + gid + '] does not have an "A" audio track!');
68      return;
69    } else if( audioA.length > 1 ) {
70      debug('Group[' + gid + '] has too many "A" audio tracks!');
71      return;
72    }
73
74    // Find the "B" audio track
75    var audioB = jQuery(group).find(SEL_AUDIO_B);
76    if( audioB === null || audioB.length <= 0 ) {
77      debug('Group[' + gid + '] does not have an "B" audio track!');
78      return;
79    } else if( audioB.length > 1 ) {
80      debug('Group[' + gid + !'] has too many "B" audio tracks!');
81      return;
82    }
83
84    var soundA, soundB;
85    if( audioA.is('a') ) {
86      debug('Using link (href) object for audio "A"');
87      soundA = audioA.attr('href');
88      if( !soundA ) {
89        debug('Missing audio file for audio "A" link');
90        return;
91      }
92    } else if( audioA.is('audio') ) {
93      debug('Using HTML5 audio (src) object for audio "A"');
94      soundA = audioA[0].src;
95      if( !soundA ) {
96        debug('Missing audio file for audio "A" HTML5 audio object');
97        return;
98      }
99      audioA.hide();
100    } else {
101      debug('Could not find a usable audio file for audio "A"');
102      return;
103    }
104    if( audioB.is('a') ) {
105      debug('Using link (href) object for audio "B"');
106      soundB = audioB.attr('href');
107      if( !soundB ) {
108        debug('Missing audio file for audio "B" link');
109        return;
110      }
111    } else if( audioB.is('audio') ) {
112      debug('Using HTML5 audio (src) object for audio "B"');
113      soundB = audioB[0].src;
114      if( !soundB ) {
115        debug('Missing audio file for audio "B" HTML5 audio object');
116        return;
117      }
118      audioB.hide();
119    } else {
120      debug('Could not find a usable audio file for audio "B"');
121      return;
122    }
123
124    if( (device.mobile() || device.tablet()) && device.ios() ) {
125      debug('Apple iOS detected. Disabling audio auto-enable');
126      Howler.mobileAutoEnable = false;
127    }
128
129    debug('AudioA: Using audio source: ' + soundA);
130    soundA = new Howl({
131      src: [soundA],
132      volume: parseInt(audioA[0].dataset[DATASET_AUDIO_VOLUME] || 100) / 100,
133      onload: function() { debug('AudioA "onload" event!'); },
134      onloaderror: function(id, err) { debug('AudioA "onloaderror" event! (err=' + err + ')'); },
135      onplay: function() { debug('AudioA "onplay" event!'); },
136      onend: function() { debug('AudioA "onend" event!'); },
137      onpause: function() { debug('AudioA "onpause" event!'); },
138      onstop: function() { debug('AudioA "onstop" event!'); },
139      onmute: function() { debug('AudioA "onmute" event!'); },
140      onvolume: function() { debug('AudioA "onvolume" event!'); },
141      onrate: function() { debug('AudioA "onrate" event!'); },
142      onseek: function() { debug('AudioA "onseek" event!'); },
143      onfade: function() { debug('AudioA "onfade" event!'); }
144    });
145    debug('AudioB: Using audio source: ' + soundB);
146    soundB = new Howl({
147      src: [soundB],
148      volume: parseInt(audioB[0].dataset[DATASET_AUDIO_VOLUME] || 100) / 100,
149      onload: function() { debug('AudioB "onload" event!'); },
150      onloaderror: function(id, err) { debug('AudioB "onloaderror" event! (err=' + err + ')'); },
151      onplay: function() { debug('AudioB "onplay" event!'); },
152      onend: function() { debug('AudioB "onend" event!'); },
153      onpause: function() { debug('AudioB "onpause" event!'); },
154      onstop: function() { debug('AudioB "onstop" event!'); },
155      onmute: function() { debug('AudioB "onmute" event!'); },
156      onvolume: function() { debug('AudioB "onvolume" event!'); },
157      onrate: function() { debug('AudioB "onrate" event!'); },
158      onseek: function() { debug('AudioB "onseek" event!'); },
159      onfade: function() { debug('AudioB "onfade" event!'); }
160    });
161
162    // Web Audio is effectively muted on iOS mobile without user interaction
163    if( (device.mobile() || device.tablet()) && device.ios() ) {
164      debug('Adding EventListener(touchend)');
165      window.addEventListener('touchend', unlockIOSAudioPlayback);
166    }
167
168    // Enable button once audio sources have loaded
169    handleBufferingState(group, false);
170    soundA.once('load', function() {
171      debug('audio[' + gid + '].soundA has loaded');
172      if( soundA.state() === 'loaded' )
173        handleBufferingState(group, true);
174    });
175    soundB.once('load', function() {
176      debug('audio[' + gid + '].soundB has loaded');
177      if( soundA.state() === 'loaded' )
178        handleBufferingState(group, true);
179    });
180
181    audio_groups.push({
182      // jQuery group object
183      group: group,
184      // jQuery play button object
185      btnPlay: btnPlay,
186      // jQuery toggle button objects
187      btnA: btnA,
188      btnB: btnB,
189      // Web audio objects
190      audioA: audioA,
191      audioA_sound: soundA,
192      audioB: audioB,
193      audioB_sound: soundB,
194    });
195  });
196
197  // Check that at least one audio group exists
198  if( audio_groups === null || audio_groups.length <= 0 ) {
199    debug('No usable audio groups found');
200    return;
201  }
202
203  // Disable all audio buttons in each group
204  debug('Disabling group buttons');
205  groups.find(SEL_PLAY_BTN).attr('disabled', 'true');
206  groups.find(SEL_AUDIO_A_BTN).attr('disabled', 'true');
207  groups.find(SEL_AUDIO_B_BTN).attr('disabled', 'true');
208  debug('Starting ABAudioSync()');
209  ABAudioSync();
210});
211
212var unlockIOSAudioPlayback = function() {
213  var ctx = Howler.ctx;
214  debug('Attempting to enable iOS Web Audio.');
215  // create empty buffer
216  var buffer = ctx.createBuffer(1, 22050, 22050);
217  var source = ctx.createBufferSource();
218  source.buffer = buffer;
219  // connect to output (your speakers)
220  source.connect(ctx.destination);
221  // play the object
222  if (typeof source.noteOn === 'undefined') {
223    source.start(0);
224  } else {
225    source.noteOn(0);
226  }
227  // Wait to remove the listener
228  source.onended = function() {
229    debug('iOS unlock state (onended): ' + source);
230    window.removeEventListener('touchend', unlockIOSAudioPlayback);
231  };
232};
233
234
235function handleBufferingState(group, buffered) {
236  if( !buffered ) {
237    jQuery(group).find(SEL_BUFFERING_LBL)
238      .text(group.dataset[DATASET_BUFFERING_TEXT] || 'Buffering audio...')
239      .addClass(CLS_BUFFERING);
240  } else {
241    jQuery(group).find(SEL_BUFFERING_LBL)
242      .text(group.dataset[DATASET_BUFFERED_TEXT] || '')
243      .addClass(CLS_BUFFERED);
244    jQuery(group).find(SEL_PLAY_BTN).attr('disabled', false);
245  }
246}
247
248
249function ABAudioSync() {
250  jQuery.each(audio_groups, function(aid, audio) {
251    debug('Setting Audio Group #' + aid + ' Play button click handlers');
252    jQuery.each(audio.btnPlay, function(bid, btn) {
253      jQuery(btn).click({group: aid, btn: bid}, btnPlay_Clicked);
254    });
255
256    if( audio.btnA ) {
257      debug('Setting Audio Group #' + aid + ' "A" button click handlers');
258      jQuery.each(audio.btnA, function(bid, btn) {
259        jQuery(btn).click({group: aid, btn: bid}, btnA_Clicked);
260      });
261    }
262
263    if( audio.btnB ) {
264      debug('Setting Audio Group #' + aid + ' "B" button click handlers');
265      jQuery.each(audio.btnB, function(bid, btn) {
266        jQuery(btn).click({group: aid, btn: bid}, btnB_Clicked);
267      });
268    }
269  });
270}
271
272var timer = Array();
273
274function btnPlay_Clicked(event) {
275  debug('audio[' + event.data.group + '].btnPlay[' + event.data.btn + '] was clicked');
276  var gid = event.data.group;
277  var group = audio_groups[gid];
278
279  // Clear any timers active
280  if( timer[gid] ) clearInterval(timer[gid]);
281
282  if( group.audioA_sound.playing() || group.audioB_sound.playing() ) {
283    // Stop all
284    group.audioA_sound.stop();
285    group.audioB_sound.stop();
286    // Clear any end handlers
287    group.audioA_sound.off('end');
288    // Disable buttons
289    group.btnPlay.text('Play');
290    group.btnA.attr('disabled','true');
291    group.btnB.attr('disabled','true');
292    group.btnA.removeClass(CLS_PLAYING);
293    group.btnB.removeClass(CLS_PLAYING);
294    jQuery(group.group).find(SEL_PLAYING_LBL)
295      .text('').removeClass(CLS_PLAYING_A).removeClass(CLS_PLAYING_B);
296  } else {
297    // If the track ends, "press" the stop button
298    group.audioA_sound.once('end', function() { btnPlay_Clicked(event); });
299    // Enable buttons
300    group.btnPlay.text('Stop');
301    group.btnA.attr('disabled','true');
302    group.btnB.removeAttr('disabled');
303    jQuery(group.group).find(SEL_PLAYING_LBL)
304      .text(group.group.dataset[DATASET_PLAYING_A_TEXT] || 'Playing track A')
305      .addClass(CLS_PLAYING_A).removeClass(CLS_PLAYING_B);
306    // Play audioA
307    group.audioB_sound.stop();
308    group.audioA_sound.stop();
309    group.audioA_sound.play();
310    // Take care of any toggle timers defined
311    if( group.group.dataset[DATASET_TOGGLE_TIMER] ) {
312      var timer_s = parseInt(group.group.dataset[DATASET_TOGGLE_TIMER]);
313      debug('Starting ' + timer_s + 's toggle timer for group[' + gid + ']!');
314      (function(group, gid) {
315        timer[gid] = setInterval(function() {
316          debug('Switching tracks of group[' + gid + ']');
317          switchTracks(group);
318        }, timer_s * 1000);
319      })(group, gid);
320    }
321  }
322}
323
324function switchTracks(group) {
325  if( !group.audioA_sound.playing() ) {
326    // Switch tracks
327    debug('Switching tracks: AudioB -> AudioA');
328    group.btnA.attr('disabled','true');
329    group.btnB.removeAttr('disabled');
330    group.btnB.removeClass(CLS_PLAYING);
331    group.btnA.addClass(CLS_PLAYING);
332    jQuery(group.group).find(SEL_PLAYING_LBL)
333      .text(group.group.dataset[DATASET_PLAYING_A_TEXT] || 'Playing track A')
334      .addClass(CLS_PLAYING_A).removeClass(CLS_PLAYING_B);
335    group.audioA_sound.seek(group.audioB_sound.seek());
336    group.audioB_sound.stop();
337    group.audioA_sound.play();
338  } else {
339    // Switch tracks
340    debug('Switching tracks: AudioA -> AudioB');
341    group.btnB.attr('disabled','true');
342    group.btnA.removeAttr('disabled');
343    group.btnA.removeClass(CLS_PLAYING);
344    group.btnB.addClass(CLS_PLAYING);
345    jQuery(group.group).find(SEL_PLAYING_LBL)
346      .text(group.group.dataset[DATASET_PLAYING_B_TEXT] || 'Playing track B')
347      .addClass(CLS_PLAYING_B).removeClass(CLS_PLAYING_A);
348    group.audioB_sound.seek(group.audioA_sound.seek());
349    group.audioA_sound.stop();
350    group.audioB_sound.play();
351  }
352}
353
354function btnA_Clicked(event) {
355  debug('audio[' + event.data.group + '].btnA[' + event.data.btn + '] was clicked');
356  switchTracks(audio_groups[event.data.group]);
357}
358
359function btnB_Clicked(event) {
360  debug('audio[' + event.data.group + '].btnB[' + event.data.btn + '] was clicked');
361  switchTracks(audio_groups[event.data.group]);
362}
363
364function debug(msg) {
365  if( DEBUG )
366    console.debug(msg);
367}

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.