1
+ const EVENTS = [
2
+ 'auxclick' ,
3
+ 'click' ,
4
+ 'contextmenu' ,
5
+ 'dblclick' ,
6
+ 'keydown' ,
7
+ 'keyup' ,
8
+ 'mousedown' ,
9
+ 'mouseup' ,
10
+ 'touchend'
11
+ ]
12
+
13
+ export default class {
14
+ constructor ( ) {
15
+ this . trying = false ;
16
+ this . state = 'blocked' ;
17
+ this . audioFile = this . createAudioData ( ) ;
18
+
19
+ EVENTS . forEach ( evtName => {
20
+ window . addEventListener ( evtName , tryUnblock , { capture : true , passive : true } ) ;
21
+ } )
22
+ }
23
+
24
+ tryUnblock ( ) {
25
+ if ( this . state === 'allowed' || this . trying ) return ;
26
+ createHTMLAudio ( ) ;
27
+ }
28
+
29
+ createAudioData ( ) {
30
+ const rate = 48000 ;
31
+ const arrayBuffer = new ArrayBuffer ( 10 ) ;
32
+ const dataView = new DataView ( arrayBuffer ) ;
33
+
34
+ dataView . setUint32 ( 0 , rate , true ) ;
35
+ dataView . setUint32 ( 4 , rate , true ) ;
36
+ dataView . setUint16 ( 8 , 1 , true ) ;
37
+
38
+ const missingCharacters = window . btoa ( String . fromCharCode ( ...new Uint8Array ( arrayBuffer ) ) ) . slice ( 0 , 13 ) ;
39
+
40
+ return `data:audio/wav;base64,UklGRisAAABXQVZFZm10IBAAAAABAAEA${ missingCharacters } AgAZGF0YQcAAACAgICAgICAAAA=` ;
41
+ }
42
+
43
+ createHTMLAudio ( ) {
44
+ this . trying = true ;
45
+
46
+ let audio = document . createElement ( 'audio' ) ;
47
+
48
+ audio . setAttribute ( 'x-webkit-airplay' , 'deny' ) ;
49
+ audio . preload = 'auto' ;
50
+ audio . loop = true ;
51
+ audio . src = this . audioFile ;
52
+ audio . load ( ) ;
53
+
54
+ audio . play ( ) . then ( ( ) => {
55
+ this . state = 'allowed' ;
56
+ } , ( ) => {
57
+ this . state = 'blocked' ;
58
+ audio . src = "about:blank" ;
59
+ audio . load ( ) ;
60
+ audio = null ;
61
+ this . trying = false ;
62
+ } ) ;
63
+ }
64
+
65
+ destroy ( ) {
66
+ EVENTS . forEach ( evtName => {
67
+ window . removeEventListener ( evtName , tryUnblock , { capture : true , passive : true } ) ;
68
+ } ) ;
69
+ }
70
+
71
+ get allowed ( ) {
72
+ return this . state === 'allowed' ;
73
+ }
74
+ }
0 commit comments