@@ -2,12 +2,17 @@ import {
2
2
registerBidder
3
3
} from '../src/adapters/bidderFactory.js' ;
4
4
5
+ import { percentInView } from '../libraries/percentInView/percentInView.js' ;
6
+
5
7
import {
6
8
deepAccess ,
7
9
isFn ,
8
10
logError ,
9
11
isArray ,
10
12
formatQS ,
13
+ getWindowTop ,
14
+ isNumber ,
15
+ isStr ,
11
16
deepSetValue
12
17
} from '../src/utils.js' ;
13
18
@@ -62,6 +67,76 @@ export function validateVideo(mediaTypes) {
62
67
return video . context !== ADPOD ;
63
68
}
64
69
70
+ export function _getMinSize ( sizes ) {
71
+ if ( ! sizes || sizes . length === 0 ) return undefined ;
72
+ return sizes . reduce ( ( minSize , currentSize ) => {
73
+ const minArea = minSize . w * minSize . h ;
74
+ const currentArea = currentSize . w * currentSize . h ;
75
+ return currentArea < minArea ? currentSize : minSize ;
76
+ } ) ;
77
+ }
78
+
79
+ export function _canSelectViewabilityContainer ( ) {
80
+ try {
81
+ window . top . document . querySelector ( '#viewability-container' ) ;
82
+ return true ;
83
+ } catch ( e ) {
84
+ return false ;
85
+ }
86
+ }
87
+
88
+ export function _isViewabilityMeasurable ( element ) {
89
+ if ( ! element ) return false ;
90
+ return _canSelectViewabilityContainer ( element ) ;
91
+ }
92
+
93
+ export function _getViewability ( element , topWin , { w, h } = { } ) {
94
+ return topWin . document . visibilityState === 'visible'
95
+ ? percentInView ( element , topWin , { w, h } )
96
+ : 0 ;
97
+ }
98
+
99
+ export function detectViewability ( bid ) {
100
+ const { params, adUnitCode } = bid ;
101
+
102
+ const viewabilityContainerIdentifier = params . viewabilityContainerIdentifier ;
103
+
104
+ let element = null ;
105
+ let bidParamSizes = null ;
106
+ let minSize = [ ] ;
107
+
108
+ if ( isStr ( viewabilityContainerIdentifier ) ) {
109
+ try {
110
+ element = document . querySelector ( viewabilityContainerIdentifier ) || window . top . document . querySelector ( viewabilityContainerIdentifier ) ;
111
+ if ( element ) {
112
+ bidParamSizes = [ element . offsetWidth , element . offsetHeight ] ;
113
+ minSize = _getMinSize ( bidParamSizes )
114
+ }
115
+ } catch ( e ) {
116
+ logError ( `Error while trying to find viewability container element: ${ viewabilityContainerIdentifier } ` ) ;
117
+ }
118
+ }
119
+
120
+ if ( ! element ) {
121
+ // Get the sizes from the mediaTypes object if it exists, otherwise use the sizes array from the bid object
122
+ bidParamSizes = bid . mediaTypes && bid . mediaTypes . banner && bid . mediaTypes . banner . sizes ? bid . mediaTypes . banner . sizes : bid . sizes ;
123
+ bidParamSizes = typeof bidParamSizes === 'undefined' && bid . mediaType && bid . mediaType . video && bid . mediaType . video . playerSize ? bid . mediaType . video . playerSize : bidParamSizes ;
124
+ bidParamSizes = typeof bidParamSizes === 'undefined' && bid . mediaType && bid . mediaType . video && isNumber ( bid . mediaType . video . w ) && isNumber ( bid . mediaType . h ) ? [ bid . mediaType . video . w , bid . mediaType . video . h ] : bidParamSizes ;
125
+ minSize = _getMinSize ( bidParamSizes ?? [ ] )
126
+ element = document . getElementById ( adUnitCode ) ;
127
+ }
128
+
129
+ if ( _isViewabilityMeasurable ( element ) ) {
130
+ const minSizeObj = {
131
+ w : minSize [ 0 ] ,
132
+ h : minSize [ 1 ]
133
+ }
134
+ return Math . round ( _getViewability ( element , getWindowTop ( ) , minSizeObj ) )
135
+ }
136
+
137
+ return null ;
138
+ }
139
+
65
140
/**
66
141
* Get ids from Prebid User ID Modules and add them to the payload
67
142
*/
@@ -91,9 +166,10 @@ export const spec = {
91
166
const hasMediaTypes = Boolean ( mediaTypes ) && ( Boolean ( mediaTypes [ BANNER ] ) || Boolean ( mediaTypes [ VIDEO ] ) ) ;
92
167
const isValidBanner = validateBanner ( mediaTypes ) ;
93
168
const isValidVideo = validateVideo ( mediaTypes ) ;
169
+ const isValidViewability = typeof params . viewabilityPercentage === 'undefined' || ( isNumber ( params . viewabilityPercentage ) && params . viewabilityPercentage >= 0 && params . viewabilityPercentage <= 1 ) ;
94
170
const hasRequiredBidParams = Boolean ( params . placementId ) ;
95
171
96
- const isValid = hasBidId && hasMediaTypes && isValidBanner && isValidVideo && hasRequiredBidParams ;
172
+ const isValid = hasBidId && hasMediaTypes && isValidBanner && isValidVideo && hasRequiredBidParams && isValidViewability ;
97
173
if ( ! isValid ) {
98
174
logError (
99
175
`Invalid bid request:
@@ -120,10 +196,18 @@ export const spec = {
120
196
params,
121
197
sizes,
122
198
} = bid ;
199
+
200
+ let detectedViewabilityPercentage = detectViewability ( bid ) ;
201
+ if ( isNumber ( detectedViewabilityPercentage ) ) {
202
+ detectedViewabilityPercentage = detectedViewabilityPercentage / 100 ;
203
+ }
204
+
123
205
return {
124
206
bidId,
125
207
mediaTypes,
126
208
sizes,
209
+ detectedViewabilityPercentage,
210
+ declaredViewabilityPercentage : bid . params . viewabilityPercentage ?? null ,
127
211
placementId : params . placementId ,
128
212
floor : getBidFloor ( bid ) ,
129
213
} ;
0 commit comments