|
| 1 | +import * as utils from '../src/utils'; |
| 2 | +import {registerBidder} from '../src/adapters/bidderFactory'; |
| 3 | +import {config} from '../src/config'; |
| 4 | +import {Renderer} from '../src/Renderer'; |
| 5 | +import {BANNER, VIDEO} from '../src/mediaTypes'; |
| 6 | + |
| 7 | +export const helper = { |
| 8 | + getTopWindowDomain: function (url) { |
| 9 | + const domainStart = url.indexOf('://') + '://'.length; |
| 10 | + return url.substring(domainStart, url.indexOf('/', domainStart) < 0 ? url.length : url.indexOf('/', domainStart)); |
| 11 | + }, |
| 12 | + startsWith: function (str, search) { |
| 13 | + return str.substr(0, search.length) === search; |
| 14 | + }, |
| 15 | + getMediaType: function (bid) { |
| 16 | + if (bid.ext) { |
| 17 | + if (bid.ext.media_type) { |
| 18 | + return bid.ext.media_type.toLowerCase(); |
| 19 | + } else if (bid.ext.vast_url) { |
| 20 | + return VIDEO; |
| 21 | + } else { |
| 22 | + return BANNER; |
| 23 | + } |
| 24 | + } |
| 25 | + return BANNER; |
| 26 | + } |
| 27 | +}; |
| 28 | + |
| 29 | +export const spec = { |
| 30 | + code: 'cleanmedianet', |
| 31 | + aliases: [], |
| 32 | + supportedMediaTypes: [BANNER, VIDEO], |
| 33 | + |
| 34 | + isBidRequestValid: function (bid) { |
| 35 | + return ( |
| 36 | + !!bid.params.supplyPartnerId && |
| 37 | + typeof bid.params.supplyPartnerId === 'string' && |
| 38 | + (typeof bid.params.bidfloor === 'undefined' || |
| 39 | + typeof bid.params.bidfloor === 'number') && |
| 40 | + (typeof bid.params['adpos'] === 'undefined' || |
| 41 | + typeof bid.params['adpos'] === 'number') && |
| 42 | + (typeof bid.params['protocols'] === 'undefined' || |
| 43 | + Array.isArray(bid.params['protocols'])) && |
| 44 | + (typeof bid.params.instl === 'undefined' || |
| 45 | + bid.params.instl === 0 || |
| 46 | + bid.params.instl === 1) |
| 47 | + ); |
| 48 | + }, |
| 49 | + |
| 50 | + buildRequests: function (validBidRequests, bidderRequest) { |
| 51 | + return validBidRequests.map(bidRequest => { |
| 52 | + const { |
| 53 | + adUnitCode, |
| 54 | + auctionId, |
| 55 | + mediaTypes, |
| 56 | + params, |
| 57 | + sizes, |
| 58 | + transactionId |
| 59 | + } = bidRequest; |
| 60 | + const baseEndpoint = 'https://bidder.cleanmediaads.com'; |
| 61 | + const rtbEndpoint = |
| 62 | + `${baseEndpoint}/r/${ |
| 63 | + params.supplyPartnerId |
| 64 | + }/bidr?rformat=open_rtb&reqformat=rtb_json&bidder=prebid` + |
| 65 | + (params.query ? '&' + params.query : ''); |
| 66 | + let url = |
| 67 | + config.getConfig('pageUrl') || bidderRequest.refererInfo.referer; |
| 68 | + |
| 69 | + const rtbBidRequest = { |
| 70 | + id: auctionId, |
| 71 | + site: { |
| 72 | + domain: helper.getTopWindowDomain(url), |
| 73 | + page: url, |
| 74 | + ref: bidderRequest.refererInfo.referer |
| 75 | + }, |
| 76 | + device: { |
| 77 | + ua: navigator.userAgent, |
| 78 | + dnt: utils.getDNT() ? 1 : 0, |
| 79 | + h: screen.height, |
| 80 | + w: screen.width, |
| 81 | + language: navigator.language |
| 82 | + }, |
| 83 | + imp: [], |
| 84 | + ext: {}, |
| 85 | + user: { |
| 86 | + ext: {} |
| 87 | + } |
| 88 | + }; |
| 89 | + |
| 90 | + if ( |
| 91 | + bidderRequest.gdprConsent && |
| 92 | + bidderRequest.gdprConsent.consentString && |
| 93 | + bidderRequest.gdprConsent.gdprApplies |
| 94 | + ) { |
| 95 | + rtbBidRequest.ext.gdpr_consent = { |
| 96 | + consent_string: bidderRequest.gdprConsent.consentString, |
| 97 | + consent_required: bidderRequest.gdprConsent.gdprApplies |
| 98 | + }; |
| 99 | + rtbBidRequest.regs = { |
| 100 | + ext: { |
| 101 | + gdpr: bidderRequest.gdprConsent.gdprApplies === true ? 1 : 0 |
| 102 | + } |
| 103 | + }; |
| 104 | + rtbBidRequest.user = { |
| 105 | + ext: { |
| 106 | + consent: bidderRequest.gdprConsent.consentString |
| 107 | + } |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | + const imp = { |
| 112 | + id: transactionId, |
| 113 | + instl: params.instl === 1 ? 1 : 0, |
| 114 | + tagid: adUnitCode, |
| 115 | + bidfloor: params.bidfloor || 0, |
| 116 | + bidfloorcur: 'USD', |
| 117 | + secure: 1 |
| 118 | + }; |
| 119 | + |
| 120 | + const hasFavoredMediaType = |
| 121 | + params.favoredMediaType && |
| 122 | + this.supportedMediaTypes.includes(params.favoredMediaType); |
| 123 | + |
| 124 | + if (!mediaTypes || mediaTypes.banner) { |
| 125 | + if (!hasFavoredMediaType || params.favoredMediaType === BANNER) { |
| 126 | + const bannerImp = Object.assign({}, imp, { |
| 127 | + banner: { |
| 128 | + w: sizes.length ? sizes[0][0] : 300, |
| 129 | + h: sizes.length ? sizes[0][1] : 250, |
| 130 | + pos: params.pos || 0, |
| 131 | + topframe: utils.inIframe() ? 0 : 1 |
| 132 | + } |
| 133 | + }); |
| 134 | + rtbBidRequest.imp.push(bannerImp); |
| 135 | + } |
| 136 | + } |
| 137 | + |
| 138 | + if (mediaTypes && mediaTypes.video) { |
| 139 | + if (!hasFavoredMediaType || params.favoredMediaType === VIDEO) { |
| 140 | + let videoImp = { |
| 141 | + video: { |
| 142 | + protocols: params.protocols || [1, 2, 3, 4, 5, 6], |
| 143 | + pos: params.pos || 0, |
| 144 | + ext: {context: mediaTypes.video.context} |
| 145 | + } |
| 146 | + }; |
| 147 | + |
| 148 | + let playerSize = mediaTypes.video.playerSize || sizes; |
| 149 | + if (utils.isArray(playerSize[0])) { |
| 150 | + videoImp.video.w = playerSize[0][0]; |
| 151 | + videoImp.video.h = playerSize[0][1]; |
| 152 | + } else if (utils.isNumber(playerSize[0])) { |
| 153 | + videoImp.video.w = playerSize[0]; |
| 154 | + videoImp.video.h = playerSize[1]; |
| 155 | + } else { |
| 156 | + videoImp.video.w = 300; |
| 157 | + videoImp.video.h = 250; |
| 158 | + } |
| 159 | + |
| 160 | + videoImp = Object.assign({}, imp, videoImp); |
| 161 | + rtbBidRequest.imp.push(videoImp); |
| 162 | + } |
| 163 | + } |
| 164 | + |
| 165 | + if (rtbBidRequest.imp.length === 0) { |
| 166 | + return; |
| 167 | + } |
| 168 | + |
| 169 | + return { |
| 170 | + method: 'POST', |
| 171 | + url: rtbEndpoint, |
| 172 | + data: rtbBidRequest, |
| 173 | + bidRequest |
| 174 | + }; |
| 175 | + }); |
| 176 | + }, |
| 177 | + |
| 178 | + interpretResponse: function (serverResponse, bidRequest) { |
| 179 | + const response = serverResponse && serverResponse.body; |
| 180 | + if (!response) { |
| 181 | + utils.logError('empty response'); |
| 182 | + return []; |
| 183 | + } |
| 184 | + |
| 185 | + const bids = response.seatbid.reduce( |
| 186 | + (acc, seatBid) => acc.concat(seatBid.bid), |
| 187 | + [] |
| 188 | + ); |
| 189 | + let outBids = []; |
| 190 | + |
| 191 | + bids.forEach(bid => { |
| 192 | + const outBid = { |
| 193 | + requestId: bidRequest.bidRequest.bidId, |
| 194 | + cpm: bid.price, |
| 195 | + width: bid.w, |
| 196 | + height: bid.h, |
| 197 | + ttl: 360, |
| 198 | + creativeId: bid.crid || bid.adid, |
| 199 | + netRevenue: true, |
| 200 | + currency: bid.cur || response.cur, |
| 201 | + mediaType: helper.getMediaType(bid) |
| 202 | + }; |
| 203 | + |
| 204 | + if ( |
| 205 | + utils.deepAccess( |
| 206 | + bidRequest.bidRequest, |
| 207 | + 'mediaTypes.' + outBid.mediaType |
| 208 | + ) |
| 209 | + ) { |
| 210 | + if (outBid.mediaType === BANNER) { |
| 211 | + outBids.push(Object.assign({}, outBid, {ad: bid.adm})); |
| 212 | + } else if (outBid.mediaType === VIDEO) { |
| 213 | + const context = utils.deepAccess( |
| 214 | + bidRequest.bidRequest, |
| 215 | + 'mediaTypes.video.context' |
| 216 | + ); |
| 217 | + outBids.push( |
| 218 | + Object.assign({}, outBid, { |
| 219 | + vastUrl: bid.ext.vast_url, |
| 220 | + vastXml: bid.adm, |
| 221 | + renderer: |
| 222 | + context === 'outstream' |
| 223 | + ? newRenderer(bidRequest.bidRequest, bid) |
| 224 | + : undefined |
| 225 | + }) |
| 226 | + ); |
| 227 | + } |
| 228 | + } |
| 229 | + }); |
| 230 | + return outBids; |
| 231 | + }, |
| 232 | + |
| 233 | + getUserSyncs: function (syncOptions, serverResponses, gdprConsent) { |
| 234 | + const syncs = []; |
| 235 | + const gdprApplies = |
| 236 | + gdprConsent && typeof gdprConsent.gdprApplies === 'boolean' |
| 237 | + ? gdprConsent.gdprApplies |
| 238 | + : false; |
| 239 | + const suffix = gdprApplies |
| 240 | + ? 'gc=' + encodeURIComponent(gdprConsent.consentString) |
| 241 | + : 'gc=missing'; |
| 242 | + serverResponses.forEach(resp => { |
| 243 | + if (resp.body) { |
| 244 | + const bidResponse = resp.body; |
| 245 | + if (bidResponse.ext && Array.isArray(bidResponse.ext['utrk'])) { |
| 246 | + bidResponse.ext['utrk'].forEach(pixel => { |
| 247 | + const url = |
| 248 | + pixel.url + |
| 249 | + (pixel.url.indexOf('?') > 0 ? '&' + suffix : '?' + suffix); |
| 250 | + return syncs.push({type: pixel.type, url}); |
| 251 | + }); |
| 252 | + } |
| 253 | + if (Array.isArray(bidResponse.seatbid)) { |
| 254 | + bidResponse.seatbid.forEach(seatBid => { |
| 255 | + if (Array.isArray(seatBid.bid)) { |
| 256 | + seatBid.bid.forEach(bid => { |
| 257 | + if (bid.ext && Array.isArray(bid.ext['utrk'])) { |
| 258 | + bid.ext['utrk'].forEach(pixel => { |
| 259 | + const url = |
| 260 | + pixel.url + |
| 261 | + (pixel.url.indexOf('?') > 0 |
| 262 | + ? '&' + suffix |
| 263 | + : '?' + suffix); |
| 264 | + return syncs.push({type: pixel.type, url}); |
| 265 | + }); |
| 266 | + } |
| 267 | + }); |
| 268 | + } |
| 269 | + }); |
| 270 | + } |
| 271 | + } |
| 272 | + }); |
| 273 | + return syncs; |
| 274 | + } |
| 275 | +}; |
| 276 | + |
| 277 | +function newRenderer(bidRequest, bid, rendererOptions = {}) { |
| 278 | + const renderer = Renderer.install({ |
| 279 | + url: |
| 280 | + (bidRequest.params && bidRequest.params.rendererUrl) || |
| 281 | + (bid.ext && bid.ext.renderer_url) || |
| 282 | + 'https://s.wlplayer.com/video/latest/renderer.js', |
| 283 | + config: rendererOptions, |
| 284 | + loaded: false |
| 285 | + }); |
| 286 | + try { |
| 287 | + renderer.setRender(renderOutstream); |
| 288 | + } catch (err) { |
| 289 | + utils.logWarn('Prebid Error calling setRender on renderer', err); |
| 290 | + } |
| 291 | + return renderer; |
| 292 | +} |
| 293 | + |
| 294 | +function renderOutstream(bid) { |
| 295 | + bid.renderer.push(() => { |
| 296 | + const unitId = bid.adUnitCode + '/' + bid.adId; |
| 297 | + window['GamoshiPlayer'].renderAd({ |
| 298 | + id: unitId, |
| 299 | + debug: window.location.href.indexOf('pbjsDebug') >= 0, |
| 300 | + placement: document.getElementById(bid.adUnitCode), |
| 301 | + width: bid.width, |
| 302 | + height: bid.height, |
| 303 | + events: { |
| 304 | + ALL_ADS_COMPLETED: () => |
| 305 | + window.setTimeout(() => { |
| 306 | + window['GamoshiPlayer'].removeAd(unitId); |
| 307 | + }, 300) |
| 308 | + }, |
| 309 | + vastUrl: bid.vastUrl, |
| 310 | + vastXml: bid.vastXml |
| 311 | + }); |
| 312 | + }); |
| 313 | +} |
| 314 | + |
| 315 | +registerBidder(spec); |
0 commit comments