@@ -36,9 +36,10 @@ if ( typeof vAPI !== 'object' || vAPI.loadLargeMediaInteractive === true ) {
36
36
37
37
const largeMediaElementAttribute = 'data-' + vAPI . sessionId ;
38
38
const largeMediaElementSelector =
39
- ':root audio[' + largeMediaElementAttribute + '],\n' +
40
- ':root img[' + largeMediaElementAttribute + '],\n' +
41
- ':root video[' + largeMediaElementAttribute + ']' ;
39
+ ':root audio[' + largeMediaElementAttribute + '],\n' +
40
+ ':root img[' + largeMediaElementAttribute + '],\n' +
41
+ ':root picture[' + largeMediaElementAttribute + '],\n' +
42
+ ':root video[' + largeMediaElementAttribute + ']' ;
42
43
43
44
/******************************************************************************/
44
45
@@ -51,9 +52,7 @@ const mediaNotLoaded = function(elem) {
51
52
case 'video' :
52
53
return elem . error !== null ;
53
54
case 'img' :
54
- if ( elem . naturalWidth !== 0 || elem . naturalHeight !== 0 ) {
55
- break ;
56
- }
55
+ if ( elem . naturalWidth !== 0 || elem . naturalHeight !== 0 ) { break ; }
57
56
const style = window . getComputedStyle ( elem ) ;
58
57
// For some reason, style can be null with Pale Moon.
59
58
return style !== null ?
@@ -74,50 +73,57 @@ const mediaNotLoaded = function(elem) {
74
73
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement
75
74
76
75
const surveyMissingMediaElements = function ( ) {
77
- var largeMediaElementCount = 0 ;
78
- var elems = document . querySelectorAll ( 'audio,img,video' ) ;
79
- var i = elems . length , elem ;
80
- while ( i -- ) {
81
- elem = elems [ i ] ;
82
- if ( mediaNotLoaded ( elem ) ) {
83
- elem . setAttribute ( largeMediaElementAttribute , '' ) ;
84
- largeMediaElementCount += 1 ;
76
+ let largeMediaElementCount = 0 ;
77
+ for ( const elem of document . querySelectorAll ( 'audio,img,video' ) ) {
78
+ if ( mediaNotLoaded ( elem ) === false ) { continue ; }
79
+ elem . setAttribute ( largeMediaElementAttribute , '' ) ;
80
+ largeMediaElementCount += 1 ;
81
+ switch ( elem . localName ) {
82
+ case 'img' : {
83
+ const picture = elem . closest ( 'picture' ) ;
84
+ if ( picture !== null ) {
85
+ picture . setAttribute ( largeMediaElementAttribute , '' ) ;
86
+ }
87
+ } break ;
88
+ default :
89
+ break ;
85
90
}
86
91
}
87
92
return largeMediaElementCount ;
88
93
} ;
89
94
90
- if ( surveyMissingMediaElements ( ) === 0 ) {
91
- return ;
92
- }
95
+ if ( surveyMissingMediaElements ( ) === 0 ) { return ; }
93
96
94
97
vAPI . loadLargeMediaInteractive = true ;
95
98
96
- // Insert custom style tag.
97
- let styleTag = document . createElement ( 'style' ) ;
98
- styleTag . setAttribute ( 'type' , 'text/css' ) ;
99
- styleTag . textContent = [
100
- largeMediaElementSelector + ' {' ,
101
- 'border: 1px dotted red !important;' ,
102
- 'box-sizing: border-box !important;' ,
103
- 'cursor: zoom-in !important;' ,
104
- 'display: inline-block;' ,
105
- 'font-size: 1em !important;' ,
106
- 'min-height: 1em !important;' ,
107
- 'min-width: 1em !important;' ,
108
- 'opacity: 1 !important;' ,
109
- 'outline: none !important;' ,
110
- '}'
111
- ] . join ( '\n' ) ;
112
- document . head . appendChild ( styleTag ) ;
99
+ // Insert CSS to highlight blocked media elements.
100
+ if ( vAPI . largeMediaElementStyleSheet === undefined ) {
101
+ vAPI . largeMediaElementStyleSheet = [
102
+ largeMediaElementSelector + ' {' ,
103
+ 'border: 2px dotted red !important;' ,
104
+ 'box-sizing: border-box !important;' ,
105
+ 'cursor: zoom-in !important;' ,
106
+ 'display: inline-block;' ,
107
+ 'font-size: 1rem !important;' ,
108
+ 'min-height: 1em !important;' ,
109
+ 'min-width: 1em !important;' ,
110
+ 'opacity: 1 !important;' ,
111
+ 'outline: none !important;' ,
112
+ 'visibility: visible !important;' ,
113
+ 'z-index: 2147483647' ,
114
+ '}' ,
115
+ ] . join ( '\n' ) ;
116
+ vAPI . userStylesheet . add ( vAPI . largeMediaElementStyleSheet ) ;
117
+ vAPI . userStylesheet . apply ( ) ;
118
+ }
113
119
114
120
/******************************************************************************/
115
121
116
122
const stayOrLeave = ( ( ) => {
117
- let timer = null ;
123
+ let timer ;
118
124
119
125
const timeoutHandler = function ( leaveNow ) {
120
- timer = null ;
126
+ timer = undefined ;
121
127
if ( leaveNow !== true ) {
122
128
if (
123
129
document . querySelector ( largeMediaElementSelector ) !== null ||
@@ -127,17 +133,16 @@ const stayOrLeave = (( ) => {
127
133
}
128
134
}
129
135
// Leave
130
- if ( styleTag !== null ) {
131
- styleTag . parentNode . removeChild ( styleTag ) ;
132
- styleTag = null ;
136
+ for ( const elem of document . querySelectorAll ( largeMediaElementSelector ) ) {
137
+ elem . removeAttribute ( largeMediaElementAttribute ) ;
133
138
}
134
139
vAPI . loadLargeMediaInteractive = false ;
135
140
document . removeEventListener ( 'error' , onLoadError , true ) ;
136
141
document . removeEventListener ( 'click' , onMouseClick , true ) ;
137
142
} ;
138
143
139
144
return function ( leaveNow ) {
140
- if ( timer !== null ) {
145
+ if ( timer !== undefined ) {
141
146
clearTimeout ( timer ) ;
142
147
}
143
148
if ( leaveNow ) {
@@ -160,24 +165,44 @@ const loadImage = async function(elem) {
160
165
161
166
elem . setAttribute ( 'src' , src ) ;
162
167
elem . removeAttribute ( largeMediaElementAttribute ) ;
168
+
169
+ switch ( elem . localName ) {
170
+ case 'img' : {
171
+ const picture = elem . closest ( 'picture' ) ;
172
+ if ( picture !== null ) {
173
+ picture . removeAttribute ( largeMediaElementAttribute ) ;
174
+ }
175
+ } break ;
176
+ default :
177
+ break ;
178
+ }
179
+
163
180
stayOrLeave ( ) ;
164
181
} ;
165
182
166
183
/******************************************************************************/
167
184
168
185
const onMouseClick = function ( ev ) {
169
- if ( ev . button !== 0 ) { return ; }
170
-
171
- const elem = ev . target ;
172
- if ( elem . matches ( largeMediaElementSelector ) === false ) { return ; }
186
+ if ( ev . button !== 0 || ev . isTrusted === false ) { return ; }
187
+
188
+ const toLoad = [ ] ;
189
+ const elems = document . elementsFromPoint instanceof Function
190
+ ? document . elementsFromPoint ( ev . clientX , ev . clientY )
191
+ : [ ev . target ] ;
192
+ for ( const elem of elems ) {
193
+ if ( elem . matches ( largeMediaElementSelector ) && mediaNotLoaded ( elem ) ) {
194
+ toLoad . push ( elem ) ;
195
+ }
196
+ }
173
197
174
- if ( mediaNotLoaded ( elem ) === false ) {
175
- elem . removeAttribute ( largeMediaElementAttribute ) ;
198
+ if ( toLoad . length === 0 ) {
176
199
stayOrLeave ( ) ;
177
200
return ;
178
201
}
179
202
180
- loadImage ( elem ) ;
203
+ for ( const elem of toLoad ) {
204
+ loadImage ( elem ) ;
205
+ }
181
206
182
207
ev . preventDefault ( ) ;
183
208
ev . stopPropagation ( ) ;
@@ -210,7 +235,7 @@ document.addEventListener('error', onLoadError, true);
210
235
211
236
/******************************************************************************/
212
237
213
- vAPI . shutdown . add ( function ( ) {
238
+ vAPI . shutdown . add ( ( ) => {
214
239
stayOrLeave ( true ) ;
215
240
} ) ;
216
241
0 commit comments