├── .nvmrc ├── integrationExamples └── gpt │ ├── .gitignore │ ├── amp │ ├── gulpfile.js │ └── creative.html │ ├── creative_rendering.html │ └── pbjs_video_adUnit.html ├── .babelrc ├── test ├── helpers │ ├── pbjs-test-only.js │ └── karma-init.js ├── fixtures │ ├── config.json │ ├── ad-server-targeting.json │ ├── googletag-slots.json │ ├── targeting-map.json │ ├── allAdapters.js │ └── cpmInputsOutputs.json └── spec │ ├── e2e │ ├── common │ │ ├── globals.js │ │ └── utils.js │ ├── testcase1 │ │ ├── pbjsapi-group │ │ │ ├── getbidresponses_spec.js │ │ │ └── adservertargeting_spec.js │ │ └── dom-group │ │ │ └── dom_spec.js │ ├── custom-reporter │ │ ├── junit.xml.ejs │ │ └── pbjs-html-reporter.js │ ├── custom-assertions │ │ └── first.js │ └── gpt-examples │ │ └── e2e_default.html │ ├── ga_spec.js │ ├── aliasBidder_spec.js │ ├── unit │ └── adapters │ │ └── analytics │ │ └── appnexus_spec.js │ ├── integration │ ├── faker │ │ ├── fixtures.js │ │ └── googletag.js │ └── bug-825-truncate-bidsRequested_spec.js │ ├── cpmBucketManager_spec.js │ ├── adloader_spec.js │ ├── url_spec.js │ ├── loaders │ ├── adapterLoader_spec.js │ └── getAdapters_spec.js │ ├── api_spec.js │ ├── adapters │ ├── getintent_spec.js │ ├── rhythmone_spec.js │ ├── roxot_spec.js │ └── underdogmedia_spec.js │ └── sizeMapping_spec.js ├── .travis.yml ├── src ├── adapters │ ├── analytics │ │ ├── appnexus.js │ │ ├── pulsepoint.js │ │ ├── example.js │ │ ├── roxot.js │ │ ├── example2.js │ │ ├── libraries │ │ │ ├── example.js │ │ │ └── example2.js │ │ └── AnalyticsAdapter.js │ ├── baseAdapter.js │ ├── adapter.js │ ├── districtmDMX.js │ ├── getintent.js │ ├── komoona.js │ ├── jcm.js │ ├── vertoz.js │ ├── underdogmedia.js │ ├── kruxlink.js │ ├── pulsepoint.js │ ├── adequant.js │ ├── roxot.js │ ├── widespace.js │ ├── admedia.js │ ├── centro.js │ ├── springserve.js │ ├── fidelity.js │ ├── openx.js │ ├── gumgum.js │ ├── triplelift.js │ ├── adblade.js │ ├── authenticated.js │ ├── memeglobal.js │ └── sonobi.js ├── video.js ├── prebidGlobal.js ├── bidfactory.js ├── url.js ├── constants.json ├── adserver.js ├── sizeMapping.js ├── polyfill.js ├── ajax.js ├── cpmBucketManager.js ├── adloader.js └── events.js ├── .jscsrc ├── nightwatch.conf.js ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .jshintrc ├── CONTRIBUTING.md ├── loaders ├── getAdapters.js └── analyticsLoader.js ├── nightwatch.json ├── adapters.json ├── nightwatch.browserstack.json ├── .gitignore ├── webpack.conf.js ├── package.json ├── karma.conf.js └── gulpHelpers.js /.nvmrc: -------------------------------------------------------------------------------- 1 | 5.1 2 | -------------------------------------------------------------------------------- /integrationExamples/gpt/.gitignore: -------------------------------------------------------------------------------- 1 | pbjs_adblade_example_gpt.html 2 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": ["transform-object-assign", "transform-es3-property-literals", "transform-es3-member-expression-literals"] 4 | } 5 | -------------------------------------------------------------------------------- /test/helpers/pbjs-test-only.js: -------------------------------------------------------------------------------- 1 | export const pbjsTestOnly = { 2 | 3 | getAdUnits() { 4 | return $$PREBID_GLOBAL$$.adUnits; 5 | }, 6 | 7 | clearAllAdUnits() { 8 | $$PREBID_GLOBAL$$.adUnits = []; 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /test/helpers/karma-init.js: -------------------------------------------------------------------------------- 1 | (function (window) { 2 | if (!window.parent.pbjsKarmaInitDone && window.location.pathname === '/context.html') { 3 | window.parent.pbjsKarmaInitDone = true; 4 | window.open('/debug.html', '_blank'); 5 | } 6 | })(window); 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "5.1" 5 | 6 | before_install: 7 | - npm install -g gulp 8 | - export CHROME_BIN=chromium-browser 9 | - export DISPLAY=:99.0 10 | - sh -e /etc/init.d/xvfb start 11 | 12 | script: 13 | - gulp run-tests 14 | -------------------------------------------------------------------------------- /test/fixtures/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "adUnitElementIDs": [ 3 | "div-test-ad-0", 4 | "div-test-ad-1", 5 | "div-test-ad-2" 6 | ], 7 | "adUnitCodes": [ 8 | "/19968336/header-bid-tag-0", 9 | "/123456/header-bid-tag-1", 10 | "/123456/header-bid-tag-2" 11 | ] 12 | } -------------------------------------------------------------------------------- /test/spec/e2e/common/globals.js: -------------------------------------------------------------------------------- 1 | var HtmlReporter = require('nightwatch-html-reporter'); 2 | var reporter = new HtmlReporter({ 3 | openBrowser: true, 4 | reportsDirectory: __dirname + '/reports', 5 | themeName: 'cover', 6 | }); 7 | module.exports = { 8 | reporter: reporter.fn 9 | }; 10 | -------------------------------------------------------------------------------- /src/adapters/analytics/appnexus.js: -------------------------------------------------------------------------------- 1 | /** 2 | * appnexus.js - AppNexus Prebid Analytics Adapter 3 | */ 4 | 5 | import adapter from 'AnalyticsAdapter'; 6 | 7 | export default adapter({ 8 | global: 'AppNexusPrebidAnalytics', 9 | handler: 'on', 10 | analyticsType: 'bundle' 11 | }); 12 | 13 | -------------------------------------------------------------------------------- /src/adapters/analytics/pulsepoint.js: -------------------------------------------------------------------------------- 1 | /** 2 | * pulsepoint.js - Analytics Adapter for PulsePoint 3 | */ 4 | 5 | import adapter from 'AnalyticsAdapter'; 6 | 7 | export default adapter({ 8 | global: 'PulsePointPrebidAnalytics', 9 | handler: 'on', 10 | analyticsType: 'bundle' 11 | }); 12 | 13 | -------------------------------------------------------------------------------- /test/fixtures/ad-server-targeting.json: -------------------------------------------------------------------------------- 1 | { 2 | "/9968336/header-bid-tag-0": { 3 | "hb_bidder": "rubicon", 4 | "hb_adid": "13f44b0d3c", 5 | "hb_pb": "1.50" 6 | }, 7 | "/9968336/header-bid-tag1": { 8 | "hb_bidder": "openx", 9 | "hb_adid": "147ac541a", 10 | "hb_pb": "1.00" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/fixtures/googletag-slots.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "getSlotElementId": "function(){ return \"div-gpt-ad-1438287399331-0\"; }" 4 | }, 5 | { 6 | "getSlotElementId": "function(){ return \"div-gpt-ad-1438287399331-1\"; }" 7 | }, 8 | { 9 | "getSlotElementId": "function(){ return \"div-gpt-ad-1438287399331-2\"; }" 10 | } 11 | ] -------------------------------------------------------------------------------- /src/adapters/baseAdapter.js: -------------------------------------------------------------------------------- 1 | export class BaseAdapter { 2 | constructor(code) { 3 | this.code = code; 4 | } 5 | 6 | getCode() { 7 | return this.code; 8 | } 9 | 10 | setCode(code) { 11 | this.code = code; 12 | } 13 | 14 | callBids() { 15 | throw 'adapter implementation must override callBids method'; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "maxErrors": 1000, 3 | "requireTrailingComma": null, 4 | "requireCamelCaseOrUpperCaseIdentifiers": null, 5 | "requireSpacesInAnonymousFunctionExpression": null, 6 | "validateIndentation": 2, 7 | "disallowSpacesInFunctionDeclaration": { 8 | "beforeOpeningRoundBrace": true 9 | }, 10 | "disallowNewlineBeforeBlockStatements": true 11 | } 12 | -------------------------------------------------------------------------------- /src/video.js: -------------------------------------------------------------------------------- 1 | import { videoAdapters } from './adaptermanager'; 2 | 3 | /** 4 | * Helper functions for working with video-enabled adUnits 5 | */ 6 | export const videoAdUnit = adUnit => adUnit.mediaType === 'video'; 7 | const nonVideoBidder = bid => !videoAdapters.includes(bid.bidder); 8 | export const hasNonVideoBidder = adUnit => adUnit.bids.filter(nonVideoBidder).length; 9 | -------------------------------------------------------------------------------- /src/adapters/analytics/example.js: -------------------------------------------------------------------------------- 1 | /** 2 | * example.js - analytics adapter for Example Analytics Library example 3 | */ 4 | 5 | import adapter from 'AnalyticsAdapter'; 6 | 7 | export default adapter( 8 | { 9 | url: 'http://localhost:9999/src/adapters/analytics/libraries/example.js', 10 | global: 'ExampleAnalyticsGlobalObject', 11 | handler: 'on', 12 | analyticsType: 'library' 13 | } 14 | ); 15 | -------------------------------------------------------------------------------- /src/prebidGlobal.js: -------------------------------------------------------------------------------- 1 | // if $$PREBID_GLOBAL$$ already exists in global document scope, use it, if not, create the object 2 | // global defination should happen BEFORE imports to avoid global undefined errors. 3 | window.$$PREBID_GLOBAL$$ = (window.$$PREBID_GLOBAL$$ || {}); 4 | window.$$PREBID_GLOBAL$$.que = window.$$PREBID_GLOBAL$$.que || []; 5 | 6 | export function getGlobal() { 7 | return window.$$PREBID_GLOBAL$$; 8 | } 9 | -------------------------------------------------------------------------------- /integrationExamples/gpt/amp/gulpfile.js: -------------------------------------------------------------------------------- 1 | /** Run `gulp serve` to serve files from this directory in development 2 | * Set two different entries in hosts to use x-domain iframes 3 | * AMP requires https 4 | */ 5 | 6 | var gulp = require('gulp'); 7 | var connect = require('gulp-connect'); 8 | var port = 5000; 9 | 10 | gulp.task('serve', function() { 11 | connect.server({ 12 | port: port, 13 | root: './', 14 | livereload: true, 15 | https: true 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /test/fixtures/targeting-map.json: -------------------------------------------------------------------------------- 1 | { 2 | "/19968336/header-bid-tag-0": [ 3 | { 4 | "hb_bidder": [ 5 | "appnexus" 6 | ] 7 | }, 8 | { 9 | "hb_adid": [ 10 | "233bcbee889d46d" 11 | ] 12 | }, 13 | { 14 | "hb_pb": [ 15 | "10.00" 16 | ] 17 | }, 18 | { 19 | "hb_size": [ 20 | "300x250" 21 | ] 22 | }, 23 | { 24 | "foobar": [ 25 | "300x250" 26 | ] 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /test/spec/e2e/common/utils.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | findIframeInDiv : function(divid) { 3 | var div = document.getElementById(divid); 4 | var iframes = div.getElementsByTagName('iframe'); 5 | console.log(iframes.length); 6 | try { 7 | if(iframes.length === 1 && iframes[0].contentWindow.document.body.innerHTML === "") { 8 | return false; 9 | } else { 10 | return true; 11 | } 12 | } catch (e) { 13 | return true; 14 | } 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /src/adapters/adapter.js: -------------------------------------------------------------------------------- 1 | function Adapter(code) { 2 | var bidderCode = code; 3 | 4 | function setBidderCode(code) { 5 | bidderCode = code; 6 | } 7 | 8 | function getBidderCode() { 9 | return bidderCode; 10 | } 11 | 12 | function callBids() { 13 | } 14 | 15 | return { 16 | callBids: callBids, 17 | setBidderCode: setBidderCode, 18 | getBidderCode: getBidderCode 19 | }; 20 | } 21 | 22 | exports.createNew = function (bidderCode) { 23 | return new Adapter(bidderCode); 24 | }; 25 | -------------------------------------------------------------------------------- /test/spec/ga_spec.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var ga = require('../../src/adapters/analytics/ga'); 3 | 4 | describe('Ga', function () { 5 | 6 | describe('enableAnalytics', function () { 7 | 8 | it('should accept a tracker name option and output prefixed send string', function () { 9 | var config = { options: { trackerName: 'foo' } }; 10 | ga.enableAnalytics(config); 11 | 12 | var output = ga.getTrackerSend(); 13 | assert.equal(output, 'foo.send'); 14 | }); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/adapters/analytics/roxot.js: -------------------------------------------------------------------------------- 1 | import { ajax } from 'src/ajax'; 2 | import adapter from 'AnalyticsAdapter'; 3 | 4 | const utils = require('../../utils'); 5 | 6 | const url = '//d.rxthdr.com/analytics'; 7 | const analyticsType = 'endpoint'; 8 | 9 | export default utils.extend(adapter( 10 | { 11 | url, 12 | analyticsType 13 | } 14 | ), 15 | { 16 | track({ eventType, args }) { 17 | ajax(url, (result) => utils.logInfo('Event ' + eventType + ' sent to roxot analytics with result ' + result), JSON.stringify({ eventType, args })); 18 | } 19 | }); 20 | -------------------------------------------------------------------------------- /integrationExamples/gpt/creative_rendering.html: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 21 | -------------------------------------------------------------------------------- /nightwatch.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = (function(settings) { 2 | var browsers = require('./browsers.json'); 3 | for(var browser in browsers) { 4 | var desiredCapabilities = { 5 | "browserName": browsers[browser].browser, 6 | "version": browsers[browser].browser_version, 7 | "platform": browsers[browser].os, 8 | "os": browsers[browser].os, 9 | "os_version": browsers[browser].os_version, 10 | "browser": browsers[browser].browser, 11 | "browser_version": browsers[browser].browser_version, 12 | }; 13 | 14 | settings.test_settings[browser] = {} 15 | settings.test_settings[browser]['desiredCapabilities'] = desiredCapabilities; 16 | } 17 | return settings; 18 | 19 | })(require('./nightwatch.browserstack.json')); 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Type of issue 2 | 3 | 4 | ## Description 5 | 6 | 7 | ## Steps to reproduce 8 | 13 | 14 | ### Expected results 15 | 16 | ### Actual results 17 | 18 | ## Platform details 19 | 20 | 21 | ## Other information 22 | 23 | -------------------------------------------------------------------------------- /src/adapters/analytics/example2.js: -------------------------------------------------------------------------------- 1 | import { ajax } from 'src/ajax'; 2 | 3 | /** 4 | * example2.js - analytics adapter for Example2 Analytics Endpoint example 5 | */ 6 | 7 | import adapter from 'AnalyticsAdapter'; 8 | const utils = require('../../utils'); 9 | 10 | const url = 'https://httpbin.org/post'; 11 | const analyticsType = 'endpoint'; 12 | 13 | export default utils.extend(adapter( 14 | { 15 | url, 16 | analyticsType 17 | } 18 | ), 19 | { 20 | // Override AnalyticsAdapter functions by supplying custom methods 21 | track({ eventType, args }) { 22 | console.log('track function override for Example2 Analytics'); 23 | ajax(url, (result) => console.log('Analytics Endpoint Example2: result = ' + result), JSON.stringify({ eventType, args })); 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": false, 3 | "browser": true, 4 | "curly": false, 5 | "devel": true, 6 | "eqeqeq": true, 7 | "freeze": true, 8 | "immed": true, 9 | "maxdepth": 5, 10 | "newcap": true, 11 | "noarg": true, 12 | "node": true, 13 | "notypeof": true, 14 | "esnext": true, 15 | "trailing": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": false, 19 | "scripturl": true, 20 | "globals": { 21 | "before": true, 22 | "after": true, 23 | "exports": true, 24 | "$$PREBID_GLOBAL$$": true, 25 | "pbjsTestOnly": true, 26 | "assert": false, 27 | "expect": false, 28 | "dump": false, 29 | "describe": false, 30 | "it": false, 31 | "xit": false, 32 | "pkg": false, 33 | "sinon": false, 34 | "beforeEach": false, 35 | "afterEach": false, 36 | "JSON": true 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Prebid.js 2 | Thank you for taking the time to contribute to Prebid.js! 3 | 4 | ## Pull Requests 5 | Please make sure that pull requests are scoped to one change, and that any added or changed code includes tests with greater than 80% code coverage. See [Testing Prebid.js](http://prebid.org/dev-docs/testing-prebid.html) for help on writing tests. 6 | 7 | ## Issues 8 | [prebid.org](http://prebid.org/) contains documentation that may help answer questions you have about using Prebid.js. If you can't find the answer there, try searching for a similar issue on the [issues page](https://github.com/prebid/Prebid.js/issues). If you don't find an answer there, [open a new issue](https://github.com/prebid/Prebid.js/issues/new). 9 | 10 | ## Documentation 11 | If you have a documentation issue or pull request, please open a ticket or PR in the [documentation repository](https://github.com/prebid/prebid.github.io). 12 | -------------------------------------------------------------------------------- /loaders/getAdapters.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const argv = require('yargs').argv; 6 | 7 | const defaultAdapters = 'adapters.json'; 8 | 9 | function load(file) { 10 | try { 11 | const buffer = fs.readFileSync(file); 12 | return JSON.parse(buffer.toString()); 13 | } catch (e) { 14 | return []; 15 | } 16 | } 17 | 18 | module.exports = function getAdapters() { 19 | let customAdapters = argv.adapters; 20 | 21 | if (!customAdapters) { 22 | return load(defaultAdapters); 23 | } 24 | 25 | customAdapters = path.resolve(process.cwd(), customAdapters); 26 | 27 | try { 28 | fs.statSync(customAdapters); 29 | return load(customAdapters); 30 | } catch (e) { 31 | console.log(`Prebid Warning: custom adapters config cannot be loaded from ${customAdapters}, ` + 32 | 'using default adapters.json'); 33 | return load(defaultAdapters); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /test/spec/aliasBidder_spec.js: -------------------------------------------------------------------------------- 1 | import { pbjsTestOnly } from 'test/helpers/pbjs-test-only'; 2 | 3 | describe('Publisher API _ Alias Bidder', function () { 4 | var assert = require('chai').assert; 5 | var expect = require('chai').expect; 6 | var should = require('chai').should(); 7 | var prebid = require('../../src/prebid'); 8 | 9 | before(function () { 10 | 11 | var topSlotCode = '/19968336/header-bid-tag1'; 12 | var topSlotSizes = [[728, 90], [970, 90]]; 13 | var adUnit = { 14 | code: topSlotCode, 15 | sizes: topSlotSizes, 16 | bids: [ 17 | { 18 | bidder: 'appnexus', 19 | params: { 20 | placementId: '5215561' 21 | } 22 | } 23 | ] 24 | }; 25 | 26 | $$PREBID_GLOBAL$$.addAdUnits(adUnit); 27 | }); 28 | 29 | after(function () { 30 | pbjsTestOnly.clearAllAdUnits(); 31 | }); 32 | 33 | describe('set Alias Bidder', function () { 34 | 35 | it('should have both of target bidder and alias bidder', function () { 36 | 37 | $$PREBID_GLOBAL$$.aliasBidder('appnexus', 'bRealTime1'); 38 | 39 | }); 40 | }); 41 | 42 | }); 43 | -------------------------------------------------------------------------------- /test/spec/unit/adapters/analytics/appnexus_spec.js: -------------------------------------------------------------------------------- 1 | import appnexusAnalytics from 'src/adapters/analytics/appnexus'; 2 | import { assert } from 'chai'; 3 | import { getBidRequestedPayload } from 'test/fixtures/fixtures'; 4 | 5 | const spyEnqueue = sinon.spy(appnexusAnalytics, 'enqueue'); 6 | const spyTrack = sinon.spy(appnexusAnalytics, 'track'); 7 | 8 | const bidRequestedPayload = getBidRequestedPayload(); 9 | 10 | // describe(` 11 | // FEATURE: AppNexus Prebid Analytics Adapter (APA) 12 | // STORY: As a publisher I use APA to collect data for auction events\n`, ()=> { 13 | // describe(`SCENARIO: Bids are received from bidder 14 | // GIVEN: A publisher page requests bids 15 | // WHEN: The bidRequested event fires`, () => { 16 | // appnexusAnalytics.enqueue('bidRequested', bidRequestedPayload); 17 | // it(`THEN: APA enqueue is called with event payload 18 | // AND: APA track does not get called`, () => { 19 | // assert.ok(spyEnqueue.calledWith('bidRequested')); 20 | // assert.deepEqual(spyEnqueue.args[0][1], bidRequestedPayload); 21 | // assert.ok(!spyTrack.called); 22 | // }); 23 | // }); 24 | // }); 25 | -------------------------------------------------------------------------------- /nightwatch.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_folders": ["./test/spec/e2e"], 3 | "output_folder": "./build/coverage/e2e/reports", 4 | "custom_commands_path" : "", 5 | "custom_assertions_path" : "./test/spec/e2e/custom-assertions", 6 | "page_objects_path" : "", 7 | "globals_path" : "", 8 | "end_session_on_fail" : true, 9 | "skip_testcases_on_fail" : false, 10 | 11 | "selenium" : { 12 | "start_process" : true, 13 | "server_path" : "${SELENIUM_JAR_PATH}", 14 | "log_path" : "", 15 | "host" : "127.0.0.1", 16 | "port" : 4444, 17 | "cli_args" : { 18 | "webdriver.ie.driver" : "" 19 | } 20 | }, 21 | 22 | "test_settings" : { 23 | "default" : { 24 | "launch_url" : "http://localhost", 25 | "selenium_port" : 4444, 26 | "selenium_host" : "localhost", 27 | "silent": true, 28 | "exclude":["custom-assertions","custom-commands","common","custom-reporter"], 29 | "screenshots" : { 30 | "enabled" : false, 31 | "path" : "" 32 | }, 33 | "desiredCapabilities": { 34 | "browserName": "firefox", 35 | "javascriptEnabled": true, 36 | "acceptSslCerts": true 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/spec/integration/faker/fixtures.js: -------------------------------------------------------------------------------- 1 | import faker from 'faker'; 2 | import { makeSlot } from './googletag'; 3 | 4 | export function makeAdSlot(overrides = {}) { 5 | return Object.assign(makeSlot( 6 | { 7 | code: overrides.code, 8 | divId: overrides.divId 9 | }), overrides); 10 | } 11 | 12 | export function makeAdUnit(overrides = {}) { 13 | return Object.assign({ 14 | code: `ad-unit-code-${randomFive()}`, 15 | sizes: [[300, 250], [300, 600]], 16 | bids: [] 17 | }, overrides); 18 | } 19 | 20 | export function makeBidder(overrides = {}) { 21 | let adapter; 22 | adapter = Object.assign({ 23 | bidder: `${faker.company.bsBuzz()}Media`, 24 | params: { 25 | abc: faker.random.alphaNumeric(10), 26 | xyz: faker.random.number({ max: 10, precision: 2 }) 27 | }, 28 | callBids: sinon.spy() 29 | }, overrides); 30 | 31 | return adapter; 32 | } 33 | 34 | export function makeRequest(overrides = {}) { 35 | return Object.assign({ 36 | adUnits: overrides.adUnits, 37 | bidsBackHandler: sinon.spy(), 38 | timeout: 2000 39 | }, overrides); 40 | } 41 | 42 | export function randomFive() { return faker.random.number({ min: 10000, max: 99999 }); } -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | ## Type of change 6 | 7 | - [ ] Bugfix 8 | - [ ] Feature 9 | - [ ] New bidder adapter 10 | - [ ] Code style update (formatting, local variables) 11 | - [ ] Refactoring (no functional changes, no api changes) 12 | - [ ] Build related changes 13 | - [ ] CI related changes 14 | - [ ] Other 15 | 16 | ## Description of change 17 | 18 | 19 | 20 | - test parameters for validating bids 21 | ``` 22 | { 23 | bidder: '', 24 | params: { 25 | // ... 26 | } 27 | } 28 | ``` 29 | - contact email of the adapter’s maintainer 30 | - [ ] official adapter submission 31 | 32 | 33 | ## Other information 34 | 35 | -------------------------------------------------------------------------------- /src/bidfactory.js: -------------------------------------------------------------------------------- 1 | var utils = require('./utils.js'); 2 | 3 | /** 4 | Required paramaters 5 | bidderCode, 6 | height, 7 | width, 8 | statusCode 9 | Optional paramaters 10 | adId, 11 | cpm, 12 | ad, 13 | adUrl, 14 | dealId, 15 | priceKeyString; 16 | */ 17 | function Bid(statusCode, bidRequest) { 18 | var _bidId = bidRequest && bidRequest.bidId || utils.getUniqueIdentifierStr(); 19 | var _statusCode = statusCode || 0; 20 | 21 | this.bidderCode = ''; 22 | this.width = 0; 23 | this.height = 0; 24 | this.statusMessage = _getStatus(); 25 | this.adId = _bidId; 26 | 27 | function _getStatus() { 28 | switch (_statusCode) { 29 | case 0: 30 | return 'Pending'; 31 | case 1: 32 | return 'Bid available'; 33 | case 2: 34 | return 'Bid returned empty or error response'; 35 | case 3: 36 | return 'Bid timed out'; 37 | } 38 | } 39 | 40 | this.getStatusCode = function () { 41 | return _statusCode; 42 | }; 43 | 44 | //returns the size of the bid creative. Concatenation of width and height by ‘x’. 45 | this.getSize = function () { 46 | return this.width + 'x' + this.height; 47 | }; 48 | 49 | } 50 | 51 | // Bid factory function. 52 | exports.createBid = function () { 53 | return new Bid(...arguments); 54 | }; 55 | -------------------------------------------------------------------------------- /integrationExamples/gpt/amp/creative.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 39 | -------------------------------------------------------------------------------- /test/spec/e2e/testcase1/pbjsapi-group/getbidresponses_spec.js: -------------------------------------------------------------------------------- 1 | //var assert = require('assert'); 2 | var assert = require('chai').assert; 3 | var utils = require('util'); 4 | 5 | module.exports = { 6 | 'bidReceived not empty' : function(browser) { 7 | browser 8 | .url('http://localhost:9999/test/spec/e2e/gpt-examples/gpt_default.html') 9 | .waitForElementVisible('body', 3000) 10 | .pause(5000) 11 | .execute(function() { 12 | return window.pbjs._bidsReceived.length; 13 | }, [], function(result) { 14 | //browser.assert.first(false, 'Bid response empty'); 15 | assert.isOk(result.value, 'Bid response empty'); 16 | }); 17 | }, 18 | 'check keys' : function(browser) { 19 | browser 20 | .execute(function() { 21 | return window.pbjs._bidsReceived; 22 | }, [], function(result) { 23 | //minimum expected keys in bid received 24 | var expected = ["bidderCode", "width", "height", "adId", "cpm", "requestId", "bidder", "adUnitCode", "timeToRespond"]; 25 | Object.keys(result.value).forEach(function(key){ 26 | var compare = Object.keys(result.value[key]); 27 | assert.includeMembers(compare, expected, 'include members'); 28 | }); 29 | }); 30 | }, 31 | after : function(browser) { 32 | browser.end(); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /adapters.json: -------------------------------------------------------------------------------- 1 | [ 2 | "aardvark", 3 | "adblade", 4 | "adbutler", 5 | "adequant", 6 | "adform", 7 | "adkernel", 8 | "admedia", 9 | "aol", 10 | "appnexus", 11 | "appnexusAst", 12 | "conversant", 13 | "districtmDMX", 14 | "fidelity", 15 | "getintent", 16 | "gumgum", 17 | "hiromedia", 18 | "indexExchange", 19 | "kruxlink", 20 | "komoona", 21 | "openx", 22 | "piximedia", 23 | "pubmatic", 24 | "pulsepoint", 25 | "rhythmone", 26 | "rubicon", 27 | "sonobi", 28 | "sovrn", 29 | "springserve", 30 | "triplelift", 31 | "yieldbot", 32 | "nginad", 33 | "brightcom", 34 | "wideorbit", 35 | "jcm", 36 | "underdogmedia", 37 | "memeglobal", 38 | "centro", 39 | "roxot", 40 | "vertoz", 41 | "widespace", 42 | { 43 | "appnexus": { 44 | "alias": "brealtime" 45 | } 46 | }, 47 | { 48 | "appnexus": { 49 | "alias": "pagescience" 50 | } 51 | }, 52 | { 53 | "appnexus": { 54 | "alias": "defymedia" 55 | } 56 | }, 57 | { 58 | "appnexusAst": { 59 | "supportedMediaTypes": ["video"] 60 | } 61 | }, 62 | { 63 | "rubicon": { 64 | "alias": "rubiconLite" 65 | } 66 | }, 67 | { 68 | "appnexus": { 69 | "alias": "featureforward" 70 | } 71 | } 72 | 73 | ] 74 | -------------------------------------------------------------------------------- /nightwatch.browserstack.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_folders": ["./test/spec/e2e"], 3 | "output_folder": "./build/coverage/e2e/reports", 4 | "custom_commands_path": "", 5 | "custom_assertions_path": "", 6 | "page_objects_path": "", 7 | "globals_path": "", 8 | 9 | "selenium" : { 10 | "start_process" : false, 11 | "host" : "hub.browserstack.com", 12 | "port" : 80 13 | }, 14 | 15 | "test_settings": { 16 | "default": { 17 | "launch_url" : "http://hub.browserstack.com", 18 | "selenium_port" : 80, 19 | "selenium_host" : "hub.browserstack.com", 20 | "silent": true, 21 | "exclude":["custom-assertions","custom-commands","common","custom-reporter"], 22 | "screenshots" : { 23 | "enabled" : false, 24 | "path" : "" 25 | }, 26 | "desiredCapabilities": { 27 | "browserName": "chrome", 28 | "browser_version" : "51.0", 29 | "platform" : "WINDOWS", 30 | 31 | "javascriptEnabled": true, 32 | "acceptSslCerts": true, 33 | 34 | "os" : "WINDOWS", 35 | "os_version" : "7", 36 | "browser" : "Chrome", 37 | "browser_version" : "51.0", 38 | "browserstack.local": true, 39 | "browserstack.debug": true, 40 | "browserstack.selenium_version" : "2.53.0", 41 | "browserstack.user": "${BROWSERSTACK_USERNAME}", 42 | "browserstack.key": "${BROWSERSTACK_KEY}" 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/url.js: -------------------------------------------------------------------------------- 1 | export function parseQS(query) { 2 | return !query ? {} : query 3 | .replace(/^\?/, '') 4 | .split('&') 5 | .reduce((acc, criteria) => { 6 | let [k, v] = criteria.split('='); 7 | if (/\[\]$/.test(k)) { 8 | k = k.replace('[]', ''); 9 | acc[k] = acc[k] || []; 10 | acc[k].push(v); 11 | } else { 12 | acc[k] = v || ''; 13 | } 14 | return acc; 15 | }, {}); 16 | } 17 | 18 | export function formatQS(query) { 19 | return Object 20 | .keys(query) 21 | .map(k => Array.isArray(query[k]) ? 22 | query[k].map(v => `${k}[]=${v}`).join('&') : 23 | `${k}=${query[k]}`) 24 | .join('&'); 25 | } 26 | 27 | export function parse(url) { 28 | let parsed = document.createElement('a'); 29 | parsed.href = decodeURIComponent(url); 30 | return { 31 | protocol: (parsed.protocol || '').replace(/:$/, ''), 32 | hostname: parsed.hostname, 33 | port: +parsed.port, 34 | pathname: parsed.pathname, 35 | search: parseQS(parsed.search || ''), 36 | hash: (parsed.hash || '').replace(/^#/, ''), 37 | host: parsed.host 38 | }; 39 | } 40 | 41 | export function format(obj) { 42 | return (obj.protocol || 'http') + '://' + 43 | (obj.host || 44 | obj.hostname + (obj.port ? `:${obj.port}` : '')) + 45 | (obj.pathname || '') + 46 | (obj.search ? `?${formatQS(obj.search || '')}` : '') + 47 | (obj.hash ? `#${obj.hash}` : ''); 48 | } 49 | -------------------------------------------------------------------------------- /test/spec/integration/faker/googletag.js: -------------------------------------------------------------------------------- 1 | import faker from 'faker'; 2 | import { randomFive } from './fixtures'; 3 | 4 | var Slot = function Slot({ code, divId }) { 5 | code = code || `ad-slot-code-${randomFive()}`; 6 | divId = divId || `div-id-${randomFive()}`; 7 | 8 | var slot = { 9 | targeting: [], 10 | getSlotElementId: function getSlotElementId() { 11 | return divId; 12 | }, 13 | 14 | getAdUnitPath: function getAdUnitPath() { 15 | return code; 16 | }, 17 | 18 | setTargeting: function setTargeting(key, value) { 19 | var obj = []; 20 | obj[key] = value; 21 | this.targeting.push(obj); 22 | }, 23 | 24 | getTargeting: function getTargeting() { 25 | return this.targeting; 26 | }, 27 | 28 | getTargetingKeys: function getTargetingKeys() { 29 | return []; 30 | }, 31 | 32 | clearTargeting: function clearTargeting() { 33 | return window.googletag.pubads().getSlots(); 34 | } 35 | }; 36 | slot.spySetTargeting = sinon.spy(slot, 'setTargeting'); 37 | return slot; 38 | }; 39 | 40 | export function makeSlot() { 41 | const slot = new Slot(...arguments); 42 | window.googletag._slots.push(slot); 43 | return slot; 44 | } 45 | 46 | window.googletag = { 47 | _slots: [], 48 | pubads: function () { 49 | var self = this; 50 | return { 51 | getSlots: function () { 52 | return self._slots; 53 | }, 54 | 55 | setSlots: function (slots) { 56 | self._slots = slots; 57 | } 58 | }; 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /src/constants.json: -------------------------------------------------------------------------------- 1 | { 2 | "JSON_MAPPING": { 3 | "PL_CODE": "code", 4 | "PL_SIZE": "sizes", 5 | "PL_BIDS": "bids", 6 | "BD_BIDDER": "bidder", 7 | "BD_ID": "paramsd", 8 | "BD_PL_ID": "placementId", 9 | "ADSERVER_TARGETING": "adserverTargeting", 10 | "BD_SETTING_STANDARD": "standard" 11 | }, 12 | "REPO_AND_VERSION": "%%REPO_AND_VERSION%%", 13 | "DEBUG_MODE": "pbjs_debug", 14 | "STATUS": { 15 | "GOOD": 1, 16 | "NO_BID": 2 17 | }, 18 | "CB": { 19 | "TYPE": { 20 | "ALL_BIDS_BACK": "allRequestedBidsBack", 21 | "AD_UNIT_BIDS_BACK": "adUnitBidsBack", 22 | "BID_WON": "bidWon" 23 | } 24 | }, 25 | "objectType_function": "function", 26 | "objectType_undefined": "undefined", 27 | "objectType_object": "object", 28 | "objectType_string": "string", 29 | "objectType_number": "number", 30 | "EVENTS": { 31 | "AUCTION_INIT": "auctionInit", 32 | "AUCTION_END": "auctionEnd", 33 | "BID_ADJUSTMENT": "bidAdjustment", 34 | "BID_TIMEOUT": "bidTimeout", 35 | "BID_REQUESTED": "bidRequested", 36 | "BID_RESPONSE": "bidResponse", 37 | "BID_WON": "bidWon" 38 | }, 39 | "EVENT_ID_PATHS": { 40 | "bidWon": "adUnitCode" 41 | }, 42 | "ORDER": { 43 | "RANDOM": "random" 44 | }, 45 | "GRANULARITY_OPTIONS": { 46 | "LOW": "low", 47 | "MEDIUM": "medium", 48 | "HIGH": "high", 49 | "AUTO": "auto", 50 | "DENSE": "dense", 51 | "CUSTOM": "custom" 52 | }, 53 | "TARGETING_KEYS": [ 54 | "hb_bidder", 55 | "hb_adid", 56 | "hb_pb", 57 | "hb_size" 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built Files 2 | node_modules/ 3 | build 4 | 5 | # Test Files 6 | test/app 7 | gpt.html 8 | gpt-each-bidder3.html 9 | 10 | # Selenium files 11 | bin 12 | selenium*.log 13 | 14 | # Dev File 15 | integrationExamples/gpt/gpt.html 16 | integrationExamples/gpt/*-test.html 17 | integrationExamples/implementations/ 18 | src/adapters/analytics/libraries 19 | 20 | # Coverage reports 21 | build/coverage/ 22 | 23 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion 24 | 25 | *.iml 26 | 27 | ## Directory-based project format: 28 | .idea/ 29 | # if you remove the above rule, at least ignore the following: 30 | 31 | # User-specific stuff: 32 | # .idea/workspace.xml 33 | # .idea/tasks.xml 34 | # .idea/dictionaries 35 | 36 | # Sensitive or high-churn files: 37 | # .idea/dataSources.ids 38 | # .idea/dataSources.xml 39 | # .idea/sqlDataSources.xml 40 | # .idea/dynamic.xml 41 | # .idea/uiDesigner.xml 42 | 43 | # Gradle: 44 | # .idea/gradle.xml 45 | # .idea/libraries 46 | 47 | # Mongo Explorer plugin: 48 | # .idea/mongoSettings.xml 49 | 50 | ## File-based project format: 51 | *.ipr 52 | *.iws 53 | 54 | ## Plugin-specific files: 55 | 56 | # IntelliJ 57 | /out/ 58 | 59 | # mpeltonen/sbt-idea plugin 60 | .idea_modules/ 61 | 62 | # JIRA plugin 63 | atlassian-ide-plugin.xml 64 | 65 | # Crashlytics plugin (for Android Studio and IntelliJ) 66 | com_crashlytics_export_strings.xml 67 | crashlytics.properties 68 | crashlytics-build.properties 69 | *.swp 70 | *.swo 71 | 72 | #zip files (for releases) 73 | *.zip 74 | 75 | # TypeScript typings 76 | typings/ 77 | 78 | # MacOS system files 79 | .DS_Store 80 | -------------------------------------------------------------------------------- /loaders/analyticsLoader.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const blockLoader = require('block-loader'); 5 | let analyticsAdapters = require('../package.json').analytics; 6 | 7 | var options = { 8 | start: '/** INSERT ANALYTICS - DO NOT EDIT OR REMOVE */', 9 | end: '/** END INSERT ANALYTICS */', 10 | process: function insertAnalytics() { 11 | // read directory for analytics adapter file names, map the file names to String.replace, 12 | // use a regex to remove file extensions, then return the array of adapter names 13 | const files = fs.readdirSync('src/adapters/analytics') 14 | .map(file => file.replace(/\.[^/.]+$/, '')); 15 | 16 | let adapters = analyticsAdapters.map(adapter => adapter.length ? adapter : Object.keys(adapter)[0]); 17 | 18 | let inserts = adapters.filter(adapter => { 19 | if (files.includes(adapter)) { 20 | return adapter; 21 | } else { 22 | console.log(`Prebid Warning: no adapter found for ${adapter}, continuing.`); 23 | } 24 | }); 25 | 26 | // if no matching adapters and no adapter files found, exit 27 | if (!inserts || !inserts.length) { 28 | return null; 29 | } 30 | 31 | // return the javascript strings to insert into adaptermanager.js 32 | return inserts.map((adapter) => { 33 | return `var ${adapter} = require('./adapters/analytics/${adapter}.js').default 34 | || require('./adapters/analytics/${adapter}.js'); 35 | exports.registerAnalyticsAdapter({ adapter: ${adapter}, code: '${adapter}' });\n`; 36 | }).join(''); 37 | } 38 | }; 39 | 40 | module.exports = blockLoader(options); 41 | -------------------------------------------------------------------------------- /test/spec/e2e/testcase1/dom-group/dom_spec.js: -------------------------------------------------------------------------------- 1 | //var assert = require('assert'); 2 | 3 | module.exports = { 4 | 5 | 'Test rendering ad div-2' : function (browser) { 6 | 7 | var checkAdRendering2 = function() { 8 | var div = document.getElementById('div-2'); 9 | var iframes = div.getElementsByTagName('iframe'); 10 | try { 11 | if(iframes.length == 1 && iframes[0].contentWindow.document.body.innerHTML == "") { 12 | return false; 13 | } else { 14 | return true; 15 | } 16 | } catch (e) { 17 | return true; 18 | } 19 | } 20 | 21 | browser 22 | .url('http://localhost:9999/test/spec/e2e/gpt-examples/e2e_default.html') 23 | .waitForElementVisible('body', 3000) 24 | .pause(5000) 25 | .execute(checkAdRendering2, [], function(result) { 26 | this.assert.equal(result.value, true, 'Ad of div-2 not rendered'); 27 | }); 28 | }, 29 | 'Test rendering ad div-1' : function (browser) { 30 | 31 | var checkAdRendering = function() { 32 | var div = document.getElementById('div-1'); 33 | var iframes = div.getElementsByTagName('iframe'); 34 | try { 35 | if(iframes.length == 1 && iframes[0].contentWindow.document.body.innerHTML == "") { 36 | return false; 37 | } else { 38 | return true; 39 | } 40 | } catch (e) { 41 | return true; 42 | } 43 | } 44 | 45 | browser 46 | .execute(checkAdRendering, [], function(result) { 47 | this.assert.equal(result.value, true, 'Ad of div-1 not rendered'); 48 | }); 49 | }, 50 | after : function(browser) { 51 | browser.end(); 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /src/adapters/districtmDMX.js: -------------------------------------------------------------------------------- 1 | var bidfactory = require('../bidfactory.js'); 2 | var bidmanager = require('../bidmanager.js'); 3 | var adLoader = require('../adloader'); 4 | 5 | var DistrictmAdaptor = function districtmAdaptor(){ 6 | let districtmUrl = window.location.protocol + '//prebid.districtm.ca/lib.js'; 7 | this.callBids = params =>{ 8 | if(!window.hb_dmx_res){ 9 | adLoader.loadScript(districtmUrl,()=>{ 10 | this.sendBids(params); 11 | }); 12 | }else{ 13 | this.sendBids(params); 14 | } 15 | return params; 16 | }; 17 | 18 | 19 | this.handlerRes = function(response, bidObject){ 20 | let bid; 21 | if(parseFloat(response.result.cpm) > 0){ 22 | bid = bidfactory.createBid(1); 23 | bid.bidderCode = bidObject.bidder; 24 | bid.cpm = response.result.cpm; 25 | bid.width = response.result.width; 26 | bid.height = response.result.height; 27 | bid.ad = response.result.banner; 28 | bidmanager.addBidResponse(bidObject.placementCode, bid); 29 | }else{ 30 | bid = bidfactory.createBid(2); 31 | bid.bidderCode = bidObject.bidder; 32 | bidmanager.addBidResponse(bidObject.placementCode, bid); 33 | } 34 | 35 | return bid; 36 | }; 37 | 38 | 39 | this.sendBids = function(params){ 40 | var bids = params.bids; 41 | for(var i = 0; i < bids.length; i++){ 42 | bids[i].params.sizes = window.hb_dmx_res.auction.fixSize(bids[i].sizes); 43 | } 44 | window.hb_dmx_res.auction.run(window.hb_dmx_res.ssp, bids, this.handlerRes); 45 | return bids; 46 | }; 47 | 48 | 49 | return { 50 | callBids: this.callBids, 51 | sendBids: this.sendBids, 52 | handlerRes: this.handlerRes 53 | }; 54 | }; 55 | 56 | module.exports = DistrictmAdaptor; 57 | -------------------------------------------------------------------------------- /src/adserver.js: -------------------------------------------------------------------------------- 1 | import {formatQS} from './url'; 2 | 3 | //Adserver parent class 4 | const AdServer = function(attr) { 5 | this.name = attr.adserver; 6 | this.code = attr.code; 7 | this.getWinningBidByCode = function() { 8 | var bidObject = $$PREBID_GLOBAL$$._bidsReceived.find(bid => bid.adUnitCode === this.code); 9 | return bidObject; 10 | }; 11 | }; 12 | 13 | //DFP ad server 14 | exports.dfpAdserver = function (options, urlComponents) { 15 | var adserver = new AdServer(options); 16 | adserver.urlComponents = urlComponents; 17 | 18 | var dfpReqParams = { 19 | 'env' : 'vp', 20 | 'gdfp_req' : '1', 21 | 'impl' : 's', 22 | 'unviewed_position_start' : '1' 23 | }; 24 | 25 | var dfpParamsWithVariableValue = ['output', 'iu', 'sz', 'url', 'correlator', 'description_url', 'hl']; 26 | 27 | var getCustomParams = function(targeting) { 28 | return encodeURIComponent(formatQS(targeting)); 29 | }; 30 | 31 | adserver.appendQueryParams = function() { 32 | var bid = adserver.getWinningBidByCode(); 33 | this.urlComponents.search.description_url = encodeURIComponent(bid.vastUrl); 34 | this.urlComponents.search.cust_params = getCustomParams(bid.adserverTargeting); 35 | this.urlComponents.correlator = Date.now(); 36 | }; 37 | 38 | adserver.verifyAdserverTag = function() { 39 | for(var key in dfpReqParams) { 40 | if(!this.urlComponents.search.hasOwnProperty(key) || this.urlComponents.search[key] !== dfpReqParams[key]) { 41 | return false; 42 | } 43 | } 44 | for(var i in dfpParamsWithVariableValue) { 45 | if(!this.urlComponents.search.hasOwnProperty(dfpParamsWithVariableValue[i])) { 46 | return false; 47 | } 48 | } 49 | return true; 50 | }; 51 | 52 | return adserver; 53 | }; 54 | -------------------------------------------------------------------------------- /src/sizeMapping.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module sizeMapping 3 | */ 4 | import * as utils from './utils'; 5 | let _win; 6 | 7 | function mapSizes(adUnit) { 8 | if(!isSizeMappingValid(adUnit.sizeMapping)){ 9 | return adUnit.sizes; 10 | } 11 | const width = getScreenWidth(); 12 | if(!width) { 13 | //size not detected - get largest value set for desktop 14 | const mapping = adUnit.sizeMapping.reduce((prev, curr) => { 15 | return prev.minWidth < curr.minWidth ? curr : prev; 16 | }); 17 | if(mapping.sizes) { 18 | return mapping.sizes; 19 | } 20 | return adUnit.sizes; 21 | } 22 | let sizes = ''; 23 | const mapping = adUnit.sizeMapping.find(sizeMapping =>{ 24 | return width > sizeMapping.minWidth; 25 | }); 26 | if(mapping && mapping.sizes){ 27 | sizes = mapping.sizes; 28 | utils.logMessage(`AdUnit : ${adUnit.code} resized based on device width to : ${sizes}`); 29 | } 30 | else{ 31 | utils.logMessage(`AdUnit : ${adUnit.code} not mapped to any sizes for device width. This request will be suppressed.`); 32 | } 33 | return sizes; 34 | 35 | } 36 | 37 | function isSizeMappingValid(sizeMapping) { 38 | if(utils.isArray(sizeMapping) && sizeMapping.length > 0){ 39 | return true; 40 | } 41 | utils.logInfo('No size mapping defined'); 42 | return false; 43 | } 44 | 45 | function getScreenWidth(win) { 46 | var w = win || _win || window; 47 | var d = w.document; 48 | 49 | if (w.innerWidth) { 50 | return w.innerWidth; 51 | } 52 | else if (d.body.clientWidth) { 53 | return d.body.clientWidth; 54 | } 55 | else if (d.documentElement.clientWidth) { 56 | return d.documentElement.clientWidth; 57 | } 58 | return 0; 59 | } 60 | 61 | function setWindow(win) { 62 | _win = win; 63 | } 64 | 65 | export { mapSizes, getScreenWidth, setWindow }; 66 | -------------------------------------------------------------------------------- /test/spec/e2e/custom-reporter/junit.xml.ejs: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | <% for (var item in module.completed) { 10 | var testcase = module.completed[item]; 11 | var assertions = testcase.assertions %> 12 | <% 13 | for (var i = 0; i < assertions.length; i++) { %><% if (assertions[i].failure) { %> <%= assertions[i].stackTrace %><% } %> 14 | <% if (assertions[i].screenshots && assertions[i].screenshots.length > 0) { %><% for (var j = 0; j < assertions[i].screenshots.length; j++) { %>[[ATTACHMENT|<%= assertions[i].screenshots[j] %>]]<% } %><% } %> 15 | <% } 16 | if (testcase.failed > 0 && testcase.stackTrace) { 17 | %><%= testcase.stackTrace %><% } %> 18 | <% if (systemerr != '') {%> 19 | 20 | <%= systemerr %> 21 | <% } %> 22 | <% } %> 23 | <% if (module.skipped && (module.skipped.length > 0)) { %> 24 | <% for (var j = 0; j < module.skipped.length; j++) { %> 25 | 27 | 28 | 29 | <% } %> 30 | <% } %> 31 | 32 | 33 | -------------------------------------------------------------------------------- /test/spec/cpmBucketManager_spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import {getPriceBucketString, isValidePriceConfig} from 'src/cpmBucketManager'; 3 | let cpmFixtures = require('test/fixtures/cpmInputsOutputs.json'); 4 | 5 | describe('cpmBucketManager', () => { 6 | 7 | it('getPriceBucketString function generates the correct price strings', () => { 8 | let input = cpmFixtures.cpmInputs; 9 | for(let i = 0; i < input.length; i++){ 10 | let output = getPriceBucketString(input[i]); 11 | let jsonOutput = JSON.stringify(output); 12 | expect(jsonOutput).to.deep.equal(JSON.stringify(cpmFixtures.priceStringOutputs[i])); 13 | } 14 | }); 15 | 16 | it('gets the correct custom bucket strings', () => { 17 | let cpm = 16.50908; 18 | let customConfig = { 19 | "buckets" : [{ 20 | "precision" : 4, 21 | "min" : 0, 22 | "max" : 3, 23 | "increment" : 0.01, 24 | }, 25 | { 26 | "precision" : 4, 27 | "min" : 3, 28 | "max" : 18, 29 | "increment" : 0.05, 30 | "cap" : true 31 | } 32 | ] 33 | }; 34 | let expected = '{"low":"5.00","med":"16.50","high":"16.50","auto":"16.50","dense":"16.50","custom":"16.5000"}'; 35 | let output = getPriceBucketString(cpm, customConfig); 36 | expect(JSON.stringify(output)).to.deep.equal(expected); 37 | }); 38 | 39 | it('checks whether custom config is valid', () => { 40 | let badConfig = { 41 | "buckets" : [{ 42 | "min" : 0, 43 | "max" : 3, 44 | "increment" : 0.01, 45 | }, 46 | { 47 | //missing min prop 48 | "max" : 18, 49 | "increment" : 0.05, 50 | "cap" : true 51 | } 52 | ] 53 | }; 54 | 55 | expect(isValidePriceConfig(badConfig)).to.be.false; 56 | }); 57 | 58 | }); 59 | -------------------------------------------------------------------------------- /test/spec/e2e/custom-assertions/first.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if the given attribute of an element has the expected value. 3 | * 4 | * ``` 5 | * this.demoTest = function (client) { 6 | * browser.assert.attributeEquals('body', 'data-attr', 'some value'); 7 | * }; 8 | * ``` 9 | * 10 | * @method attributeEquals 11 | * @param {string} selector The selector (CSS / Xpath) used to locate the element. 12 | * @param {string} attribute The attribute name 13 | * @param {string} expected The expected value of the attribute to check. 14 | * @param {string} [message] Optional log message to display in the output. If missing, one is displayed by default. 15 | * @api assertions 16 | */ 17 | 18 | var util = require('util'); 19 | exports.assertion = function(expected, msg) { 20 | var DEFAULT_MSG = 'Testing if attribute %s of <%s> equals "%s".'; 21 | 22 | this.message = msg; 23 | 24 | this.expected = function() { 25 | return expected; 26 | }; 27 | 28 | this.pass = function(value) { 29 | return value === expected; 30 | }; 31 | 32 | this.failure = function(result) { 33 | var failed = false; 34 | return failed; 35 | }; 36 | 37 | this.value = function(result) { 38 | console.log('**********'); 39 | console.log(result); 40 | return result.value; 41 | }; 42 | 43 | this.command = function(callback) { 44 | var _this = this; 45 | var execcallback = function(result) { 46 | //console.log(_this); 47 | console.log('**********'); 48 | console.log(result); 49 | console.log(callback.toString()); 50 | if (callback) { 51 | return callback.call(_this, result.value); 52 | } 53 | }; 54 | 55 | this.api.execute(function(){ 56 | //cusotm logic 57 | return 'hello'; 58 | }, [], execcallback); 59 | 60 | //var result = {'value':'hello'}; 61 | 62 | return this; 63 | }; 64 | 65 | }; 66 | -------------------------------------------------------------------------------- /test/spec/adloader_spec.js: -------------------------------------------------------------------------------- 1 | describe('adLoader', function () { 2 | var assert = require('chai').assert, 3 | adLoader = require('../../src/adloader'); 4 | 5 | describe('trackPixel', function () { 6 | it('correctly appends a cachebuster query paramter to a pixel with no existing parameters', function () { 7 | var inputUrl = 'http://www.example.com/tracking_pixel', 8 | token = '?rnd=', 9 | expectedPartialUrl = inputUrl + token, 10 | actual = adLoader.trackPixel(inputUrl), 11 | actualPartialUrl = actual.split(token)[0] + token, 12 | randomNumber = parseInt(actual.split(token)[1]); 13 | assert.strictEqual(actualPartialUrl, expectedPartialUrl); 14 | assert.isNumber(randomNumber); 15 | }); 16 | }); 17 | 18 | it('correctly appends a cachebuster query paramter to a pixel with one existing parameter', function () { 19 | var inputUrl = 'http://www.example.com/tracking_pixel?food=bard', 20 | token = '&rnd=', 21 | expectedPartialUrl = inputUrl + token, 22 | actual = adLoader.trackPixel(inputUrl), 23 | actualPartialUrl = actual.split(token)[0] + token, 24 | randomNumber = parseInt(actual.split(token)[1]); 25 | assert.strictEqual(actualPartialUrl, expectedPartialUrl); 26 | assert.isNumber(randomNumber); 27 | }); 28 | 29 | it('correctly appends a cachebuster query paramter to a pixel with multiple existing parameters', function () { 30 | var inputUrl = 'http://www.example.com/tracking_pixel?food=bard&zing=zang', 31 | token = '&rnd=', 32 | expectedPartialUrl = inputUrl + token, 33 | actual = adLoader.trackPixel(inputUrl), 34 | actualPartialUrl = actual.split(token)[0] + token, 35 | randomNumber = parseInt(actual.split(token)[1]); 36 | assert.strictEqual(actualPartialUrl, expectedPartialUrl); 37 | assert.isNumber(randomNumber); 38 | }); 39 | 40 | }); 41 | -------------------------------------------------------------------------------- /test/spec/url_spec.js: -------------------------------------------------------------------------------- 1 | import {format, parse} from '../../src/url'; 2 | import { expect } from 'chai'; 3 | 4 | describe('helpers.url', () => { 5 | 6 | describe('parse()', () => { 7 | 8 | let parsed; 9 | 10 | beforeEach(() => { 11 | parsed = parse('http://example.com:3000/pathname/?search=test&foo=bar#hash'); 12 | }); 13 | 14 | it('extracts the protocol', () => { 15 | expect(parsed).to.have.property('protocol', 'http'); 16 | }); 17 | 18 | it('extracts the hostname', () => { 19 | expect(parsed).to.have.property('hostname', 'example.com'); 20 | }); 21 | 22 | it('extracts the port', () => { 23 | expect(parsed).to.have.property('port', 3000); 24 | }); 25 | 26 | it('extracts the pathname', () => { 27 | expect(['/pathname/', 'pathname/']).to.include(parsed.pathname); 28 | }); 29 | 30 | it('extracts the search query', () => { 31 | expect(parsed).to.have.property('search'); 32 | expect(parsed.search).to.eql({ 33 | foo: 'bar', 34 | search: 'test' 35 | }); 36 | }); 37 | 38 | it('extracts the hash', () => { 39 | expect(parsed).to.have.property('hash', 'hash'); 40 | }); 41 | 42 | it('extracts the host', () => { 43 | expect(parsed).to.have.property('host', 'example.com:3000'); 44 | }); 45 | 46 | }); 47 | 48 | describe('format()', () => { 49 | 50 | it('formats an object in to a URL', () => { 51 | expect(format({ 52 | protocol: 'http', 53 | hostname: 'example.com', 54 | port: 3000, 55 | pathname: '/pathname/', 56 | search: {foo: 'bar', search: 'test'}, 57 | hash: 'hash' 58 | })).to.equal('http://example.com:3000/pathname/?foo=bar&search=test#hash'); 59 | }); 60 | 61 | it('will use defaults for missing properties', () => { 62 | expect(format({ 63 | hostname: 'example.com' 64 | })).to.equal('http://example.com'); 65 | }); 66 | 67 | }); 68 | 69 | }); 70 | -------------------------------------------------------------------------------- /src/polyfill.js: -------------------------------------------------------------------------------- 1 | /** @module polyfill 2 | Misc polyfills 3 | */ 4 | /*jshint -W121 */ 5 | if (!Array.prototype.find) { 6 | Object.defineProperty(Array.prototype, "find", { 7 | value: function(predicate) { 8 | if (this === null) { 9 | throw new TypeError('Array.prototype.find called on null or undefined'); 10 | } 11 | if (typeof predicate !== 'function') { 12 | throw new TypeError('predicate must be a function'); 13 | } 14 | var list = Object(this); 15 | var length = list.length >>> 0; 16 | var thisArg = arguments[1]; 17 | var value; 18 | 19 | for (var i = 0; i < length; i++) { 20 | value = list[i]; 21 | if (predicate.call(thisArg, value, i, list)) { 22 | return value; 23 | } 24 | } 25 | return undefined; 26 | } 27 | }); 28 | } 29 | 30 | if (!Array.prototype.includes) { 31 | Object.defineProperty(Array.prototype, "includes", { 32 | value: function(searchElement) { 33 | var O = Object(this); 34 | var len = parseInt(O.length, 10) || 0; 35 | if (len === 0) { 36 | return false; 37 | } 38 | var n = parseInt(arguments[1], 10) || 0; 39 | var k; 40 | if (n >= 0) { 41 | k = n; 42 | } else { 43 | k = len + n; 44 | if (k < 0) {k = 0;} 45 | } 46 | var currentElement; 47 | while (k < len) { 48 | currentElement = O[k]; 49 | if (searchElement === currentElement || 50 | (searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN 51 | return true; 52 | } 53 | k++; 54 | } 55 | return false; 56 | } 57 | }); 58 | } 59 | 60 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger 61 | Number.isInteger = Number.isInteger || function(value) { 62 | return typeof value === 'number' && 63 | isFinite(value) && 64 | Math.floor(value) === value; 65 | }; 66 | -------------------------------------------------------------------------------- /src/adapters/getintent.js: -------------------------------------------------------------------------------- 1 | /*jshint loopfunc: true */ 2 | 3 | var bidfactory = require('../bidfactory.js'); 4 | var bidmanager = require('../bidmanager.js'); 5 | var adloader = require('../adloader.js'); 6 | 7 | var GetIntentAdapter = function GetIntentAdapter() { 8 | var headerBiddingStaticJS = window.location.protocol + '//cdn.adhigh.net/adserver/hb.js'; 9 | 10 | function _callBids(params) { 11 | if (typeof window.gi_hb === 'undefined') { 12 | adloader.loadScript(headerBiddingStaticJS, function() { 13 | bid(params); 14 | }, true); 15 | } else { 16 | bid(params); 17 | } 18 | } 19 | 20 | function addOptional(params, request, props) { 21 | for (var i = 0; i < props.length; i++) { 22 | if (params.hasOwnProperty(props[i])) { 23 | request[props[i]] = params[props[i]]; 24 | } 25 | } 26 | } 27 | 28 | function bid(params) { 29 | var bids = params.bids || []; 30 | for (var i = 0; i < bids.length; i++) { 31 | var bidRequest = bids[i]; 32 | var request = { 33 | pid: bidRequest.params.pid, // required 34 | tid: bidRequest.params.tid, // required 35 | known: bidRequest.params.known || 1, 36 | size: bidRequest.sizes[0].join("x"), 37 | }; 38 | addOptional(bidRequest.params, request, ['cur', 'floor']); 39 | window.gi_hb.makeBid(request, function(bidResponse) { 40 | if (bidResponse.no_bid === 1) { 41 | var nobid = bidfactory.createBid(2); 42 | nobid.bidderCode = bidRequest.bidder; 43 | bidmanager.addBidResponse(bidRequest.placementCode, nobid); 44 | } else { 45 | var size = bidResponse.size.split('x'); 46 | var bid = bidfactory.createBid(1); 47 | bid.bidderCode = bidRequest.bidder; 48 | bid.cpm = bidResponse.cpm; 49 | bid.ad = bidResponse.ad; 50 | bid.width = size[0]; 51 | bid.height = size[1]; 52 | bidmanager.addBidResponse(bidRequest.placementCode, bid); 53 | } 54 | }); 55 | } 56 | } 57 | 58 | return { 59 | callBids: _callBids 60 | }; 61 | }; 62 | 63 | module.exports = GetIntentAdapter; 64 | -------------------------------------------------------------------------------- /webpack.conf.js: -------------------------------------------------------------------------------- 1 | var prebid = require('./package.json'); 2 | var StringReplacePlugin = require('string-replace-webpack-plugin'); 3 | var path = require('path'); 4 | 5 | module.exports = { 6 | output: { 7 | filename: 'prebid.js' 8 | }, 9 | devtool: 'source-map', 10 | resolve: { 11 | modulesDirectories: ['', 'node_modules', 'src'] 12 | }, 13 | resolveLoader: { 14 | modulesDirectories: ['loaders', 'node_modules'] 15 | }, 16 | module: { 17 | loaders: [ 18 | { 19 | test: /\.js$/, 20 | include: /(src|test)/, 21 | exclude: path.resolve(__dirname, 'node_modules'), 22 | loader: 'babel', // 'babel-loader' is also a legal name to reference 23 | query: { 24 | presets: ['es2015'] 25 | } 26 | }, 27 | { 28 | test: /\.json$/, 29 | loader: 'json' 30 | }, 31 | { 32 | test: /adaptermanager.js/, 33 | include: /(src)/, 34 | loader: 'analyticsLoader' 35 | }, 36 | { 37 | test: /adaptermanager.js/, 38 | include: /(src)/, 39 | loader: 'adapterLoader' 40 | }, 41 | { 42 | test: /constants.json$/, 43 | include: /(src)/, 44 | loader: StringReplacePlugin.replace({ 45 | replacements: [ 46 | { 47 | pattern: /%%REPO_AND_VERSION%%/g, 48 | replacement: function (match, p1, offset, string) { 49 | return `${prebid.repository.url.split('/')[3]}_prebid_${prebid.version}`; 50 | } 51 | } 52 | ] 53 | }) 54 | }, 55 | { 56 | test: /\.js$/, 57 | include: /(src|test|integrationExamples)/, 58 | loader: StringReplacePlugin.replace({ 59 | replacements: [ 60 | { 61 | pattern: /\$\$PREBID_GLOBAL\$\$/g, 62 | replacement: function (match, p1, offset, string) { 63 | return prebid.globalVarName; 64 | } 65 | } 66 | ] 67 | }) 68 | } 69 | ] 70 | }, 71 | plugins: [ 72 | new StringReplacePlugin() 73 | ] 74 | }; 75 | -------------------------------------------------------------------------------- /src/adapters/komoona.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils.js'); 2 | var bidfactory = require('../bidfactory.js'); 3 | var bidmanager = require('../bidmanager.js'); 4 | var adloader = require('../adloader'); 5 | var STATUS = require('../constants').STATUS; 6 | 7 | var KomoonaAdapter = function KomoonaAdapter() { 8 | var KOMOONA_BIDDER_NAME = 'komoona'; 9 | 10 | function _callBids(params) { 11 | var kbConf = { 12 | ts_as: new Date().getTime(), 13 | hb_placements: [], 14 | hb_placement_bidids: {}, 15 | kb_callback: _bid_arrived 16 | }; 17 | 18 | var bids = params.bids || []; 19 | if (!bids || !bids.length) { 20 | return; 21 | } 22 | 23 | bids.forEach((currentBid) => { 24 | kbConf.hdbdid = kbConf.hdbdid || currentBid.params.hbid; 25 | kbConf.encode_bid = kbConf.encode_bid || currentBid.params.encode_bid; 26 | kbConf.hb_placement_bidids[currentBid.params.placementId] = currentBid.bidId; 27 | kbConf.hb_placements.push(currentBid.params.placementId); 28 | }); 29 | 30 | var scriptUrl = `//s.komoona.com/kb/0.1/kmn_sa_kb_c.${kbConf.hdbdid}.js`; 31 | 32 | adloader.loadScript(scriptUrl, function() { 33 | /*global KmnKB */ 34 | if (typeof KmnKB === 'function') { 35 | KmnKB.start(kbConf); 36 | } 37 | }, true); 38 | } 39 | 40 | function _bid_arrived(bid) { 41 | var bidObj = utils.getBidRequest(bid.bidid); 42 | var bidStatus = bid.creative ? STATUS.GOOD : STATUS.NO_BID; 43 | var bidResponse = bidfactory.createBid(bidStatus, bidObj); 44 | bidResponse.bidderCode = KOMOONA_BIDDER_NAME; 45 | 46 | if (bidStatus === STATUS.GOOD) { 47 | bidResponse.ad = bid.creative; 48 | bidResponse.cpm = bid.cpm; 49 | bidResponse.width = parseInt(bid.width); 50 | bidResponse.height = parseInt(bid.height); 51 | } 52 | 53 | var placementCode = bidObj && bidObj.placementCode; 54 | bidmanager.addBidResponse(placementCode, bidResponse); 55 | } 56 | 57 | // Export the callBids function, so that prebid.js can execute this function 58 | // when the page asks to send out bid requests. 59 | return { 60 | callBids: _callBids, 61 | }; 62 | }; 63 | 64 | module.exports = KomoonaAdapter; 65 | -------------------------------------------------------------------------------- /src/adapters/analytics/libraries/example.js: -------------------------------------------------------------------------------- 1 | /** @module example */ 2 | 3 | window.ExampleAnalyticsGlobalObject = function(hander, type, data) { 4 | console.log(`call to Example Analytics library: example('${hander}', '${type}', ${JSON.stringify(data)})`); 5 | }; 6 | 7 | window[window.ExampleAnalyticsGlobalObject] = function() {}; 8 | 9 | //var utils = require('utils'); 10 | //var events = require('events'); 11 | //var pbjsHandlers = require('prebid-event-handlers'); 12 | var utils = { errorless: function(fn) { return fn; } }; 13 | 14 | var events = { init: function() { return arguments; } }; 15 | 16 | var pbjsHandlers = { 17 | onBidAdjustment: args => console.log('pbjsHandlers onBidAdjustment args:', args), 18 | onBidTimeout: args => console.log('pbjsHandlers bidTimeout args:', args), 19 | onBidRequested: args => console.log('pbjsHandlers bidRequested args:', args), 20 | onBidResponse: args => console.log('pbjsHandlers bidResponse args:', args), 21 | onBidWon: args => console.log('pbjsHandlers bidWon args:', args) 22 | }; 23 | 24 | // init 25 | var example = window[window.ExampleAnalyticsGlobalObject]; 26 | var bufferedQueries = example.q || []; 27 | 28 | events.init(); 29 | 30 | // overwrite example object and handle 'on' callbacks 31 | window[window.ExampleAnalyticsGlobalObject] = example = utils.errorless(function() { 32 | if (arguments[0] && arguments[0] === 'on') { 33 | var eventName = arguments[1] && arguments[1]; 34 | var args = arguments[2] && arguments[2]; 35 | if (eventName && args) { 36 | if (eventName === 'bidAdjustment') { 37 | pbjsHandlers.onBidAdjustment.apply(this, [args]); 38 | } 39 | if (eventName === 'bidTimeout') { 40 | pbjsHandlers.onBidTimeout.apply(this, [args]); 41 | } 42 | if (eventName === 'bidRequested') { 43 | pbjsHandlers.onBidRequested.apply(this, [args]); 44 | } 45 | if (eventName === 'bidResponse') { 46 | pbjsHandlers.onBidResponse.apply(this, [args]); 47 | } 48 | if (eventName === 'bidWon') { 49 | pbjsHandlers.onBidWon.apply(this, [args]); 50 | } 51 | } 52 | } 53 | }); 54 | 55 | // apply bufferedQueries 56 | bufferedQueries.forEach(function(args) { 57 | example.apply(this, args); 58 | }); 59 | -------------------------------------------------------------------------------- /src/adapters/analytics/libraries/example2.js: -------------------------------------------------------------------------------- 1 | /** @module example */ 2 | 3 | window.ExampleAnalyticsGlobalObject2 = function(hander, type, data) { 4 | console.log(`call to Example2 Analytics library: example2('${hander}', '${type}', ${JSON.stringify(data)})`); 5 | }; 6 | 7 | window[window.ExampleAnalyticsGlobalObject2] = function() {}; 8 | 9 | //var utils = require('utils'); 10 | //var events = require('events'); 11 | //var pbjsHandlers = require('prebid-event-handlers'); 12 | var utils = { errorless: function(fn) { return fn; } }; 13 | 14 | var events = { init: function() { return arguments; } }; 15 | 16 | var pbjsHandlers = { 17 | onBidAdjustment: args => console.log('pbjsHandlers onBidAdjustment args:', args), 18 | onBidTimeout: args => console.log('pbjsHandlers bidTimeout args:', args), 19 | onBidRequested: args => console.log('pbjsHandlers bidRequested args:', args), 20 | onBidResponse: args => console.log('pbjsHandlers bidResponse args:', args), 21 | onBidWon: args => console.log('pbjsHandlers bidWon args:', args) 22 | }; 23 | 24 | // init 25 | var example = window[window.ExampleAnalyticsGlobalObject2]; 26 | var bufferedQueries = example.q || []; 27 | 28 | events.init(); 29 | 30 | // overwrite example object and handle 'on' callbacks 31 | window[window.ExampleAnalyticsGlobalObject2] = example = utils.errorless(function() { 32 | if (arguments[0] && arguments[0] === 'on') { 33 | var eventName = arguments[1] && arguments[1]; 34 | var args = arguments[2] && arguments[2]; 35 | if (eventName && args) { 36 | if (eventName === 'bidAdjustment') { 37 | pbjsHandlers.onBidAdjustment.apply(this, [args]); 38 | } 39 | if (eventName === 'bidTimeout') { 40 | pbjsHandlers.onBidTimeout.apply(this, [args]); 41 | } 42 | if (eventName === 'bidRequested') { 43 | pbjsHandlers.onBidRequested.apply(this, [args]); 44 | } 45 | if (eventName === 'bidResponse') { 46 | pbjsHandlers.onBidResponse.apply(this, [args]); 47 | } 48 | if (eventName === 'bidWon') { 49 | pbjsHandlers.onBidWon.apply(this, [args]); 50 | } 51 | } 52 | } 53 | }); 54 | 55 | // apply bufferedQueries 56 | bufferedQueries.forEach(function(args) { 57 | example.apply(this, args); 58 | }); 59 | -------------------------------------------------------------------------------- /src/ajax.js: -------------------------------------------------------------------------------- 1 | import {parse as parseURL, format as formatURL} from './url'; 2 | 3 | var utils = require('./utils'); 4 | 5 | const XHR_DONE = 4; 6 | 7 | /** 8 | * Simple IE9+ and cross-browser ajax request function 9 | * Note: x-domain requests in IE9 do not support the use of cookies 10 | * 11 | * @param url string url 12 | * @param callback object callback 13 | * @param data mixed data 14 | * @param options object 15 | */ 16 | 17 | export function ajax(url, callback, data, options = {}) { 18 | try { 19 | let x; 20 | let useXDomainRequest = false; 21 | let method = options.method || (data ? 'POST' : 'GET'); 22 | 23 | if (!window.XMLHttpRequest) { 24 | useXDomainRequest = true; 25 | } else{ 26 | x = new window.XMLHttpRequest(); 27 | if (x.responseType === undefined) { 28 | useXDomainRequest = true; 29 | } 30 | } 31 | 32 | if (useXDomainRequest) { 33 | x = new window.XDomainRequest(); 34 | x.onload = function () { 35 | callback(x.responseText, x); 36 | }; 37 | 38 | // http://stackoverflow.com/questions/15786966/xdomainrequest-aborts-post-on-ie-9 39 | x.onerror = function () { 40 | utils.logMessage('xhr onerror'); 41 | }; 42 | x.ontimeout = function () { 43 | utils.logMessage('xhr timeout'); 44 | }; 45 | x.onprogress = function() { 46 | utils.logMessage('xhr onprogress'); 47 | }; 48 | } else { 49 | x.onreadystatechange = function () { 50 | if (x.readyState === XHR_DONE && callback) { 51 | callback(x.responseText, x); 52 | } 53 | }; 54 | } 55 | 56 | if (method === 'GET' && data) { 57 | let urlInfo = parseURL(url); 58 | Object.assign(urlInfo.search, data); 59 | url = formatURL(urlInfo); 60 | } 61 | 62 | x.open(method, url); 63 | 64 | if (!useXDomainRequest) { 65 | if (options.withCredentials) { 66 | x.withCredentials = true; 67 | } 68 | if (options.preflight) { 69 | x.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 70 | } 71 | x.setRequestHeader('Content-Type', options.contentType || 'text/plain'); 72 | } 73 | x.send(method === 'POST' && data); 74 | } catch (error) { 75 | utils.logError('xhr construction', error); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/adapters/jcm.js: -------------------------------------------------------------------------------- 1 | var bidfactory = require('src/bidfactory.js'); 2 | var bidmanager = require('src/bidmanager.js'); 3 | var adloader = require('src/adloader.js'); 4 | var utils = require('src/utils.js'); 5 | 6 | 7 | var JCMAdapter = function JCMAdapter() { 8 | 9 | window.pbjs = window.pbjs || {}; 10 | window.pbjs.processJCMResponse = function(JCMResponse) { 11 | if (JCMResponse) { 12 | var JCMRespObj = JSON.parse(JCMResponse); 13 | if (JCMRespObj) { 14 | var bids = JCMRespObj.bids; 15 | for (var i = 0; i < bids.length; i++) { 16 | var bid = bids[i]; 17 | var bidObject; 18 | if (bid.cpm > 0) { 19 | bidObject = bidfactory.createBid(1); 20 | bidObject.bidderCode = 'jcm'; 21 | bidObject.cpm = bid.cpm; 22 | bidObject.ad = decodeURIComponent(bid.ad.replace(/\+/g, '%20')); 23 | bidObject.width = bid.width; 24 | bidObject.height = bid.height; 25 | bidmanager.addBidResponse(utils.getBidRequest(bid.callbackId).placementCode, bidObject); 26 | } 27 | else { 28 | bidObject = bidfactory.createBid(2); 29 | bidObject.bidderCode = 'jcm'; 30 | bidmanager.addBidResponse(utils.getBidRequest(bid.callbackId).placementCode, bidObject); 31 | } 32 | } 33 | } 34 | } 35 | }; 36 | 37 | function _callBids(params) { 38 | 39 | var BidRequest = { 40 | bids: [] 41 | }; 42 | 43 | for (var i = 0; i < params.bids.length; i++) { 44 | 45 | var adSizes = ""; 46 | var bid = params.bids[i]; 47 | for (var x = 0; x < bid.sizes.length; x++) { 48 | adSizes += utils.parseGPTSingleSizeArray(bid.sizes[x]); 49 | if (x !== (bid.sizes.length - 1)) { 50 | adSizes += ','; 51 | } 52 | } 53 | 54 | 55 | BidRequest.bids.push({ 56 | "callbackId" : bid.bidId, 57 | "siteId" : bid.params.siteId, 58 | "adSizes" : adSizes 59 | }); 60 | } 61 | 62 | var JSONStr = JSON.stringify(BidRequest); 63 | var reqURL = document.location.protocol+"//media.adfrontiers.com/pq?t=hb&bids=" + encodeURIComponent(JSONStr); 64 | adloader.loadScript(reqURL); 65 | } 66 | 67 | return { 68 | callBids: _callBids 69 | }; 70 | 71 | }; 72 | 73 | module.exports = JCMAdapter; 74 | -------------------------------------------------------------------------------- /src/adapters/vertoz.js: -------------------------------------------------------------------------------- 1 | var CONSTANTS = require('../constants.json'); 2 | var utils = require('../utils.js'); 3 | var bidfactory = require('../bidfactory.js'); 4 | var bidmanager = require('../bidmanager.js'); 5 | var adloader = require('../adloader.js'); 6 | 7 | var VertozAdapter = function VertozAdapter() { 8 | 9 | const BASE_URI='//banner.vrtzads.com/vzhbidder/bid?'; 10 | const BIDDER_NAME='vertoz'; 11 | const QUERY_PARAM_KEY='q'; 12 | 13 | function _callBids(params) { 14 | var bids = params.bids || []; 15 | 16 | for (var i = 0; i < bids.length; i++) { 17 | var bid = bids[i]; 18 | let slotBidId = utils.getValue(bid,'bidId'); 19 | let cb = Math.round(new Date().getTime() / 1000); 20 | let vzEndPoint = BASE_URI; 21 | let reqParams = bid.params || {}; 22 | let placementId = utils.getValue(reqParams,'placementId'); 23 | 24 | if(utils.isEmptyStr(placementId)){ 25 | utils.logError('missing params:',BIDDER_NAME,'Enter valid vzPlacementId'); 26 | return; 27 | } 28 | 29 | let reqSrc = utils.getTopWindowLocation().href; 30 | var vzReq = { 31 | _vzPlacementId:placementId, 32 | _rqsrc:reqSrc, 33 | _cb:cb, 34 | _slotBidId:slotBidId 35 | }; 36 | let queryParamValue=JSON.stringify(vzReq); 37 | vzEndPoint = utils.tryAppendQueryString(vzEndPoint, QUERY_PARAM_KEY, queryParamValue); 38 | adloader.loadScript(vzEndPoint); 39 | } 40 | 41 | } 42 | 43 | $$PREBID_GLOBAL$$.vzResponse = function (vertozResponse) { 44 | var bidRespObj = vertozResponse; 45 | var bidObject; 46 | var reqBidObj=utils.getBidRequest(bidRespObj.slotBidId); 47 | 48 | if (bidRespObj.cpm) { 49 | bidObject = bidfactory.createBid(CONSTANTS.STATUS.GOOD,reqBidObj); 50 | bidObject.cpm = Number(bidRespObj.cpm); 51 | bidObject.ad = bidRespObj.ad + utils.createTrackPixelHtml(decodeURIComponent(bidRespObj.nurl)); 52 | bidObject.width = bidRespObj.adWidth; 53 | bidObject.height = bidRespObj.adHeight; 54 | }else { 55 | let respStatusText=bidRespObj.statusText; 56 | bidObject = bidfactory.createBid(CONSTANTS.STATUS.NO_BID,reqBidObj); 57 | utils.logMessage(respStatusText); 58 | } 59 | 60 | var adSpaceId = reqBidObj.placementCode; 61 | bidObject.bidderCode = BIDDER_NAME; 62 | bidmanager.addBidResponse(adSpaceId, bidObject); 63 | }; 64 | return { callBids: _callBids }; 65 | 66 | }; 67 | 68 | module.exports = VertozAdapter; 69 | -------------------------------------------------------------------------------- /test/spec/e2e/testcase1/pbjsapi-group/adservertargeting_spec.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var utils = require('util'); 3 | 4 | module.exports = { 5 | 'AdserverTargeting Test Case 1' : function (browser) { 6 | browser 7 | .url('http://localhost:9999/test/spec/e2e/gpt-examples/gpt_default.html') 8 | .waitForElementVisible('body', 3000) 9 | .pause(5000) 10 | .execute(function(){ 11 | 12 | if(typeof window.pbjs.bidderSettings == "undefined") { 13 | var pbjsBidderSettingsObject = [ 14 | "hb_bidder", 15 | "hb_adid", 16 | "hb_pb", 17 | "hb_size" 18 | ]; 19 | } else { 20 | var pbjsBidderSettings = window.pbjs.bidderSettings; 21 | var pbjsBidderSettingsObject = {}; 22 | Object.keys(pbjsBidderSettings).forEach(function (prop) { 23 | //if(prop == 'standard') return; 24 | var value = pbjsBidderSettings[prop]; 25 | var bs = value.adserverTargeting.map(function(item){ 26 | return item.key; 27 | }); 28 | pbjsBidderSettings.standard.adserverTargeting.map(function(value){ 29 | if(bs.indexOf(value.key) == -1 ) { 30 | bs.push(value.key) 31 | } 32 | }); 33 | pbjsBidderSettingsObject[prop] = bs; 34 | }); 35 | } 36 | 37 | var adserverTargetingObject = {}; 38 | var adserverTargeting = window.pbjs.getAdserverTargeting(); 39 | Object.keys(adserverTargeting).forEach(function(value){ 40 | if(Object.keys(adserverTargeting[value]).length == 0) return; 41 | adserverTargetingObject[adserverTargeting[value].hb_bidder] = Object.keys(adserverTargeting[value]) 42 | }); 43 | 44 | return [pbjsBidderSettingsObject, adserverTargetingObject]; 45 | }, [], function(result) { 46 | Object.keys(result.value[1]).forEach(function(key) { 47 | if(utils.isArray(result.value[0])) { 48 | assert.deepEqual(result.value[0].sort(), result.value[1][key].sort()); 49 | } else { 50 | if(result.value[0].hasOwnProperty(key)) { 51 | var obj1 = result.value[0][key].sort(); 52 | } else { 53 | var obj1 = result.value[0]['standard'].sort(); 54 | } 55 | assert.deepEqual(obj1, result.value[1][key].sort()); 56 | } 57 | }); 58 | }); 59 | }, 60 | after : function(browser) { 61 | browser.end(); 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /test/spec/loaders/adapterLoader_spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const proxyquire = require('proxyquire'); 4 | const allAdapters = require('../../fixtures/allAdapters'); 5 | const expect = require('chai').expect; 6 | require('../../../loaders/adapterLoader'); 7 | 8 | const defaultAdapters = ["aardvark","adblade","adbutler","adequant","adform","admedia","aol","appnexus","appnexusAst","getintent","hiromedia","indexExchange","kruxlink","komoona","openx","piximedia","pubmatic","pulsepoint","rubicon","sonobi","sovrn","springserve","triplelift","yieldbot","nginad","brightcom","wideorbit","jcm","underdogmedia","memeglobal","centro","roxot",{"appnexus":{"alias":"brealtime"}},{"appnexus":{"alias":"pagescience"}},{"appnexus":{"alias":"defymedia"}},{"appnexusAst":{"supportedMediaTypes":["video"]}}]; 9 | 10 | const input = `/** INSERT ADAPTERS - DO NOT EDIT OR REMOVE */ 11 | /** END INSERT ADAPTERS */`; 12 | 13 | describe('adapterLoader.js', () => { 14 | it('should replace with the default set of adapters', () => { 15 | const getAdapterStub = () => defaultAdapters; 16 | const loader = proxyquire('../../../loaders/adapterLoader', {'./getAdapters' : getAdapterStub}); 17 | let output = loader(input); 18 | expect(output).to.equal(allAdapters.getAllAdaptersString()); 19 | 20 | }); 21 | 22 | it('should return custom adapter list if file exists', () => { 23 | const customAdapter = [{customAdapterName :{srcPath: '/somepath/customAdapterName.js'}}]; 24 | const getAdapterStub = () => customAdapter; 25 | const loader = proxyquire('../../../loaders/adapterLoader', {'fs': {existsSync : ()=> true }, './getAdapters' : getAdapterStub}); 26 | let output = loader(input); 27 | const expected = 'let customAdapterName = require(\'/somepath/customAdapterName.js\');\n exports.registerBidAdapter(new customAdapterName, \'customAdapterName\');\nexports.videoAdapters = [];'; 28 | expect(output).to.equal(expected); 29 | }); 30 | 31 | it('should ignore custom adapters that that do not exist', () => { 32 | const customAdapter = ['appnexus', {customAdapterName :{srcPath: '/somepath/customAdapterName.js'}}]; 33 | const getAdapterStub = () => customAdapter; 34 | const loader = proxyquire('../../../loaders/adapterLoader', {'fs': {existsSync : ()=> false }, './getAdapters' : getAdapterStub}); 35 | let output = loader(input); 36 | const expected = 'var AppnexusAdapter = require(\'./adapters/appnexus.js\');\n exports.registerBidAdapter(new AppnexusAdapter(), \'appnexus\');\nexports.videoAdapters = [];'; 37 | expect(output).to.equal(expected); 38 | }); 39 | 40 | }); 41 | -------------------------------------------------------------------------------- /src/adapters/underdogmedia.js: -------------------------------------------------------------------------------- 1 | var bidfactory = require('../bidfactory.js'); 2 | var bidmanager = require('../bidmanager.js'); 3 | var adloader = require('../adloader.js'); 4 | var utils = require('../utils.js'); 5 | 6 | var UnderdogMediaAdapter = function UnderdogMediaAdapter() { 7 | 8 | var getJsStaticUrl = window.location.protocol + '//bid.underdog.media/udm_header_lib.js'; 9 | 10 | function _callBids(params) { 11 | if (typeof window.udm_header_lib === 'undefined') { 12 | adloader.loadScript(getJsStaticUrl, function () { bid(params); }); 13 | } else { 14 | bid(params); 15 | } 16 | } 17 | 18 | function bid(params) { 19 | var bids = params.bids; 20 | var mapped_bids = []; 21 | for (var i = 0; i < bids.length; i++) { 22 | var bidRequest = bids[i]; 23 | var callback = bidResponseCallback(bidRequest); 24 | mapped_bids.push({ 25 | sizes: bidRequest.sizes, 26 | siteId: bidRequest.params.siteId, 27 | bidfloor: bidRequest.params.bidfloor, 28 | placementCode: bidRequest.placementCode, 29 | divId: bidRequest.params.divId, 30 | subId: bidRequest.params.subId, 31 | callback: callback 32 | }); 33 | } 34 | var udmBidRequest = new window.udm_header_lib.BidRequestArray(mapped_bids); 35 | udmBidRequest.send(); 36 | } 37 | 38 | function bidResponseCallback(bid) { 39 | return function (bidResponse) { 40 | bidResponseAvailable(bid, bidResponse); 41 | }; 42 | } 43 | 44 | function bidResponseAvailable(bidRequest, bidResponse) { 45 | if(bidResponse.bids.length > 0){ 46 | for(var i = 0; i < bidResponse.bids.length; i++){ 47 | var udm_bid = bidResponse.bids[i]; 48 | var bid = bidfactory.createBid(1); 49 | bid.bidderCode = bidRequest.bidder; 50 | bid.cpm = udm_bid.cpm; 51 | bid.width = udm_bid.width; 52 | bid.height = udm_bid.height; 53 | 54 | if(udm_bid.ad_url !== undefined){ 55 | bid.adUrl = udm_bid.ad_url; 56 | } 57 | else if(udm_bid.ad_html !== undefined){ 58 | bid.ad = udm_bid.ad_html; 59 | }else{ 60 | utils.logMessage('Underdogmedia bid is lacking both ad_url and ad_html, skipping bid'); 61 | continue; 62 | } 63 | 64 | bidmanager.addBidResponse(bidRequest.placementCode, bid); 65 | } 66 | }else{ 67 | var nobid = bidfactory.createBid(2); 68 | nobid.bidderCode = bidRequest.bidder; 69 | bidmanager.addBidResponse(bidRequest.placementCode, nobid); 70 | } 71 | } 72 | 73 | return { 74 | callBids: _callBids 75 | }; 76 | 77 | }; 78 | 79 | module.exports = UnderdogMediaAdapter; 80 | -------------------------------------------------------------------------------- /src/adapters/kruxlink.js: -------------------------------------------------------------------------------- 1 | var bidfactory = require('../bidfactory.js'); 2 | var bidmanager = require('../bidmanager.js'); 3 | var adloader = require('../adloader.js'); 4 | 5 | function _qs(key, value) { 6 | return encodeURIComponent(key) + '=' + encodeURIComponent(value); 7 | } 8 | 9 | function _makeBidResponse(placementCode, bid) { 10 | var bidResponse = bidfactory.createBid(bid !== undefined ? 1 : 2); 11 | bidResponse.bidderCode = 'kruxlink'; 12 | if (bid !== undefined) { 13 | bidResponse.cpm = bid.price; 14 | bidResponse.ad = bid.adm; 15 | bidResponse.width = bid.w; 16 | bidResponse.height = bid.h; 17 | } 18 | bidmanager.addBidResponse(placementCode, bidResponse); 19 | } 20 | 21 | function _makeCallback(id, placements) { 22 | var callback = '_kruxlink_' + id; 23 | $$PREBID_GLOBAL$$[callback] = function(response) { 24 | // Clean up our callback 25 | delete $$PREBID_GLOBAL$$[callback]; 26 | 27 | // Add in the bid respones 28 | if (response.seatbid !== undefined) { 29 | for (var i = 0; i < response.seatbid.length; i++) { 30 | var seatbid = response.seatbid[i]; 31 | if (seatbid.bid !== undefined) { 32 | for (var j = 0; j < seatbid.bid.length; j++) { 33 | var bid = seatbid.bid[j]; 34 | if (bid.impid !== undefined) { 35 | _makeBidResponse(placements[bid.impid], bid); 36 | delete placements[bid.impid]; 37 | } 38 | } 39 | } 40 | } 41 | } 42 | 43 | // Add any no-bids remaining 44 | for (var impid in placements) { 45 | if (placements.hasOwnProperty(impid)) { 46 | _makeBidResponse(placements[impid]); 47 | } 48 | } 49 | }; 50 | 51 | return '$$PREBID_GLOBAL$$.' + callback; 52 | } 53 | 54 | function _callBids(params) { 55 | var impids = []; 56 | var placements = {}; 57 | 58 | var bids = params.bids || []; 59 | for (var i = 0; i < bids.length; i++) { 60 | var bidRequest = bids[i]; 61 | var bidRequestParams = bidRequest.params || {}; 62 | var impid = bidRequestParams.impid; 63 | placements[impid] = bidRequest.placementCode; 64 | 65 | impids.push(impid); 66 | } 67 | 68 | var callback = _makeCallback(params.bidderRequestId, placements); 69 | var qs = [ 70 | _qs('id', params.bidderRequestId), 71 | _qs('u', window.location.href), 72 | _qs('impid', impids.join(',')), 73 | _qs('calltype', 'pbd'), 74 | _qs('callback', callback) 75 | ]; 76 | var url = 'https://link.krxd.net/hb?' + qs.join('&'); 77 | 78 | adloader.loadScript(url); 79 | } 80 | 81 | module.exports = function KruxAdapter() { 82 | return { 83 | callBids: _callBids 84 | }; 85 | }; 86 | -------------------------------------------------------------------------------- /test/spec/integration/bug-825-truncate-bidsRequested_spec.js: -------------------------------------------------------------------------------- 1 | import { equal, notEqual } from 'assert'; 2 | 3 | import faker from 'faker'; 4 | import { makeAdUnit, makeBidder, makeAdSlot, makeRequest } from './faker/fixtures'; 5 | import adaptermanager from 'src/adaptermanager'; 6 | 7 | function resetPrebid() { 8 | delete window.$$PREBID_GLOBAL$$; 9 | require('src/prebid'); 10 | } 11 | 12 | let pbjsBackup; 13 | let adUnits; 14 | let adapters; 15 | 16 | describe('Bug: #825 adUnit code based refresh times out without setting targets', () => { 17 | describe('Given a page with five ad slots has loaded and the auction is finished', () => { 18 | 19 | before(() => { 20 | pbjsBackup = $$PREBID_GLOBAL$$; 21 | 22 | adapters = [ 23 | makeBidder(), 24 | makeBidder(), 25 | makeBidder() 26 | ]; 27 | 28 | adUnits = [ 29 | makeAdUnit({ bids: adapters }), 30 | makeAdUnit({ bids: adapters }), 31 | makeAdUnit({ bids: adapters }), 32 | makeAdUnit({ bids: adapters }), 33 | makeAdUnit({ bids: adapters }) 34 | ]; 35 | 36 | makeAdSlot({ code: adUnits[0].code }); 37 | makeAdSlot({ code: adUnits[1].code }); 38 | makeAdSlot({ code: adUnits[2].code }); 39 | makeAdSlot({ code: adUnits[3].code }); 40 | makeAdSlot({ code: adUnits[4].code }); 41 | 42 | resetPrebid(); 43 | 44 | adapters.forEach(adapter => { 45 | adaptermanager.bidderRegistry[[adapter.bidder]] = { callBids: adapter.callBids }; 46 | }); 47 | }); 48 | 49 | after(() => { 50 | window.$$PREBID_GLOBAL$$ = pbjsBackup; 51 | }); 52 | 53 | describe('When the first auction completes', () => { 54 | it('Then there will be correct number of bidder requests', () => { 55 | var clock = sinon.useFakeTimers(); 56 | $$PREBID_GLOBAL$$.requestBids(makeRequest({ adUnits })); 57 | equal($$PREBID_GLOBAL$$._bidsRequested.length, 3, 'there are three bidder requests'); 58 | var firstRequestId = $$PREBID_GLOBAL$$._bidsRequested.map(request => request.requestId).filter((value, index, array) => array.indexOf(value) === index)[0]; 59 | $$PREBID_GLOBAL$$.requestBids(makeRequest({ adUnits: [adUnits[0]] })); 60 | clock.tick($$PREBID_GLOBAL$$.bidderTimeout); 61 | var nextRequestId = $$PREBID_GLOBAL$$._bidsRequested.map(request => request.requestId).filter((value, index, array) => array.indexOf(value) === index)[0]; 62 | equal($$PREBID_GLOBAL$$._bidsRequested.length, 3, 'there are still three bidder request'); 63 | notEqual(firstRequestId, nextRequestId, 'the request IDs have changed'); 64 | clock.restore(); 65 | }); 66 | }); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /src/adapters/pulsepoint.js: -------------------------------------------------------------------------------- 1 | var bidfactory = require('../bidfactory.js'); 2 | var bidmanager = require('../bidmanager.js'); 3 | var adloader = require('../adloader.js'); 4 | var utils = require('../utils.js'); 5 | 6 | var PulsePointAdapter = function PulsePointAdapter() { 7 | 8 | var getJsStaticUrl = window.location.protocol + '//tag.contextweb.com/getjs.static.js'; 9 | var bidUrl = window.location.protocol + '//bid.contextweb.com/header/tag'; 10 | 11 | function _callBids(params) { 12 | if (typeof window.pp === 'undefined') { 13 | adloader.loadScript(getJsStaticUrl, function () { bid(params); }, true); 14 | } else { 15 | bid(params); 16 | } 17 | } 18 | 19 | function bid(params) { 20 | var bids = params.bids; 21 | for (var i = 0; i < bids.length; i++) { 22 | var bidRequest = bids[i]; 23 | requestBid(bidRequest); 24 | } 25 | } 26 | 27 | function requestBid(bidRequest) { 28 | try { 29 | var ppBidRequest = new window.pp.Ad(bidRequestOptions(bidRequest)); 30 | ppBidRequest.display(); 31 | } catch(e) { 32 | //register passback on any exceptions while attempting to fetch response. 33 | utils.logError('pulsepoint.requestBid', 'ERROR', e); 34 | bidResponseAvailable(bidRequest); 35 | } 36 | } 37 | 38 | function bidRequestOptions(bidRequest) { 39 | var callback = bidResponseCallback(bidRequest); 40 | var options = { 41 | cn: 1, 42 | ca: window.pp.requestActions.BID, 43 | cu: bidUrl, 44 | adUnitId: bidRequest.placementCode, 45 | callback: callback 46 | }; 47 | for(var param in bidRequest.params) { 48 | if(bidRequest.params.hasOwnProperty(param)) { 49 | options[param] = bidRequest.params[param]; 50 | } 51 | } 52 | return options; 53 | } 54 | 55 | function bidResponseCallback(bid) { 56 | return function (bidResponse) { 57 | bidResponseAvailable(bid, bidResponse); 58 | }; 59 | } 60 | 61 | function bidResponseAvailable(bidRequest, bidResponse) { 62 | if (bidResponse) { 63 | var adSize = bidRequest.params.cf.toUpperCase().split('X'); 64 | var bid = bidfactory.createBid(1, bidRequest); 65 | bid.bidderCode = bidRequest.bidder; 66 | bid.cpm = bidResponse.bidCpm; 67 | bid.ad = bidResponse.html; 68 | bid.width = adSize[0]; 69 | bid.height = adSize[1]; 70 | bidmanager.addBidResponse(bidRequest.placementCode, bid); 71 | } else { 72 | var passback = bidfactory.createBid(2, bidRequest); 73 | passback.bidderCode = bidRequest.bidder; 74 | bidmanager.addBidResponse(bidRequest.placementCode, passback); 75 | } 76 | } 77 | 78 | return { 79 | callBids: _callBids 80 | }; 81 | 82 | }; 83 | 84 | module.exports = PulsePointAdapter; 85 | -------------------------------------------------------------------------------- /test/spec/loaders/getAdapters_spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mockfs = require('mock-fs'); 4 | const proxyquire = require('proxyquire'); 5 | const expect = require('chai').expect; 6 | 7 | require('../../../loaders/getAdapters'); 8 | 9 | describe('loaders/getAdapters', () => { 10 | 11 | let defaultAdapters; 12 | let customAdapters; 13 | 14 | beforeEach(() => { 15 | defaultAdapters = [ 'adapter 1', 'adapter 2', 'adapter 3' ]; 16 | customAdapters = [ 'adapter 1' ]; 17 | }); 18 | 19 | afterEach(() => { 20 | mockfs.restore(); 21 | }); 22 | 23 | describe('when custom adapter list is defined', () => { 24 | 25 | describe('and exists', () => { 26 | 27 | it('should return custom adapter list', () => { 28 | mockfs({ 29 | 'adapters.json': JSON.stringify(defaultAdapters), 30 | 'custom-adapters.json': JSON.stringify(customAdapters) 31 | }); 32 | const getAdapters = proxyquire('../../../loaders/getAdapters', { 33 | yargs: { argv: { adapters: 'custom-adapters.json' } } 34 | }); 35 | expect(getAdapters()).to.deep.equal(customAdapters); 36 | }); 37 | 38 | }); 39 | 40 | describe('and does not exist', () => { 41 | 42 | it('should return default adapter list and show warning', () => { 43 | let log; 44 | const consoleLog = console.log.bind(console); 45 | console.log = (message) => { 46 | log = message; 47 | }; 48 | mockfs({ 49 | 'adapters.json': JSON.stringify(defaultAdapters) 50 | }); 51 | const getAdapters = proxyquire('../../../loaders/getAdapters', { 52 | yargs: { argv: { adapters: 'non-existent-adapters.json' } } 53 | }); 54 | expect(getAdapters()).to.deep.equal(defaultAdapters); 55 | expect(log).to.match(/non-existent-adapters.json/); 56 | console.log = consoleLog; 57 | }); 58 | 59 | }); 60 | 61 | }); 62 | 63 | describe('when custom adapter list is not defined', () => { 64 | 65 | it('should return default adapter list', () => { 66 | mockfs({ 67 | 'adapters.json': JSON.stringify(defaultAdapters) 68 | }); 69 | const getAdapters = proxyquire('../../../loaders/getAdapters', { 70 | yargs: { argv: {} } 71 | }); 72 | expect(getAdapters()).to.deep.equal(defaultAdapters); 73 | }); 74 | 75 | }); 76 | 77 | describe('when default adapter list cannot be found', () => { 78 | 79 | it('should return empty array', () => { 80 | mockfs({ 81 | 'adapters.json': mockfs.file({ mode: 0x000 }) 82 | }); 83 | const getAdapters = proxyquire('../../../loaders/getAdapters', { 84 | yargs: { argv: {} } 85 | }); 86 | expect(getAdapters()).to.deep.equal([]); 87 | }); 88 | 89 | }); 90 | 91 | }); 92 | -------------------------------------------------------------------------------- /src/adapters/adequant.js: -------------------------------------------------------------------------------- 1 | var bidfactory = require('../bidfactory.js'); 2 | var bidmanager = require('../bidmanager.js'); 3 | var adloader = require('../adloader.js'); 4 | var utils = require('../utils.js'); 5 | var CONSTANTS = require('../constants.json'); 6 | 7 | module.exports = function() { 8 | var req_url_base = 'https://rex.adequant.com/rex/c2s_prebid?'; 9 | 10 | function _callBids(params) { 11 | var req_url = []; 12 | var publisher_id = null; 13 | var sizes = []; 14 | var cats = null; 15 | var replies = []; 16 | var placements = {}; 17 | 18 | var bids = params.bids || []; 19 | for (var i = 0; i < bids.length; i++) { 20 | var bid_request = bids[i]; 21 | var br_params = bid_request.params || {}; 22 | placements[bid_request.placementCode] = true; 23 | 24 | publisher_id = br_params.publisher_id.toString() || publisher_id; 25 | var bidfloor = br_params.bidfloor || 0.01; 26 | cats = br_params.cats || cats; 27 | if (typeof(cats) === utils.objectType_string) { cats = cats.split(' '); } 28 | var br_sizes = utils.parseSizesInput(bid_request.sizes); 29 | for (var j = 0; j < br_sizes.length; j++) { 30 | sizes.push(br_sizes[j]+'_'+bidfloor); 31 | replies.push(bid_request.placementCode); 32 | } 33 | } 34 | // send out 1 bid request for all bids 35 | if (publisher_id) { req_url.push('a='+publisher_id); } 36 | if (cats) { req_url.push('c='+cats.join('+')); } 37 | if (sizes) { req_url.push('s='+sizes.join('+')); } 38 | 39 | adloader.loadScript(req_url_base+req_url.join('&'), function() { process_bids(replies, placements); }); 40 | } 41 | 42 | function process_bids(replies, placements) { 43 | var placement_code, bid, adequant_creatives = window.adequant_creatives; 44 | if (adequant_creatives && adequant_creatives.seatbid) { 45 | for (var i=0; i { 81 | if (prev.max > curr.max) { 82 | return prev; 83 | } 84 | return curr; 85 | }, { 86 | 'max': 0, 87 | }); 88 | let bucket = config.buckets.find(bucket => { 89 | if (cpm > cap.max) { 90 | const precision = bucket.precision || _defaultPrecision; 91 | cpmStr = bucket.max.toFixed(precision); 92 | } else if (cpm <= bucket.max && cpm >= bucket.min) { 93 | return bucket; 94 | } 95 | }); 96 | if (bucket) { 97 | cpmStr = getCpmTarget(cpm, bucket.increment, bucket.precision); 98 | } 99 | return cpmStr; 100 | } 101 | 102 | function isValidePriceConfig(config) { 103 | if (!config || !config.buckets || !Array.isArray(config.buckets)) { 104 | return false; 105 | } 106 | let isValid = true; 107 | config.buckets.forEach(bucket => { 108 | if(typeof bucket.min === 'undefined' || !bucket.max || !bucket.increment) { 109 | isValid = false; 110 | } 111 | }); 112 | return isValid; 113 | } 114 | 115 | function getCpmTarget(cpm, increment, precision) { 116 | if (!precision) { 117 | precision = _defaultPrecision; 118 | } 119 | let bucketSize = 1 / increment; 120 | return (Math.floor(cpm * bucketSize) / bucketSize).toFixed(precision); 121 | } 122 | 123 | export { getPriceBucketString, isValidePriceConfig }; 124 | -------------------------------------------------------------------------------- /integrationExamples/gpt/pbjs_video_adUnit.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Prebid.js video adUnit example 6 | 7 | 8 | 9 | 10 | 11 | 24 | 25 | 76 | 77 | 78 | 79 |
80 | 90 |
91 | 92 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prebid.js", 3 | "version": "0.17.0", 4 | "description": "Header Bidding Management Library", 5 | "main": "src/prebid.js", 6 | "scripts": { 7 | "test": "gulp test && gulp mocha" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/prebid/Prebid.js.git" 12 | }, 13 | "keywords": [ 14 | "advertising", 15 | "auction", 16 | "header bidding", 17 | "prebid" 18 | ], 19 | "globalVarName": "pbjs", 20 | "analytics": [], 21 | "author": "the prebid.js contributors", 22 | "license": "Apache-2.0", 23 | "devDependencies": { 24 | "babel-core": "^6.5.2", 25 | "babel-loader": "^6.2.3", 26 | "babel-plugin-transform-object-assign": "^6.8.0", 27 | "babel-plugin-transform-es3-property-literals": "^6.8.0", 28 | "babel-plugin-transform-es3-member-expression-literals": "^6.8.0", 29 | "babel-preset-es2015": "^6.5.0", 30 | "block-loader": "^2.1.0", 31 | "chai": "^3.3.0", 32 | "coveralls": "^2.11.11", 33 | "del": "^2.2.0", 34 | "ejs": "^2.5.1", 35 | "es5-shim": "^4.5.2", 36 | "faker": "^3.1.0", 37 | "fs.extra": "^1.3.2", 38 | "gulp": "^3.8.7", 39 | "gulp-babel": "^6.1.2", 40 | "gulp-clean": "^0.3.1", 41 | "gulp-concat": "^2.6.0", 42 | "gulp-connect": "^2.0.6", 43 | "gulp-header": "^1.7.1", 44 | "gulp-jscs": "^3.0.2", 45 | "gulp-jsdoc-to-markdown": "^1.2.1", 46 | "gulp-jshint": "^1.8.4", 47 | "gulp-karma": "0.0.4", 48 | "gulp-mocha": "^2.2.0", 49 | "gulp-rename": "^1.2.0", 50 | "gulp-replace": "^0.4.0", 51 | "gulp-shell": "^0.5.2", 52 | "gulp-uglify": "^0.3.1", 53 | "gulp-util": "^3.0.0", 54 | "gulp-webdriver": "^1.0.1", 55 | "gulp-zip": "^3.1.0", 56 | "istanbul": "^0.3.2", 57 | "istanbul-instrumenter-loader": "^0.1.2", 58 | "jshint-stylish": "^2.2.1", 59 | "json-loader": "^0.5.1", 60 | "karma": "^0.13.2", 61 | "karma-babel-preprocessor": "^6.0.1", 62 | "karma-browserstack-launcher": "^1.0.1", 63 | "karma-chai": "^0.1.0", 64 | "karma-chrome-launcher": "^0.2.0", 65 | "karma-coverage": "^0.2.7", 66 | "karma-es5-shim": "https://github.com/pokehanai/karma-es5-shim/archive/v2.1.0.tar.gz", 67 | "karma-expect": "^1.1.0", 68 | "karma-firefox-launcher": "^0.1.3", 69 | "karma-html-reporter": "^0.2.7", 70 | "karma-ie-launcher": "^0.1.5", 71 | "karma-junit-reporter": "^0.3.8", 72 | "karma-mocha": "^0.2.0", 73 | "karma-opera-launcher": "^0.1.0", 74 | "karma-phantomjs-launcher": "^0.2.1", 75 | "karma-requirejs": "^0.2.2", 76 | "karma-safari-launcher": "^0.1.1", 77 | "karma-sauce-launcher": "^0.2.10", 78 | "karma-script-launcher": "^0.1.0", 79 | "karma-sinon-ie": "^2.0.0-rc10", 80 | "karma-webpack": "^1.5.1", 81 | "localtunnel": "^1.3.0", 82 | "mkpath": "^1.0.0", 83 | "mocha": "^1.21.4", 84 | "mock-fs": "^3.11.0", 85 | "nightwatch": "^0.9.5", 86 | "open": "0.0.5", 87 | "phantomjs": "^1.9.18", 88 | "proxyquire": "^1.7.10", 89 | "querystringify": "0.0.3", 90 | "requirejs": "^2.1.20", 91 | "sinon": "^1.12.1", 92 | "string-replace-webpack-plugin": "0.0.3", 93 | "uglify-js": "^2.4.15", 94 | "url-parse": "^1.0.5", 95 | "webpack": "^1.12.3", 96 | "webpack-stream": "^3.1.0", 97 | "yargs": "^1.3.1" 98 | }, 99 | "dependencies": {} 100 | } 101 | -------------------------------------------------------------------------------- /src/adloader.js: -------------------------------------------------------------------------------- 1 | var utils = require('./utils'); 2 | let _requestCache = {}; 3 | 4 | //add a script tag to the page, used to add /jpt call to page 5 | exports.loadScript = function (tagSrc, callback, cacheRequest) { 6 | //var noop = () => {}; 7 | // 8 | //callback = callback || noop; 9 | if (!tagSrc) { 10 | utils.logError('Error attempting to request empty URL', 'adloader.js:loadScript'); 11 | return; 12 | } 13 | 14 | if (cacheRequest) { 15 | if (_requestCache[tagSrc]) { 16 | if (callback && typeof callback === 'function') { 17 | if (_requestCache[tagSrc].loaded) { 18 | //invokeCallbacks immediately 19 | callback(); 20 | } else { 21 | //queue the callback 22 | _requestCache[tagSrc].callbacks.push(callback); 23 | } 24 | } 25 | } else { 26 | _requestCache[tagSrc] = { 27 | loaded: false, 28 | callbacks: [] 29 | }; 30 | if (callback && typeof callback === 'function') { 31 | _requestCache[tagSrc].callbacks.push(callback); 32 | } 33 | 34 | requestResource(tagSrc, function () { 35 | _requestCache[tagSrc].loaded = true; 36 | try { 37 | for (let i = 0; i < _requestCache[tagSrc].callbacks.length; i++) { 38 | _requestCache[tagSrc].callbacks[i](); 39 | } 40 | } 41 | catch (e) { 42 | utils.logError('Error executing callback', 'adloader.js:loadScript', e); 43 | } 44 | }); 45 | } 46 | } 47 | 48 | //trigger one time request 49 | else { 50 | requestResource(tagSrc, callback); 51 | } 52 | 53 | }; 54 | 55 | function requestResource(tagSrc, callback) { 56 | var jptScript = document.createElement('script'); 57 | jptScript.type = 'text/javascript'; 58 | jptScript.async = true; 59 | 60 | // Execute a callback if necessary 61 | if (callback && typeof callback === 'function') { 62 | if (jptScript.readyState) { 63 | jptScript.onreadystatechange = function () { 64 | if (jptScript.readyState === 'loaded' || jptScript.readyState === 'complete') { 65 | jptScript.onreadystatechange = null; 66 | callback(); 67 | } 68 | }; 69 | } else { 70 | jptScript.onload = function () { 71 | callback(); 72 | }; 73 | } 74 | } 75 | 76 | jptScript.src = tagSrc; 77 | 78 | //add the new script tag to the page 79 | var elToAppend = document.getElementsByTagName('head'); 80 | elToAppend = elToAppend.length ? elToAppend : document.getElementsByTagName('body'); 81 | if (elToAppend.length) { 82 | elToAppend = elToAppend[0]; 83 | elToAppend.insertBefore(jptScript, elToAppend.firstChild); 84 | } 85 | } 86 | 87 | //track a impbus tracking pixel 88 | //TODO: Decide if tracking via AJAX is sufficent, or do we need to 89 | //run impression trackers via page pixels? 90 | exports.trackPixel = function (pixelUrl) { 91 | let delimiter; 92 | let trackingPixel; 93 | 94 | if (!pixelUrl || typeof (pixelUrl) !== 'string') { 95 | utils.logMessage('Missing or invalid pixelUrl.'); 96 | return; 97 | } 98 | 99 | delimiter = pixelUrl.indexOf('?') > 0 ? '&' : '?'; 100 | 101 | //add a cachebuster so we don't end up dropping any impressions 102 | trackingPixel = pixelUrl + delimiter + 'rnd=' + Math.floor(Math.random() * 1E7); 103 | (new Image()).src = trackingPixel; 104 | return trackingPixel; 105 | }; 106 | -------------------------------------------------------------------------------- /test/spec/e2e/custom-reporter/pbjs-html-reporter.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var mkpath = require('mkpath'); 3 | var path = require('path'); 4 | var ejs = require('ejs'); 5 | 6 | module.exports = new (function() { 7 | 8 | var tmpl = __dirname + '/junit.xml.ejs'; 9 | var tmplData; 10 | var globalResults; 11 | 12 | function loadTemplate(cb) { 13 | if (tmplData) { 14 | cb(tmplData); 15 | return; 16 | } 17 | fs.readFile(tmpl, function (err, data) { 18 | if (err) { 19 | throw err; 20 | } 21 | tmplData = data.toString(); 22 | cb(tmplData); 23 | }); 24 | } 25 | 26 | function adaptAssertions(module) { 27 | Object.keys(module.completed).forEach(function(item) { 28 | var testcase = module.completed[item]; 29 | var assertions = testcase.assertions; 30 | for (var i = 0; i < assertions.length; i++) { 31 | if (assertions[i].stackTrace) { 32 | assertions[i].stackTrace = stackTraceFilter(assertions[i].stackTrace.split('\n')); 33 | } 34 | } 35 | 36 | if (testcase.failed > 0 && testcase.stackTrace) { 37 | var stackParts = testcase.stackTrace.split('\n'); 38 | var errorMessage = stackParts.shift(); 39 | testcase.stackTrace = stackTraceFilter(stackParts); 40 | testcase.message = errorMessage; 41 | } 42 | }); 43 | } 44 | 45 | function writeReport(moduleKey, data, opts, callback) { 46 | var module = globalResults.modules[moduleKey]; 47 | var pathParts = moduleKey.split(path.sep); 48 | var moduleName = pathParts.pop(); 49 | var output_folder = opts.output_folder; 50 | adaptAssertions(module); 51 | 52 | var rendered = ejs.render(data, { 53 | module : module, 54 | moduleName : moduleName, 55 | systemerr : globalResults.errmessages.join('\n'), 56 | }); 57 | 58 | if (pathParts.length) { 59 | output_folder = path.join(output_folder, pathParts.join(path.sep)); 60 | mkpath.sync(output_folder); 61 | } 62 | 63 | var filename = path.join(output_folder, opts.filename_prefix + moduleName + '.xml'); 64 | fs.writeFile(filename, rendered, function(err) { 65 | callback(err); 66 | globalResults.errmessages.length = 0; 67 | }); 68 | } 69 | 70 | function stackTraceFilter(parts) { 71 | var stack = parts.reduce(function(list, line) { 72 | if (contains(line, [ 73 | 'node_modules', 74 | '(node.js:', 75 | '(events.js:' 76 | ])) { 77 | return list; 78 | } 79 | 80 | list.push(line); 81 | return list; 82 | }, []); 83 | 84 | return stack.join('\n'); 85 | } 86 | 87 | function contains(str, text) { 88 | if( Object.prototype.toString.call( text ) === '[object Array]' ) { 89 | for (var i = 0; i < text.length; i++) { 90 | if (contains(str, text[i])) { 91 | return true; 92 | } 93 | } 94 | } 95 | return str.indexOf(text) > -1; 96 | } 97 | 98 | this.write = function(results, options, callback) { 99 | options.filename_prefix = process.env.__NIGHTWATCH_ENV+'_'; 100 | globalResults = results; 101 | var keys = Object.keys(results.modules); 102 | 103 | loadTemplate(function createReport(data) { 104 | var moduleKey = keys.shift(); 105 | 106 | writeReport(moduleKey, data, options, function(err) { 107 | if (err || (keys.length === 0)) { 108 | callback(err); 109 | } else { 110 | createReport(data); 111 | } 112 | }); 113 | }); 114 | }; 115 | 116 | })(); 117 | -------------------------------------------------------------------------------- /src/adapters/roxot.js: -------------------------------------------------------------------------------- 1 | var CONSTANTS = require('../constants.json'); 2 | var utils = require('../utils.js'); 3 | var bidfactory = require('../bidfactory.js'); 4 | var bidmanager = require('../bidmanager.js'); 5 | var adloader = require('../adloader'); 6 | 7 | var RoxotAdapter = function RoxotAdapter() { 8 | var roxotUrl = "r.rxthdr.com"; 9 | 10 | $$PREBID_GLOBAL$$.roxotResponseHandler = roxotResponseHandler; 11 | 12 | return { 13 | callBids: _callBids 14 | }; 15 | 16 | function _callBids(bidReqs) { 17 | utils.logInfo('callBids roxot adapter invoking'); 18 | 19 | var domain = window.location.host; 20 | var page = window.location.pathname + location.search + location.hash; 21 | 22 | var roxotBidReqs = { 23 | id: utils.getUniqueIdentifierStr(), 24 | bids: bidReqs, 25 | site: { 26 | domain: domain, 27 | page: page 28 | } 29 | }; 30 | 31 | var scriptUrl = '//' + roxotUrl + '?callback=$$PREBID_GLOBAL$$.roxotResponseHandler' + 32 | '&src=' + CONSTANTS.REPO_AND_VERSION + 33 | '&br=' + encodeURIComponent(JSON.stringify(roxotBidReqs)); 34 | 35 | adloader.loadScript(scriptUrl); 36 | } 37 | 38 | function roxotResponseHandler(roxotResponseObject) { 39 | utils.logInfo('roxotResponseHandler invoking'); 40 | var placements = []; 41 | 42 | if (isResponseInvalid()) { 43 | return fillPlacementEmptyBid(); 44 | } 45 | 46 | roxotResponseObject.bids.forEach(pushRoxotBid); 47 | var allBidResponse = fillPlacementEmptyBid(placements); 48 | utils.logInfo('roxotResponse handler finish'); 49 | 50 | return allBidResponse; 51 | 52 | function isResponseInvalid() { 53 | return !roxotResponseObject || !roxotResponseObject.bids || !Array.isArray(roxotResponseObject.bids) || roxotResponseObject.bids.length <= 0; 54 | } 55 | 56 | function pushRoxotBid(roxotBid) { 57 | var placementCode = ''; 58 | 59 | var bidReq = $$PREBID_GLOBAL$$ 60 | ._bidsRequested.find(bidSet => bidSet.bidderCode === 'roxot') 61 | .bids.find(bid => bid.bidId === roxotBid.bidId); 62 | 63 | if (!bidReq) { 64 | return pushErrorBid(placementCode); 65 | } 66 | 67 | bidReq.status = CONSTANTS.STATUS.GOOD; 68 | 69 | placementCode = bidReq.placementCode; 70 | placements.push(placementCode); 71 | 72 | var cpm = roxotBid.cpm; 73 | var responseNurl = ''; 74 | 75 | if (!cpm) { 76 | return pushErrorBid(placementCode); 77 | } 78 | 79 | var bid = bidfactory.createBid(1, bidReq); 80 | 81 | bid.creative_id = roxotBid.id; 82 | bid.bidderCode = 'roxot'; 83 | bid.cpm = cpm; 84 | bid.ad = decodeURIComponent(roxotBid.adm + responseNurl); 85 | bid.width = parseInt(roxotBid.w); 86 | bid.height = parseInt(roxotBid.h); 87 | 88 | bidmanager.addBidResponse(placementCode, bid); 89 | } 90 | 91 | function fillPlacementEmptyBid(places) { 92 | $$PREBID_GLOBAL$$ 93 | ._bidsRequested.find(bidSet => bidSet.bidderCode === 'roxot') 94 | .bids.forEach(fillIfNotFilled); 95 | 96 | function fillIfNotFilled(bid) { 97 | if (utils.contains(places, bid.placementCode)) { 98 | return null; 99 | } 100 | 101 | pushErrorBid(bid); 102 | } 103 | } 104 | 105 | function pushErrorBid(bidRequest) { 106 | var bid = bidfactory.createBid(2, bidRequest); 107 | bid.bidderCode = 'roxot'; 108 | bidmanager.addBidResponse(bidRequest.placementCode, bid); 109 | } 110 | } 111 | }; 112 | 113 | module.exports = RoxotAdapter; -------------------------------------------------------------------------------- /test/spec/adapters/getintent_spec.js: -------------------------------------------------------------------------------- 1 | import Adapter from '../../../src/adapters/getintent'; 2 | import bidManager from '../../../src/bidmanager'; 3 | import {expect} from 'chai'; 4 | 5 | describe('getintent media adapter test', () => { 6 | 7 | let adapter; 8 | 9 | window.gi_hb = { 10 | makeBid: function(bidRequest, callback) { 11 | var pid = bidRequest.pid; 12 | var tid = bidRequest.tid; 13 | 14 | if (pid == "p1" || pid == "p2") { 15 | callback({ 16 | ad : `Ad Markup ${pid} ${tid}`, 17 | cpm : 2.71, 18 | size : `${bidRequest.size}` 19 | }, bidRequest); 20 | } else { 21 | callback({ 22 | no_bid: 1 23 | }, bidRequest); 24 | } 25 | } 26 | }; 27 | 28 | function callOut() { 29 | adapter.callBids({ 30 | bidderCode: "getintent", 31 | bids: [ 32 | { 33 | bidder: "getintent", 34 | adUnitCode: "test1", 35 | sizes: [[320,240]], 36 | params: { 37 | pid: "p1", 38 | tid: "t1", 39 | cur: "USD" 40 | } 41 | }, 42 | { 43 | bidder: "getintent", 44 | adUnitCode: "test2", 45 | sizes: [[720,90]], 46 | params: { 47 | pid: "p2", 48 | tid: "t1", 49 | cur: "USD" 50 | } 51 | }, 52 | { 53 | bidder: "getintent", 54 | adUnitCode: "test3", 55 | sizes: [[400,500]], 56 | params: { 57 | pid: "p3", 58 | tid: "t2", 59 | cur: "USD" 60 | } 61 | } 62 | ] 63 | }); 64 | } 65 | 66 | beforeEach(() => { 67 | adapter = new Adapter(); 68 | }); 69 | 70 | afterEach(() => { 71 | }); 72 | 73 | describe('adding bids to the manager', () => { 74 | 75 | let firstBid; 76 | let secondBid; 77 | let thirdBid; 78 | 79 | beforeEach(() => { 80 | sinon.stub(bidManager, 'addBidResponse'); 81 | callOut(); 82 | firstBid = bidManager.addBidResponse.firstCall.args[1]; 83 | secondBid = bidManager.addBidResponse.secondCall.args[1]; 84 | thirdBid = bidManager.addBidResponse.thirdCall.args[1]; 85 | }); 86 | 87 | afterEach(() => { 88 | bidManager.addBidResponse.restore(); 89 | }); 90 | 91 | it('was called three times', () => { 92 | sinon.assert.calledThrice(bidManager.addBidResponse); 93 | }); 94 | 95 | it('will respond to the first bid', () => { 96 | expect(firstBid).to.have.property('ad', 'Ad Markup p1 t1'); 97 | expect(firstBid).to.have.property('cpm', 2.71); 98 | expect(firstBid).to.have.property('width', '320'); 99 | expect(firstBid).to.have.property('height', '240'); 100 | }); 101 | 102 | it('will respond to the second bid', () => { 103 | expect(secondBid).to.have.property('ad', 'Ad Markup p2 t1'); 104 | expect(secondBid).to.have.property('cpm', 2.71); 105 | expect(secondBid).to.have.property('width', '720'); 106 | expect(secondBid).to.have.property('height', '90'); 107 | }); 108 | 109 | it('wont respond to the third bid', () => { 110 | expect(thirdBid).to.not.have.property('ad'); 111 | expect(thirdBid).to.not.have.property('cpm'); 112 | }); 113 | 114 | it('will add the bidder code to the bid object', () => { 115 | expect(firstBid).to.have.property('bidderCode', 'getintent'); 116 | expect(secondBid).to.have.property('bidderCode', 'getintent'); 117 | expect(thirdBid).to.have.property('bidderCode', 'getintent'); 118 | }); 119 | }); 120 | 121 | }); 122 | -------------------------------------------------------------------------------- /src/adapters/widespace.js: -------------------------------------------------------------------------------- 1 | 2 | import { getBidRequest } from '../utils.js'; 3 | 4 | var utils = require('../utils.js'); 5 | var adloader = require('../adloader.js'); 6 | var bidmanager = require('../bidmanager.js'); 7 | var bidfactory = require('../bidfactory.js'); 8 | 9 | 10 | function WidespaceAdapter() { 11 | let useSSL = 'https:' === document.location.protocol, 12 | baseURL = (useSSL ? 'https:' : 'http:') + '//engine.widespace.com/map/engine/hb/dynamic?', 13 | callbackName = '$$PREBID_GLOBAL$$.widespaceHandleCB'; 14 | 15 | function _callBids(params) { 16 | let bids = params && params.bids || []; 17 | 18 | for (var i = 0; i < bids.length; i++) { 19 | const bid = bids[i], 20 | callbackUid = bid.bidId, 21 | sid = bid.params.sid, 22 | currency = bid.params.currency; 23 | 24 | //Handle Sizes string 25 | let sizeQueryString = ''; 26 | let parsedSizes = utils.parseSizesInput(bid.sizes); 27 | 28 | sizeQueryString = parsedSizes.reduce((prev, curr) => { 29 | return prev ? `${prev},${curr}` : curr; 30 | }, sizeQueryString); 31 | 32 | var requestURL = baseURL; 33 | requestURL = utils.tryAppendQueryString(requestURL, 'hb.name', 'prebidjs'); 34 | requestURL = utils.tryAppendQueryString(requestURL, 'hb.callback', callbackName); 35 | requestURL = utils.tryAppendQueryString(requestURL, 'hb.callbackUid', callbackUid); 36 | requestURL = utils.tryAppendQueryString(requestURL, 'hb.sizes', sizeQueryString); 37 | requestURL = utils.tryAppendQueryString(requestURL, 'sid', sid); 38 | requestURL = utils.tryAppendQueryString(requestURL, 'hb.currency', currency); 39 | 40 | 41 | // Expose the callback 42 | $$PREBID_GLOBAL$$.widespaceHandleCB = window[callbackName] = handleCallback; 43 | 44 | adloader.loadScript(requestURL); 45 | } 46 | } 47 | 48 | //Handle our callback 49 | var handleCallback = function handleCallback(bidsArray) { 50 | if (!bidsArray) { return; } 51 | 52 | var bidObject, 53 | bidCode = 'widespace'; 54 | 55 | for (var i = 0, l = bidsArray.length; i < l; i++) { 56 | var bid = bidsArray[i], 57 | placementCode = '', 58 | validSizes = []; 59 | 60 | bid.sizes = {height: bid.height, width: bid.height}; 61 | 62 | var inBid = getBidRequest(bid.callbackUid); 63 | 64 | if (inBid) { 65 | bidCode = inBid.bidder; 66 | placementCode = inBid.placementCode; 67 | validSizes = inBid.sizes; 68 | } 69 | if (bid && bid.callbackUid && bid.status !=='noad' && verifySize(bid.sizes, validSizes)) { 70 | bidObject = bidfactory.createBid(1); 71 | bidObject.bidderCode = bidCode; 72 | bidObject.cpm = bid.cpm; 73 | bidObject.cur = bid.currency; 74 | bidObject.creative_id = bid.adId; 75 | bidObject.ad = bid.code; 76 | bidObject.width = bid.width; 77 | bidObject.height = bid.height; 78 | bidmanager.addBidResponse(placementCode, bidObject); 79 | } else { 80 | bidObject = bidfactory.createBid(2); 81 | bidObject.bidderCode = bidCode; 82 | bidmanager.addBidResponse(placementCode, bidObject); 83 | } 84 | } 85 | 86 | 87 | function verifySize(bid, validSizes) { 88 | for (var j = 0, k = validSizes.length; j < k; j++) { 89 | if (bid.width === validSizes[j][0] && 90 | bid.height === validSizes[j][1]) { 91 | return true; 92 | } 93 | } 94 | return false; 95 | } 96 | }; 97 | 98 | return { 99 | callBids: _callBids 100 | }; 101 | } 102 | 103 | module.exports = WidespaceAdapter; 104 | -------------------------------------------------------------------------------- /src/adapters/admedia.js: -------------------------------------------------------------------------------- 1 | import { getBidRequest } from '../utils.js'; 2 | var bidfactory = require('../bidfactory.js'); 3 | var bidmanager = require('../bidmanager.js'); 4 | var adloader = require('../adloader.js'); 5 | var utils = require('../utils.js'); 6 | var CONSTANTS = require('../constants.json'); 7 | 8 | /** 9 | * Adapter for requesting bids from AdMedia. 10 | * 11 | */ 12 | var AdmediaAdapter = function AdmediaAdapter() { 13 | 14 | function _callBids(params){ 15 | var bids, bidderUrl = (window.location.protocol) + "//b.admedia.com/banner/prebid/bidder/?"; 16 | bids = params.bids || []; 17 | for (var i = 0; i < bids.length; i++) { 18 | var request_obj = {}; 19 | var bid = bids[i]; 20 | 21 | if (bid.params.aid) { 22 | request_obj.aid = bid.params.aid; 23 | } 24 | else{ 25 | utils.logError('required param aid is missing', "admedia"); 26 | continue; 27 | } 28 | 29 | //optional page_url macro 30 | if (bid.params.page_url) { 31 | request_obj.page_url = bid.params.page_url; 32 | } 33 | 34 | //if set, return a test ad for all aids 35 | if (bid.params.test_ad === 1) { 36 | request_obj.test_ad = 1; 37 | } 38 | 39 | var parsedSizes = utils.parseSizesInput(bid.sizes); 40 | var parsedSizesLength = parsedSizes.length; 41 | if (parsedSizesLength > 0) { 42 | //first value should be "size" 43 | request_obj.size = parsedSizes[0]; 44 | if (parsedSizesLength > 1) { 45 | //any subsequent values should be "promo_sizes" 46 | var promo_sizes = []; 47 | for (var j = 1; j < parsedSizesLength; j++) { 48 | promo_sizes.push(parsedSizes[j]); 49 | } 50 | 51 | request_obj.promo_sizes = promo_sizes.join(","); 52 | 53 | } 54 | } 55 | 56 | //detect urls 57 | request_obj.siteDomain = window.location.host; 58 | request_obj.sitePage = window.location.href; 59 | request_obj.siteRef = document.referrer; 60 | request_obj.topUrl = utils.getTopWindowUrl(); 61 | 62 | request_obj.callbackId = bid.bidId; 63 | 64 | var endpoint = bidderUrl+utils.parseQueryStringParameters(request_obj); 65 | 66 | //utils.logMessage('Admedia request built: ' + endpoint); 67 | 68 | adloader.loadScript(endpoint); 69 | } 70 | } 71 | 72 | //expose the callback to global object 73 | $$PREBID_GLOBAL$$.admediaHandler = function(response){ 74 | var bidObject = {}; 75 | var callback_id = response.callback_id; 76 | var placementCode = ''; 77 | var bidObj = getBidRequest(callback_id); 78 | if (bidObj) { 79 | placementCode = bidObj.placementCode; 80 | } 81 | 82 | if(bidObj && response.cpm>0 && !!response.ad){ 83 | bidObject = bidfactory.createBid(CONSTANTS.STATUS.GOOD); 84 | bidObject.bidderCode = bidObj.bidder; 85 | bidObject.cpm = parseFloat(response.cpm); 86 | bidObject.ad = response.ad; 87 | bidObject.width = response.width; 88 | bidObject.height = response.height; 89 | } 90 | else{ 91 | bidObject = bidfactory.createBid(CONSTANTS.STATUS.NO_BID); 92 | bidObject.bidderCode = bidObj.bidder; 93 | utils.logMessage('No prebid response from Admedia for placement code ' + placementCode); 94 | } 95 | 96 | bidmanager.addBidResponse(placementCode, bidObject); 97 | 98 | }; 99 | 100 | 101 | // Export the callBids function, so that prebid.js can execute this function 102 | // when the page asks to send out bid requests. 103 | return { 104 | callBids: _callBids 105 | }; 106 | }; 107 | 108 | module.exports = AdmediaAdapter; 109 | -------------------------------------------------------------------------------- /test/spec/e2e/gpt-examples/e2e_default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 39 | 40 | 41 | 42 | 72 | 73 | 74 | 86 | 87 | 98 | 99 | 100 | 101 | 102 |

Prebid.js TestCase1

103 | 104 | 105 |
106 | 109 |
110 |
111 | 114 |
115 | 116 | 117 | -------------------------------------------------------------------------------- /test/spec/sizeMapping_spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import * as sizeMapping from 'src/sizeMapping'; 3 | 4 | var validAdUnit = { 5 | 'sizes': [300,250], 6 | 'sizeMapping': [ 7 | { 8 | 'minWidth': 1024, 9 | 'sizes': [[300,250],[728,90]] 10 | }, 11 | { 12 | 'minWidth': 480, 13 | 'sizes': [120,60] 14 | }, 15 | { 16 | 'minWidth': 0, 17 | 'sizes': [20,20] 18 | } 19 | ] 20 | }; 21 | 22 | var invalidAdUnit = { 23 | 'sizes': [300,250], 24 | 'sizeMapping': {} // wrong type 25 | }; 26 | 27 | var invalidAdUnit2 = { 28 | 'sizes': [300,250], 29 | 'sizeMapping': [{ 30 | foo : 'bar' //bad 31 | }] 32 | }; 33 | 34 | let mockWindow = {}; 35 | 36 | function resetMockWindow() { 37 | mockWindow = { 38 | document: { 39 | body: { 40 | clientWidth: 1024 41 | }, 42 | documentElement: { 43 | clientWidth: 1024 44 | } 45 | }, 46 | innerWidth: 1024 47 | }; 48 | } 49 | 50 | describe('sizeMapping', function() { 51 | 52 | beforeEach(resetMockWindow); 53 | 54 | it('mapSizes 1029 width', function() { 55 | mockWindow.innerWidth = 1029; 56 | sizeMapping.setWindow(mockWindow); 57 | let sizes = sizeMapping.mapSizes(validAdUnit); 58 | expect(sizes).to.deep.equal([[300,250],[728,90]]); 59 | expect(validAdUnit.sizes).to.deep.equal([300,250]); 60 | }); 61 | 62 | it('mapSizes 400 width', function() { 63 | mockWindow.innerWidth = 400; 64 | sizeMapping.setWindow(mockWindow); 65 | let sizes = sizeMapping.mapSizes(validAdUnit); 66 | expect(sizes).to.deep.equal([20,20]); 67 | expect(validAdUnit.sizes).to.deep.equal([300,250]); 68 | }); 69 | 70 | it('mapSizes - invalid adUnit - should return sizes', function() { 71 | mockWindow.innerWidth = 1029; 72 | sizeMapping.setWindow(mockWindow); 73 | let sizes = sizeMapping.mapSizes(invalidAdUnit); 74 | expect(sizes).to.deep.equal([300,250]); 75 | expect(invalidAdUnit.sizes).to.deep.equal([300,250]); 76 | 77 | mockWindow.innerWidth = 400; 78 | sizeMapping.setWindow(mockWindow); 79 | sizes = sizeMapping.mapSizes(invalidAdUnit); 80 | expect(sizes).to.deep.equal([300,250]); 81 | expect(invalidAdUnit.sizes).to.deep.equal([300,250]); 82 | }); 83 | 84 | it('mapSizes - should return desktop (largest) sizes if screen width not detected', function() { 85 | mockWindow.innerWidth = 0; 86 | mockWindow.document.body.clientWidth = 0; 87 | mockWindow.document.documentElement.clientWidth = 0; 88 | sizeMapping.setWindow(mockWindow); 89 | let sizes = sizeMapping.mapSizes(validAdUnit); 90 | expect(sizes).to.deep.equal([[300, 250], [728, 90]]); 91 | expect(validAdUnit.sizes).to.deep.equal([300,250]); 92 | }); 93 | 94 | 95 | it('mapSizes - should return sizes if sizemapping improperly defined ', function() { 96 | mockWindow.innerWidth = 0; 97 | mockWindow.document.body.clientWidth = 0; 98 | mockWindow.document.documentElement.clientWidth = 0; 99 | sizeMapping.setWindow(mockWindow); 100 | let sizes = sizeMapping.mapSizes(invalidAdUnit2); 101 | expect(sizes).to.deep.equal([300,250]); 102 | expect(validAdUnit.sizes).to.deep.equal([300,250]); 103 | }); 104 | 105 | 106 | it('getScreenWidth', function() { 107 | mockWindow.innerWidth = 900; 108 | mockWindow.document.body.clientWidth = 900; 109 | mockWindow.document.documentElement.clientWidth = 900; 110 | expect(sizeMapping.getScreenWidth(mockWindow)).to.equal(900); 111 | }); 112 | 113 | it('getScreenWidth - should return 0 if it cannot deteremine size', function() { 114 | mockWindow.innerWidth = null; 115 | mockWindow.document.body.clientWidth = null; 116 | mockWindow.document.documentElement.clientWidth = null; 117 | expect(sizeMapping.getScreenWidth(mockWindow)).to.equal(0); 118 | }); 119 | 120 | }); 121 | -------------------------------------------------------------------------------- /src/adapters/centro.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils.js'); 2 | var bidfactory = require('../bidfactory.js'); 3 | var bidmanager = require('../bidmanager.js'); 4 | var adloader = require('../adloader'); 5 | 6 | var CentroAdapter = function CentroAdapter() { 7 | var baseUrl = '//t.brand-server.com/hb', 8 | devUrl = '//staging.brand-server.com/hb', 9 | bidderCode = 'centro', 10 | handlerPrefix = 'adCentroHandler_', 11 | 12 | LOG_ERROR_MESS = { 13 | noUnit: 'Bid has no unit', 14 | noAdTag: 'Bid has missmatch format.', 15 | noBid: 'Response has no bid.', 16 | anotherCode: 'Bid has another bidderCode - ', 17 | undefBid: 'Bid is undefined', 18 | unitNum: 'Requested unit is ' 19 | }; 20 | 21 | function _makeHandler(handlerName, unit, placementCode) { 22 | return function(response){ 23 | try { 24 | delete window[handlerName]; 25 | } catch(err) {//catching for old IE 26 | window[handlerName] = undefined; 27 | } 28 | _responseProcessing(response, unit, placementCode); 29 | }; 30 | } 31 | 32 | function _sendBidRequest(bid) { 33 | var placementCode = bid.placementCode, 34 | size = bid.sizes && bid.sizes[0]; 35 | 36 | bid = bid.params; 37 | if (!bid.unit) { 38 | //throw exception, or call utils.logError 39 | utils.logError(LOG_ERROR_MESS.noUnit, bidderCode); 40 | return; 41 | } 42 | var query = ['s=' + bid.unit];//,'url=www.abc15.com','sz=320x50']; 43 | var isDev = bid.unit.toString() === '28136'; 44 | 45 | if (bid.page_url) { 46 | query.push('url=' + encodeURIComponent(bid.page_url)); 47 | } 48 | //check size format 49 | if ( 50 | size instanceof Array && 51 | size.length===2 && 52 | typeof size[0] === 'number' && 53 | typeof size[1] === 'number' 54 | ) { 55 | query.push('sz=' + size.join('x')); 56 | } 57 | //make handler name for JSONP request 58 | var handlerName = handlerPrefix + bid.unit + size.join('x'); 59 | query.push('callback=' + handlerName); 60 | 61 | //maybe is needed add some random parameter to disable cache 62 | //query.push('r='+Math.round(Math.random() * 1e5)); 63 | 64 | window[handlerName] = _makeHandler(handlerName, bid.unit, placementCode); 65 | 66 | adloader.loadScript((document.location.protocol === 'https:'? 'https:' : 'http:') + (isDev? devUrl : baseUrl) + '?' + query.join('&')); 67 | } 68 | 69 | /* 70 | "sectionID": 7302, 71 | "height": 250, 72 | "width": 300, 73 | "value": 3.2, 74 | "adTag":'' 75 | */ 76 | function _responseProcessing(resp, unit, placementCode) { 77 | var bidObject; 78 | var bid = resp && resp.bid || resp; 79 | 80 | if (bid && bid.adTag && bid.sectionID === unit) { 81 | bidObject = bidfactory.createBid(1); 82 | bidObject.cpm = bid.value; 83 | bidObject.ad = bid.adTag; 84 | bidObject.width = bid.width; 85 | bidObject.height = bid.height; 86 | } else { 87 | //throw exception, or call utils.logError with resp.statusMessage 88 | utils.logError(LOG_ERROR_MESS.unitNum + unit + '. ' + (bid? bid.statusMessage || LOG_ERROR_MESS.noAdTag : LOG_ERROR_MESS.noBid), bidderCode); 89 | bidObject = bidfactory.createBid(2); 90 | } 91 | bidObject.bidderCode = bidderCode; 92 | bidmanager.addBidResponse(placementCode, bidObject); 93 | } 94 | 95 | /* 96 | { 97 | bidderCode: "centro", 98 | bids: [ 99 | { 100 | unit: '3242432', 101 | page_url: "http://", 102 | size: [300, 250] 103 | */ 104 | function _callBids(params) { 105 | var bid, bids = params.bids || []; 106 | for (var i = 0; i < bids.length; i++) { 107 | bid = bids[i]; 108 | if (bid && bid.bidder === bidderCode) { 109 | _sendBidRequest(bid); 110 | } 111 | } 112 | } 113 | 114 | return { 115 | callBids: _callBids 116 | }; 117 | }; 118 | 119 | 120 | module.exports = CentroAdapter; 121 | -------------------------------------------------------------------------------- /test/spec/adapters/rhythmone_spec.js: -------------------------------------------------------------------------------- 1 | var r1 = require('../../../src/adapters/rhythmone.js'); 2 | var assert = require('assert'); 3 | 4 | describe('rhythmone adapter tests', function () { 5 | describe('rhythmoneResponse', function () { 6 | 7 | var fakeResponse = { 8 | "id": "1fe94c2e-4b31-4e09-b074-ba90fe7ce92d", 9 | "seatbid": [ 10 | { 11 | "bid": [ 12 | { 13 | "id": "ff8b09b1-5264-52be-4b7b-0156526452bf", 14 | "impid": "div-gpt-ad-1438287399331-0", 15 | "price": 1.0, 16 | "adid": "35858", 17 | "adm": "\"\"", 18 | "adomain": ["www.rhythmone.com"], 19 | "cid": "35857", 20 | "cat": [], 21 | "h": 250, 22 | "w": 300 23 | } 24 | ], 25 | "seat": "14", 26 | "group": 0 27 | } 28 | ], 29 | "bidid": "ff8b09b1-5264-52be-4b7b-0156526452bf" 30 | }; 31 | 32 | var endEvent = function(){}, 33 | wonEvent = function(){};; 34 | 35 | var z = new r1( 36 | { 37 | addBidResponse: function(placementcode, adResponse){ 38 | it("should echo placementcode div-gpt-ad-1438287399331-0", function(){ 39 | assert.equal(placementcode, "div-gpt-ad-1438287399331-0"); 40 | }); 41 | it("should have the expected ad response", function(){ 42 | assert.equal((adResponse.ad.length > 0), true); 43 | assert.equal(adResponse.width, 300); 44 | assert.equal(adResponse.height, 250); 45 | assert.equal(adResponse.cpm, 1); 46 | assert.equal(adResponse.bidderCode, "rhythmone"); 47 | }); 48 | } 49 | }, 50 | { 51 | "navigator":{}, 52 | "pbjs":{ 53 | "onEvent":function(e, f){ 54 | if(e.toLowerCase() === "auctionend") endEvent = f; 55 | if(e.toLowerCase() === "bidwon") wonEvent = f; 56 | }, 57 | "getBidResponses":function(){return {"div-gpt-ad-1438287399331-0":{"bids":[{cpm:1,bidderCode:"rhythmone"},{cpm:2,bidderCode:"rhythmone"}]}};} 58 | } 59 | }, 60 | function(url, callback){ 61 | callback(JSON.stringify(fakeResponse), {status:200, responseText: JSON.stringify(fakeResponse)}); 62 | }); 63 | 64 | z.callBids({ 65 | "bidderCode":"rhythmone", 66 | "bids":[ 67 | { 68 | "bidder":"rhythmone", 69 | "params":{ 70 | "placementId":"xyz", 71 | "keywords":"", 72 | "categories":[], 73 | "trace":true, 74 | "method":"get", 75 | "endpoint":"http://fakedomain.com" 76 | }, 77 | "placementCode":"div-gpt-ad-1438287399331-0", 78 | "sizes":[[300,250]] 79 | } 80 | ] 81 | }); 82 | 83 | endEvent(); 84 | wonEvent({ 85 | bidderCode: "rhythmone", 86 | adUnitCode: "div-gpt-ad-1438287399331-0" 87 | }); 88 | }); 89 | }); -------------------------------------------------------------------------------- /src/adapters/analytics/AnalyticsAdapter.js: -------------------------------------------------------------------------------- 1 | import CONSTANTS from 'src/constants.json'; 2 | import { loadScript } from 'src/adloader'; 3 | import { ajax } from 'src/ajax'; 4 | 5 | const events = require('src/events'); 6 | const utils = require('../../utils'); 7 | 8 | const AUCTION_INIT = CONSTANTS.EVENTS.AUCTION_INIT; 9 | const BID_REQUESTED = CONSTANTS.EVENTS.BID_REQUESTED; 10 | const BID_TIMEOUT = CONSTANTS.EVENTS.BID_TIMEOUT; 11 | const BID_RESPONSE = CONSTANTS.EVENTS.BID_RESPONSE; 12 | const BID_WON = CONSTANTS.EVENTS.BID_WON; 13 | const BID_ADJUSTMENT = CONSTANTS.EVENTS.BID_ADJUSTMENT; 14 | 15 | const LIBRARY = 'library'; 16 | const ENDPOINT = 'endpoint'; 17 | const BUNDLE = 'bundle'; 18 | 19 | var _timedOutBidders = []; 20 | 21 | export default function AnalyticsAdapter({ url, analyticsType, global, handler }) { 22 | var _queue = []; 23 | var _eventCount = 0; 24 | var _enableCheck = true; 25 | 26 | if (analyticsType === LIBRARY) { 27 | loadScript(url, _emptyQueue); 28 | } 29 | 30 | if (analyticsType === ENDPOINT || BUNDLE) { 31 | _emptyQueue(); 32 | } 33 | 34 | return { 35 | track: _track, 36 | enqueue: _enqueue, 37 | enableAnalytics: _enable, 38 | getAdapterType: () => analyticsType, 39 | getGlobal: () => global, 40 | getHandler: () => handler, 41 | getUrl: () => url 42 | }; 43 | 44 | function _track({ eventType, args }) { 45 | if (this.getAdapterType() === LIBRARY || BUNDLE) { 46 | window[global](handler, eventType, args); 47 | } 48 | 49 | if (this.getAdapterType() === ENDPOINT) { 50 | _callEndpoint(...arguments); 51 | } 52 | } 53 | 54 | function _callEndpoint({ eventType, args, callback }) { 55 | ajax(url, callback, JSON.stringify({ eventType, args })); 56 | } 57 | 58 | function _enqueue({ eventType, args }) { 59 | const _this = this; 60 | 61 | if (global && window[global] && eventType && args) { 62 | this.track({ eventType, args }); 63 | } else { 64 | _queue.push(function () { 65 | _eventCount++; 66 | _this.track({ eventType, args }); 67 | }); 68 | } 69 | } 70 | 71 | function _enable(config) { 72 | var _this = this; 73 | 74 | //first send all events fired before enableAnalytics called 75 | events.getEvents().forEach(event => { 76 | if (!event) { 77 | return; 78 | } 79 | 80 | const { eventType, args } = event; 81 | 82 | if (eventType === BID_TIMEOUT) { 83 | _timedOutBidders = args.bidderCode; 84 | } else { 85 | _enqueue.call(_this, { eventType, args }); 86 | } 87 | }); 88 | 89 | //Next register event listeners to send data immediately 90 | 91 | //bidRequests 92 | events.on(BID_REQUESTED, args => this.enqueue({ eventType: BID_REQUESTED, args })); 93 | events.on(BID_RESPONSE, args => this.enqueue({ eventType: BID_RESPONSE, args })); 94 | events.on(BID_TIMEOUT, args => this.enqueue({ eventType: BID_TIMEOUT, args })); 95 | events.on(BID_WON, args => this.enqueue({ eventType: BID_WON, args })); 96 | events.on(BID_ADJUSTMENT, args => this.enqueue({ eventType: BID_ADJUSTMENT, args })); 97 | events.on(AUCTION_INIT, args => { 98 | args.config = config.options; // enableAnaltyics configuration object 99 | this.enqueue({ eventType: AUCTION_INIT, args }); 100 | }); 101 | 102 | // finally set this function to return log message, prevents multiple adapter listeners 103 | this.enableAnalytics = function _enable() { 104 | return utils.logMessage(`Analytics adapter for "${global}" already enabled, unnecessary call to \`enableAnalytics\`.`); 105 | }; 106 | } 107 | 108 | function _emptyQueue() { 109 | if (_enableCheck) { 110 | for (var i = 0; i < _queue.length; i++) { 111 | _queue[i](); 112 | } 113 | 114 | //override push to execute the command immediately from now on 115 | _queue.push = function (fn) { 116 | fn(); 117 | }; 118 | 119 | _enableCheck = false; 120 | } 121 | 122 | utils.logMessage(`event count sent to ${global}: ${_eventCount}`); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/adapters/springserve.js: -------------------------------------------------------------------------------- 1 | var bidfactory = require('../bidfactory.js'); 2 | var bidmanager = require('../bidmanager.js'); 3 | var adloader = require('../adloader'); 4 | 5 | var SpringServeAdapter; 6 | SpringServeAdapter = function SpringServeAdapter() { 7 | 8 | function buildSpringServeCall(bid) { 9 | 10 | var spCall = window.location.protocol + '//bidder.springserve.com/display/hbid?'; 11 | 12 | //get width and height from bid attribute 13 | var size = bid.sizes[0]; 14 | var width = size[0]; 15 | var height = size[1]; 16 | 17 | spCall += '&w='; 18 | spCall += width; 19 | spCall += '&h='; 20 | spCall += height; 21 | 22 | var params = bid.params; 23 | 24 | //maps param attributes to request parameters 25 | var requestAttrMap = { 26 | sp: 'supplyPartnerId', 27 | imp_id: 'impId' 28 | }; 29 | 30 | for (var property in requestAttrMap) { 31 | if (requestAttrMap.hasOwnProperty && params.hasOwnProperty(requestAttrMap[property])) { 32 | spCall += '&'; 33 | spCall += property; 34 | spCall += '='; 35 | 36 | //get property from params and include it in request 37 | spCall += params[requestAttrMap[property]]; 38 | } 39 | } 40 | 41 | var domain = window.location.hostname; 42 | 43 | //override domain when testing 44 | if (params.hasOwnProperty('test') && params.test === true) { 45 | spCall += '&debug=true'; 46 | domain = 'test.com'; 47 | } 48 | 49 | spCall += '&domain='; 50 | spCall += domain; 51 | spCall += '&callback=$$PREBID_GLOBAL$$.handleSpringServeCB'; 52 | 53 | return spCall; 54 | } 55 | 56 | function _callBids(params) { 57 | var bids = params.bids || []; 58 | for (var i = 0; i < bids.length; i++) { 59 | var bid = bids[i]; 60 | //bidmanager.pbCallbackMap[bid.params.impId] = params; 61 | adloader.loadScript(buildSpringServeCall(bid)); 62 | } 63 | } 64 | 65 | $$PREBID_GLOBAL$$.handleSpringServeCB = function (responseObj) { 66 | if (responseObj && responseObj.seatbid && responseObj.seatbid.length > 0 && 67 | responseObj.seatbid[0].bid[0] !== undefined) { 68 | //look up the request attributs stored in the bidmanager 69 | var responseBid = responseObj.seatbid[0].bid[0]; 70 | //var requestObj = bidmanager.getPlacementIdByCBIdentifer(responseBid.impid); 71 | var requestBids = $$PREBID_GLOBAL$$._bidsRequested.find(bidSet => bidSet.bidderCode === 'springserve'); 72 | if (requestBids && requestBids.bids.length > 0) { 73 | requestBids = requestBids.bids.filter(bid => bid.params && bid.params.impId === +responseBid.impid); 74 | } else { 75 | requestBids = []; 76 | } 77 | var bid = bidfactory.createBid(1); 78 | var placementCode; 79 | 80 | //assign properties from the original request to the bid object 81 | for (var i = 0; i < requestBids.length; i++) { 82 | var bidRequest = requestBids[i]; 83 | if (bidRequest.bidder === 'springserve') { 84 | placementCode = bidRequest.placementCode; 85 | var size = bidRequest.sizes[0]; 86 | bid.width = size[0]; 87 | bid.height = size[1]; 88 | } 89 | } 90 | 91 | if (requestBids[0]) {bid.bidderCode = requestBids[0].bidder;} 92 | 93 | if (responseBid.hasOwnProperty('price') && responseBid.hasOwnProperty('adm')) { 94 | //assign properties from the response to the bid object 95 | bid.cpm = responseBid.price; 96 | bid.ad = responseBid.adm; 97 | } else { 98 | //make object for invalid bid response 99 | bid = bidfactory.createBid(2); 100 | bid.bidderCode = 'springserve'; 101 | } 102 | 103 | bidmanager.addBidResponse(placementCode, bid); 104 | } 105 | }; 106 | 107 | // Export the callBids function, so that prebid.js can execute this function 108 | // when the page asks to send out bid requests. 109 | return { 110 | callBids: _callBids, 111 | buildSpringServeCall: buildSpringServeCall 112 | }; 113 | }; 114 | 115 | module.exports = SpringServeAdapter; 116 | -------------------------------------------------------------------------------- /src/adapters/fidelity.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils.js'); 2 | var bidfactory = require('../bidfactory.js'); 3 | var bidmanager = require('../bidmanager.js'); 4 | var adloader = require('../adloader'); 5 | var STATUS = require('../constants').STATUS; 6 | 7 | var FidelityAdapter = function FidelityAdapter() { 8 | var FIDELITY_BIDDER_NAME = 'fidelity'; 9 | var FIDELITY_SERVER_NAME = 'x.fidelity-media.com'; 10 | 11 | function _callBids(params) { 12 | var bids = params.bids || []; 13 | bids.forEach(function (currentBid) { 14 | var server = currentBid.params.server || FIDELITY_SERVER_NAME; 15 | var m3_u = window.location.protocol + '//'+server+'/delivery/hb.php?'; 16 | m3_u += 'callback=window.$$PREBID_GLOBAL$$.fidelityResponse'; 17 | m3_u += '&requestid='+utils.getUniqueIdentifierStr(); 18 | m3_u += '&impid='+currentBid.bidId; 19 | m3_u += '&zoneid='+currentBid.params.zoneid; 20 | m3_u += '&cb='+Math.floor(Math.random()*99999999999); 21 | m3_u += document.charset ? '&charset='+document.charset : (document.characterSet ? '&charset='+document.characterSet : ''); 22 | 23 | var loc; 24 | try { 25 | loc = window.top !== window ? document.referrer : window.location.href; 26 | } catch (e) { 27 | loc = document.referrer; 28 | } 29 | loc = currentBid.params.loc || loc; 30 | m3_u += '&loc=' + encodeURIComponent(loc); 31 | 32 | var subid = currentBid.params.subid || 'hb'; 33 | m3_u += '&subid=' + subid; 34 | if (document.referrer) m3_u += '&referer=' + encodeURIComponent(document.referrer); 35 | if (currentBid.params.click) m3_u += '&ct0=' + encodeURIComponent(currentBid.params.click); 36 | m3_u += '&flashver=' + encodeURIComponent(getFlashVersion()); 37 | 38 | adloader.loadScript(m3_u); 39 | }); 40 | } 41 | 42 | function getFlashVersion(){ 43 | var plugins, plugin, result; 44 | 45 | if (navigator.plugins && navigator.plugins.length > 0) { 46 | plugins = navigator.plugins; 47 | for (var i = 0; i < plugins.length && !result; i++) { 48 | plugin = plugins[i]; 49 | if (plugin.name.indexOf("Shockwave Flash") > -1) { 50 | result = plugin.description.split("Shockwave Flash ")[1]; 51 | } 52 | } 53 | } 54 | return result ? result : ""; 55 | } 56 | 57 | function addBlankBidResponses(placementsWithBidsBack) { 58 | var allFidelityBidRequests = $$PREBID_GLOBAL$$._bidsRequested.find(bidSet => bidSet.bidderCode === FIDELITY_BIDDER_NAME); 59 | 60 | if (allFidelityBidRequests && allFidelityBidRequests.bids){ 61 | utils._each(allFidelityBidRequests.bids, function (fidelityBid) { 62 | if (!utils.contains(placementsWithBidsBack, fidelityBid.placementCode)) { 63 | // Add a no-bid response for this placement. 64 | var bid = bidfactory.createBid(STATUS.NO_BID, fidelityBid); 65 | bid.bidderCode = FIDELITY_BIDDER_NAME; 66 | bidmanager.addBidResponse(fidelityBid.placementCode, bid); 67 | } 68 | }); 69 | } 70 | } 71 | 72 | $$PREBID_GLOBAL$$.fidelityResponse = function(responseObj) { 73 | 74 | if (!responseObj || !responseObj.seatbid || responseObj.seatbid.length === 0 || !responseObj.seatbid[0].bid || responseObj.seatbid[0].bid.length === 0) { 75 | addBlankBidResponses([]); 76 | return; 77 | } 78 | 79 | var bid = responseObj.seatbid[0].bid[0]; 80 | var status = bid.adm ? STATUS.GOOD : STATUS.NO_BID; 81 | var requestObj = utils.getBidRequest(bid.impid); 82 | 83 | var bidResponse = bidfactory.createBid(status); 84 | bidResponse.bidderCode = FIDELITY_BIDDER_NAME; 85 | if (status === STATUS.GOOD) { 86 | bidResponse.cpm = parseFloat(bid.price); 87 | bidResponse.ad = bid.adm; 88 | bidResponse.width = parseInt(bid.width); 89 | bidResponse.height = parseInt(bid.height); 90 | } 91 | var placementCode = requestObj && requestObj.placementCode; 92 | bidmanager.addBidResponse(placementCode, bidResponse); 93 | }; 94 | 95 | return { 96 | callBids: _callBids 97 | }; 98 | }; 99 | 100 | module.exports = FidelityAdapter; -------------------------------------------------------------------------------- /src/adapters/openx.js: -------------------------------------------------------------------------------- 1 | // jshint ignore:start 2 | var bidfactory = require('../bidfactory.js'); 3 | var bidmanager = require('../bidmanager.js'); 4 | var adloader = require('../adloader'); 5 | 6 | /** 7 | * Adapter for requesting bids from OpenX. 8 | * 9 | * @param {Object} options - Configuration options for OpenX 10 | * @param {string} options.pageURL - Current page URL to send with bid request 11 | * @param {string} options.refererURL - Referer URL to send with bid request 12 | * 13 | * @returns {{callBids: _callBids}} 14 | * @constructor 15 | */ 16 | var OpenxAdapter = function OpenxAdapter(options) { 17 | 18 | var opts = options || {}; 19 | var scriptUrl; 20 | var bids; 21 | 22 | function _callBids(params) { 23 | bids = params.bids || []; 24 | for (var i = 0; i < bids.length; i++) { 25 | var bid = bids[i]; 26 | 27 | //load page options from bid request 28 | if (bid.params.pageURL) { 29 | opts.pageURL = bid.params.pageURL; 30 | } 31 | 32 | if (bid.params.refererURL) { 33 | opts.refererURL = bid.params.refererURL; 34 | } 35 | 36 | if (bid.params.jstag_url) { 37 | scriptUrl = bid.params.jstag_url; 38 | } 39 | 40 | if (bid.params.pgid) { 41 | opts.pgid = bid.params.pgid; 42 | } 43 | } 44 | 45 | _requestBids(); 46 | } 47 | 48 | function _requestBids() { 49 | 50 | if (scriptUrl) { 51 | adloader.loadScript(scriptUrl, function () { 52 | var i; 53 | var POX = OX(); 54 | 55 | if (opts.pageURL) { 56 | POX.setPageURL(opts.pageURL); 57 | } 58 | 59 | if (opts.refererURL) { 60 | POX.setRefererURL(opts.refererURL); 61 | } 62 | 63 | if (opts.pgid) { 64 | POX.addPage(opts.pgid); 65 | } 66 | 67 | // Add each ad unit ID 68 | for (i = 0; i < bids.length; i++) { 69 | POX.addAdUnit(bids[i].params.unit); 70 | } 71 | 72 | POX.addHook(function (response) { 73 | var i; 74 | var bid; 75 | var adUnit; 76 | var adResponse; 77 | 78 | // Map each bid to its response 79 | for (i = 0; i < bids.length; i++) { 80 | bid = bids[i]; 81 | 82 | // Get ad response 83 | adUnit = response.getOrCreateAdUnit(bid.params.unit); 84 | // If 'pub_rev' (CPM) isn't returned we got an empty response 85 | if (adUnit.get('pub_rev')) { 86 | adResponse = adResponse = bidfactory.createBid(1); 87 | 88 | adResponse.bidderCode = 'openx'; 89 | adResponse.ad_id = adUnit.get('ad_id'); 90 | adResponse.cpm = Number(adUnit.get('pub_rev')) / 1000; 91 | 92 | adResponse.ad = adUnit.get('html'); 93 | if(adUnit.get('deal_id') !== undefined) { 94 | adResponse.dealId = adUnit.get('deal_id'); 95 | } 96 | 97 | // Add record/impression pixel to the creative HTML 98 | var recordPixel = OX.utils.template(response.getRecordTemplate(), { 99 | medium: OX.utils.getMedium(), 100 | rtype: OX.Resources.RI, 101 | txn_state: adUnit.get('ts') 102 | }); 103 | adResponse.ad += '
'; 104 | 105 | adResponse.adUrl = adUnit.get('ad_url'); 106 | adResponse.width = adUnit.get('width'); 107 | adResponse.height = adUnit.get('height'); 108 | 109 | bidmanager.addBidResponse(bid.placementCode, adResponse); 110 | } else { 111 | // Indicate an ad was not returned 112 | adResponse = bidfactory.createBid(2); 113 | adResponse.bidderCode = 'openx'; 114 | bidmanager.addBidResponse(bid.placementCode, adResponse); 115 | } 116 | } 117 | }, OX.Hooks.ON_AD_RESPONSE); 118 | 119 | // Make request 120 | POX.load(); 121 | }, true); 122 | } 123 | } 124 | 125 | return { 126 | callBids: _callBids 127 | }; 128 | }; 129 | 130 | module.exports = OpenxAdapter; 131 | -------------------------------------------------------------------------------- /test/fixtures/allAdapters.js: -------------------------------------------------------------------------------- 1 | exports.getAllAdaptersString = function () { 2 | return `var AardvarkAdapter = require(\'./adapters/aardvark.js\');\n exports.registerBidAdapter(new AardvarkAdapter(), \'aardvark\');\nvar AdbladeAdapter = require(\'./adapters/adblade.js\');\n exports.registerBidAdapter(new AdbladeAdapter(), \'adblade\');\nvar AdbutlerAdapter = require(\'./adapters/adbutler.js\');\n exports.registerBidAdapter(new AdbutlerAdapter(), \'adbutler\');\nvar AdequantAdapter = require(\'./adapters/adequant.js\');\n exports.registerBidAdapter(new AdequantAdapter(), \'adequant\');\nvar AdformAdapter = require(\'./adapters/adform.js\');\n exports.registerBidAdapter(new AdformAdapter(), \'adform\');\nvar AdmediaAdapter = require(\'./adapters/admedia.js\');\n exports.registerBidAdapter(new AdmediaAdapter(), \'admedia\');\nvar AolAdapter = require(\'./adapters/aol.js\');\n exports.registerBidAdapter(new AolAdapter(), \'aol\');\nvar AppnexusAdapter = require(\'./adapters/appnexus.js\');\n exports.registerBidAdapter(new AppnexusAdapter(), \'appnexus\');\nvar AppnexusAstAdapter = require(\'./adapters/appnexusAst.js\');\n exports.registerBidAdapter(new AppnexusAstAdapter(), \'appnexusAst\');\nvar GetintentAdapter = require(\'./adapters/getintent.js\');\n exports.registerBidAdapter(new GetintentAdapter(), \'getintent\');\nvar HiromediaAdapter = require(\'./adapters/hiromedia.js\');\n exports.registerBidAdapter(new HiromediaAdapter(), \'hiromedia\');\nvar IndexExchangeAdapter = require(\'./adapters/indexExchange.js\');\n exports.registerBidAdapter(new IndexExchangeAdapter(), \'indexExchange\');\nvar KruxlinkAdapter = require(\'./adapters/kruxlink.js\');\n exports.registerBidAdapter(new KruxlinkAdapter(), \'kruxlink\');\nvar KomoonaAdapter = require(\'./adapters/komoona.js\');\n exports.registerBidAdapter(new KomoonaAdapter(), \'komoona\');\nvar OpenxAdapter = require(\'./adapters/openx.js\');\n exports.registerBidAdapter(new OpenxAdapter(), \'openx\');\nvar PiximediaAdapter = require(\'./adapters/piximedia.js\');\n exports.registerBidAdapter(new PiximediaAdapter(), \'piximedia\');\nvar PubmaticAdapter = require(\'./adapters/pubmatic.js\');\n exports.registerBidAdapter(new PubmaticAdapter(), \'pubmatic\');\nvar PulsepointAdapter = require(\'./adapters/pulsepoint.js\');\n exports.registerBidAdapter(new PulsepointAdapter(), \'pulsepoint\');\nvar RubiconAdapter = require(\'./adapters/rubicon.js\');\n exports.registerBidAdapter(new RubiconAdapter(), \'rubicon\');\nvar SonobiAdapter = require(\'./adapters/sonobi.js\');\n exports.registerBidAdapter(new SonobiAdapter(), \'sonobi\');\nvar SovrnAdapter = require(\'./adapters/sovrn.js\');\n exports.registerBidAdapter(new SovrnAdapter(), \'sovrn\');\nvar SpringserveAdapter = require(\'./adapters/springserve.js\');\n exports.registerBidAdapter(new SpringserveAdapter(), \'springserve\');\nvar TripleliftAdapter = require(\'./adapters/triplelift.js\');\n exports.registerBidAdapter(new TripleliftAdapter(), \'triplelift\');\nvar YieldbotAdapter = require(\'./adapters/yieldbot.js\');\n exports.registerBidAdapter(new YieldbotAdapter(), \'yieldbot\');\nvar NginadAdapter = require(\'./adapters/nginad.js\');\n exports.registerBidAdapter(new NginadAdapter(), \'nginad\');\nvar BrightcomAdapter = require(\'./adapters/brightcom.js\');\n exports.registerBidAdapter(new BrightcomAdapter(), \'brightcom\');\nvar WideorbitAdapter = require(\'./adapters/wideorbit.js\');\n exports.registerBidAdapter(new WideorbitAdapter(), \'wideorbit\');\nvar JcmAdapter = require(\'./adapters/jcm.js\');\n exports.registerBidAdapter(new JcmAdapter(), \'jcm\');\nvar UnderdogmediaAdapter = require(\'./adapters/underdogmedia.js\');\n exports.registerBidAdapter(new UnderdogmediaAdapter(), \'underdogmedia\');\nvar MemeglobalAdapter = require(\'./adapters/memeglobal.js\');\n exports.registerBidAdapter(new MemeglobalAdapter(), \'memeglobal\');\nvar CentroAdapter = require(\'./adapters/centro.js\');\n exports.registerBidAdapter(new CentroAdapter(), \'centro\');\nvar RoxotAdapter = require(\'./adapters/roxot.js\');\n exports.registerBidAdapter(new RoxotAdapter(), \'roxot\');\nexports.aliasBidAdapter(\'appnexus\',\'brealtime\');\nexports.aliasBidAdapter(\'appnexus\',\'pagescience\');\nexports.aliasBidAdapter(\'appnexus\',\'defymedia\');\nexports.videoAdapters = ["appnexusAst"];`; 3 | }; 4 | -------------------------------------------------------------------------------- /src/adapters/gumgum.js: -------------------------------------------------------------------------------- 1 | const bidfactory = require('../bidfactory'); 2 | const bidmanager = require('../bidmanager'); 3 | const utils = require('../utils'); 4 | const adloader = require('../adloader'); 5 | 6 | const BIDDER_CODE = 'gumgum'; 7 | const CALLBACKS = {}; 8 | 9 | const GumgumAdapter = function GumgumAdapter() { 10 | 11 | const bidEndpoint = `https://g2.gumgum.com/hbid/imp`; 12 | 13 | let WINDOW; 14 | let SCREEN; 15 | 16 | try { 17 | WINDOW = global.top; 18 | SCREEN = WINDOW.screen; 19 | } catch (error) { 20 | utils.logError(error); 21 | return; 22 | } 23 | 24 | function _callBids({ bids }) { 25 | const browserParams = { 26 | vw: WINDOW.innerWidth, 27 | vh: WINDOW.innerHeight, 28 | sw: SCREEN.width, 29 | sh: SCREEN.height, 30 | pu: WINDOW.location.href, 31 | dpr: WINDOW.devicePixelRatio || 1 32 | }; 33 | utils._each(bids, bidRequest => { 34 | const { bidId 35 | , params = {} 36 | , placementCode 37 | } = bidRequest; 38 | const trackingId = params.inScreen; 39 | const nativeId = params.native; 40 | const slotId = params.inSlot; 41 | const bid = {}; 42 | 43 | /* slot/native ads need the placement id */ 44 | switch (true) { 45 | case !!(params.inImage): bid.pi = 1; break; 46 | case !!(params.inScreen): bid.pi = 2; break; 47 | case !!(params.inSlot): bid.pi = 3; break; 48 | case !!(params.native): bid.pi = 5; break; 49 | default: return utils.logWarn( 50 | `[GumGum] No product selected for the placement ${placementCode}` + 51 | ', please check your implementation.' 52 | ); 53 | } 54 | /* tracking id is required for in-image and in-screen */ 55 | if (trackingId) bid.t = trackingId; 56 | /* native ads require a native placement id */ 57 | if (nativeId) bid.ni = nativeId; 58 | /* slot ads require a slot id */ 59 | if (slotId) bid.si = slotId; 60 | 61 | const cachedBid = Object.assign({ 62 | placementCode, 63 | id: bidId 64 | }, bid); 65 | 66 | const callback = { jsonp: `$$PREBID_GLOBAL$$.handleGumGumCB['${ bidId }']` }; 67 | CALLBACKS[bidId] = _handleGumGumResponse(cachedBid); 68 | const query = Object.assign(callback, browserParams, bid); 69 | const bidCall = `${bidEndpoint}?${utils.parseQueryStringParameters(query)}`; 70 | adloader.loadScript(bidCall); 71 | }); 72 | } 73 | 74 | const _handleGumGumResponse = cachedBidRequest => bidResponse => { 75 | const ad = bidResponse && bidResponse.ad; 76 | if (ad && ad.id) { 77 | const bid = bidfactory.createBid(1); 78 | const { t: trackingId 79 | , pi: productId 80 | , placementCode 81 | } = cachedBidRequest; 82 | bidResponse.placementCode = placementCode; 83 | const encodedResponse = encodeURIComponent(JSON.stringify(bidResponse)); 84 | const gumgumAdLoader = ``; 98 | Object.assign(bid, { 99 | cpm: ad.price, 100 | ad: gumgumAdLoader, 101 | width: ad.width, 102 | height: ad.height, 103 | bidderCode: BIDDER_CODE 104 | }); 105 | bidmanager.addBidResponse(cachedBidRequest.placementCode, bid); 106 | } else { 107 | const noBid = bidfactory.createBid(2); 108 | noBid.bidderCode = BIDDER_CODE; 109 | bidmanager.addBidResponse(cachedBidRequest.placementCode, noBid); 110 | } 111 | delete CALLBACKS[cachedBidRequest.id]; 112 | }; 113 | 114 | window.$$PREBID_GLOBAL$$.handleGumGumCB = CALLBACKS; 115 | 116 | return { 117 | callBids: _callBids 118 | }; 119 | 120 | }; 121 | 122 | module.exports = GumgumAdapter; 123 | -------------------------------------------------------------------------------- /src/adapters/triplelift.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils.js'); 2 | var adloader = require('../adloader.js'); 3 | var bidmanager = require('../bidmanager.js'); 4 | var bidfactory = require('../bidfactory.js'); 5 | 6 | /* TripleLift bidder factory function 7 | * Use to create a TripleLiftAdapter object 8 | */ 9 | 10 | 11 | var TripleLiftAdapter = function TripleLiftAdapter() { 12 | 13 | function _callBids(params) { 14 | var tlReq = params.bids; 15 | var bidsCount = tlReq.length; 16 | 17 | //set expected bids count for callback execution 18 | //bidmanager.setExpectedBidsCount('triplelift',bidsCount); 19 | 20 | for (var i = 0; i < bidsCount; i++) { 21 | var bidRequest = tlReq[i]; 22 | var callbackId = bidRequest.bidId; 23 | adloader.loadScript(buildTLCall(bidRequest, callbackId)); 24 | //store a reference to the bidRequest from the callback id 25 | //bidmanager.pbCallbackMap[callbackId] = bidRequest; 26 | } 27 | 28 | } 29 | 30 | 31 | function buildTLCall(bid, callbackId) { 32 | //determine tag params 33 | var inventoryCode = utils.getBidIdParameter('inventoryCode', bid.params); 34 | var floor = utils.getBidIdParameter('floor', bid.params); 35 | 36 | 37 | //build our base tag, based on if we are http or https 38 | var tlURI = '//tlx.3lift.com/header/auction?'; 39 | var tlCall = document.location.protocol + tlURI; 40 | 41 | tlCall = utils.tryAppendQueryString(tlCall, 'callback', '$$PREBID_GLOBAL$$.TLCB'); 42 | tlCall = utils.tryAppendQueryString(tlCall, 'lib', 'prebid'); 43 | tlCall = utils.tryAppendQueryString(tlCall, 'v', '$prebid.version$'); 44 | tlCall = utils.tryAppendQueryString(tlCall, 'callback_id', callbackId); 45 | tlCall = utils.tryAppendQueryString(tlCall, 'inv_code', inventoryCode); 46 | tlCall = utils.tryAppendQueryString(tlCall, 'floor', floor); 47 | 48 | //sizes takes a bit more logic 49 | var sizeQueryString = utils.parseSizesInput(bid.sizes); 50 | if (sizeQueryString) { 51 | tlCall += 'size=' + sizeQueryString + '&'; 52 | } 53 | 54 | //append referrer 55 | var referrer = utils.getTopWindowUrl(); 56 | tlCall = utils.tryAppendQueryString(tlCall, 'referrer', referrer); 57 | 58 | //remove the trailing "&" 59 | if (tlCall.lastIndexOf('&') === tlCall.length - 1) { 60 | tlCall = tlCall.substring(0, tlCall.length - 1); 61 | } 62 | 63 | // @if NODE_ENV='debug' 64 | utils.logMessage('tlCall request built: ' + tlCall); 65 | // @endif 66 | 67 | //append a timer here to track latency 68 | bid.startTime = new Date().getTime(); 69 | 70 | return tlCall; 71 | 72 | } 73 | 74 | 75 | //expose the callback to the global object: 76 | $$PREBID_GLOBAL$$.TLCB = function(tlResponseObj) { 77 | if (tlResponseObj && tlResponseObj.callback_id) { 78 | var bidObj = utils.getBidRequest(tlResponseObj.callback_id); 79 | var placementCode = bidObj && bidObj.placementCode; 80 | 81 | // @if NODE_ENV='debug' 82 | if (bidObj) {utils.logMessage('JSONP callback function called for inventory code: ' + bidObj.params.inventoryCode);} 83 | // @endif 84 | 85 | var bid = []; 86 | if (tlResponseObj && tlResponseObj.cpm && tlResponseObj.cpm !== 0) { 87 | 88 | bid = bidfactory.createBid(1, bidObj); 89 | bid.bidderCode = 'triplelift'; 90 | bid.cpm = tlResponseObj.cpm; 91 | bid.ad = tlResponseObj.ad; 92 | bid.width = tlResponseObj.width; 93 | bid.height = tlResponseObj.height; 94 | bid.dealId = tlResponseObj.deal_id; 95 | bidmanager.addBidResponse(placementCode, bid); 96 | 97 | } else { 98 | //no response data 99 | // @if NODE_ENV='debug' 100 | if (bidObj) {utils.logMessage('No prebid response from TripleLift for inventory code: ' + bidObj.params.inventoryCode);} 101 | // @endif 102 | bid = bidfactory.createBid(2, bidObj); 103 | bid.bidderCode = 'triplelift'; 104 | bidmanager.addBidResponse(placementCode, bid); 105 | } 106 | 107 | } else { 108 | //no response data 109 | // @if NODE_ENV='debug' 110 | utils.logMessage('No prebid response for placement %%PLACEMENT%%'); 111 | // @endif 112 | 113 | } 114 | 115 | }; 116 | 117 | return { 118 | callBids: _callBids 119 | 120 | }; 121 | }; 122 | module.exports = TripleLiftAdapter; 123 | -------------------------------------------------------------------------------- /test/spec/adapters/roxot_spec.js: -------------------------------------------------------------------------------- 1 | describe('Roxot adapter tests', function(){ 2 | const expect = require('chai').expect; 3 | const adapter = require('src/adapters/roxot'); 4 | const bidmanager = require('src/bidmanager'); 5 | 6 | describe('roxotResponseHandler', function () { 7 | 8 | it('should exist and be a function', function () { 9 | expect(pbjs.roxotResponseHandler).to.exist.and.to.be.a('function'); 10 | }); 11 | 12 | it('should add empty bid responses if no bids returned', function () { 13 | var stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); 14 | 15 | var bidderRequest = { 16 | bidderCode: 'roxot', 17 | bids: [ 18 | { 19 | bidId: 'id1', 20 | bidder: 'roxot', 21 | sizes: [[320, 50]], 22 | placementCode: 'div-gpt-ad-12345-1' 23 | }, 24 | { 25 | bidId: 'id2', 26 | bidder: 'roxot', 27 | sizes: [[320, 50]], 28 | placementCode: 'div-gpt-ad-12345-2' 29 | }, 30 | ] 31 | }; 32 | 33 | 34 | // no bids returned in the response. 35 | var response = { 36 | "id": "123", 37 | "bids": [] 38 | }; 39 | 40 | pbjs._bidsRequested.push(bidderRequest); 41 | 42 | // adapter needs to be called, in order for the stub to register. 43 | adapter(); 44 | 45 | pbjs.roxotResponseHandler(response); 46 | 47 | var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; 48 | var bidObject1 = stubAddBidResponse.getCall(0).args[1]; 49 | var bidPlacementCode2 = stubAddBidResponse.getCall(1).args[0]; 50 | var bidObject2 = stubAddBidResponse.getCall(1).args[1]; 51 | 52 | expect(bidPlacementCode1).to.equal('div-gpt-ad-12345-1'); 53 | expect(bidObject1.getStatusCode()).to.equal(2); 54 | expect(bidObject1.bidderCode).to.equal('roxot'); 55 | 56 | expect(bidPlacementCode2).to.equal('div-gpt-ad-12345-2'); 57 | expect(bidObject2.getStatusCode()).to.equal(2); 58 | expect(bidObject2.bidderCode).to.equal('roxot'); 59 | 60 | stubAddBidResponse.restore(); 61 | }); 62 | 63 | it('should add a bid response for bids returned and empty bid responses for the rest', () => { 64 | 65 | var stubAddBidResponse = sinon.stub(bidmanager, 'addBidResponse'); 66 | 67 | var bidderRequest = { 68 | bidderCode: 'roxot', 69 | bids: [ 70 | { 71 | bidId: 'id1', 72 | bidder: 'roxot', 73 | sizes: [[320, 50]], 74 | placementCode: 'div-gpt-ad-12345-1' 75 | }, 76 | { 77 | bidId: 'id2', 78 | bidder: 'roxot', 79 | sizes: [[320, 50]], 80 | placementCode: 'div-gpt-ad-12345-2' 81 | }, 82 | ] 83 | }; 84 | 85 | // Returning a single bid in the response. 86 | var response = { 87 | "id": "12345", 88 | "bids": [ 89 | { 90 | "bidId" : "id1", 91 | "cpm" : 0.09, 92 | "nurl" : "http://roxot.example.com", 93 | "adm" : "<>", 94 | "h" : 320, 95 | "w" : 50 96 | } 97 | ]}; 98 | 99 | pbjs._bidsRequested.push(bidderRequest); 100 | 101 | // adapter needs to be called, in order for the stub to register. 102 | adapter(); 103 | 104 | pbjs.roxotResponseHandler(response); 105 | 106 | var bidPlacementCode1 = stubAddBidResponse.getCall(0).args[0]; 107 | var bidObject1 = stubAddBidResponse.getCall(0).args[1]; 108 | var bidPlacementCode2 = stubAddBidResponse.getCall(1).args[0]; 109 | var bidObject2 = stubAddBidResponse.getCall(1).args[1]; 110 | 111 | expect(bidPlacementCode1).to.equal('div-gpt-ad-12345-1'); 112 | expect(bidObject1.getStatusCode()).to.equal(1); 113 | expect(bidObject1.bidderCode).to.equal('roxot'); 114 | expect(bidObject1.cpm).to.equal(0.09); 115 | expect(bidObject1.height).to.equal(320); 116 | expect(bidObject1.width).to.equal(50); 117 | expect(bidObject1.ad).to.equal('<>'); 118 | 119 | expect(bidPlacementCode2).to.equal('div-gpt-ad-12345-2'); 120 | expect(bidObject2.getStatusCode()).to.equal(2); 121 | expect(bidObject2.bidderCode).to.equal('roxot'); 122 | 123 | stubAddBidResponse.restore(); 124 | }); 125 | }); 126 | }); -------------------------------------------------------------------------------- /src/adapters/adblade.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils.js'); 2 | var bidfactory = require('../bidfactory.js'); 3 | var bidmanager = require('../bidmanager.js'); 4 | var adloader = require('../adloader'); 5 | 6 | /** 7 | * Adapter for requesting bids from Adblade 8 | * To request an Adblade Header partner account 9 | * or for additional integration support please 10 | * register at http://www.adblade.com. 11 | */ 12 | var AdbladeAdapter = function AdbladeAdapter() { 13 | 'use strict'; 14 | 15 | const BIDDER_CODE = 'adblade'; 16 | const BASE_URI = '//rtb.adblade.com/prebidjs/bid?'; 17 | const DEFAULT_BID_FLOOR = 0.0000000001; 18 | 19 | function _callBids(params) { 20 | var bids = params.bids || [], 21 | referrer = utils.getTopWindowUrl(), 22 | loc = utils.getTopWindowLocation(), 23 | domain = loc.hostname, 24 | partnerId = 0, 25 | bidRequests = {}; 26 | 27 | if (bids.length > 0) { 28 | partnerId = '' + bids[0].params.partnerId; 29 | } 30 | 31 | utils._each(bids, function(bid) { 32 | // make sure the "sizes" are an array of arrays 33 | if (!(bid.sizes[0] instanceof Array)) { 34 | bid.sizes = [bid.sizes]; 35 | } 36 | utils._each(bid.sizes, function(size) { 37 | let key = size[0] + 'x' + size[1]; 38 | 39 | bidRequests[key] = bidRequests[key] || { 40 | 'site': { 41 | 'id': partnerId, 42 | 'page': referrer, 43 | 'domain': domain, 44 | 'publisher': { 45 | 'id': partnerId, 46 | 'name': referrer, 47 | 'domain': domain 48 | } 49 | }, 50 | 'id': params.requestId, 51 | 'imp': [], 52 | 'device': { 53 | 'ua': window.navigator.userAgent, 54 | }, 55 | 'cur': ['USD'], 56 | 'user': {} 57 | }; 58 | 59 | bidRequests[key].imp.push({ 60 | 'id': bid.bidId, 61 | 'bidfloor': bid.params.bidFloor || DEFAULT_BID_FLOOR, 62 | 'tag': bid.placementCode, 63 | 'banner': { 64 | 'w': size[0], 65 | 'h': size[1], 66 | }, 67 | 'secure': 0 + (loc.protocol === 'https') 68 | }); 69 | }); 70 | }); 71 | 72 | utils._each(bidRequests, function (bidRequest) { 73 | adloader.loadScript( 74 | utils.tryAppendQueryString( 75 | utils.tryAppendQueryString( 76 | BASE_URI, 77 | 'callback', 78 | '$$PREBID_GLOBAL$$.adbladeResponse' 79 | ), 80 | 'json', 81 | JSON.stringify( 82 | bidRequest 83 | ) 84 | ) 85 | ); 86 | }); 87 | } 88 | 89 | $$PREBID_GLOBAL$$.adbladeResponse = function (response) { 90 | var auctionIdRe = /\$(%7B|\{)AUCTION_ID(%7D|\})/gi, 91 | auctionPriceRe = /\$(%7B|\{)AUCTION_PRICE(%7D|\})/gi, 92 | clickUrlRe = /\$(%7B|\{)CLICK_URL(%7D|\})/gi; 93 | 94 | if (typeof(response) === 'undefined' || !response.hasOwnProperty('seatbid') || utils.isEmpty(response.seatbid)) { 95 | // handle empty bids 96 | var bidsRequested = $$PREBID_GLOBAL$$._bidsRequested.find(bidSet => bidSet.bidderCode === BIDDER_CODE).bids; 97 | if (bidsRequested.length > 0) { 98 | let bid = bidfactory.createBid(2); 99 | bid.bidderCode = BIDDER_CODE; 100 | bidmanager.addBidResponse(bidsRequested[0].placementCode, bid); 101 | } 102 | 103 | return; 104 | } 105 | 106 | utils._each(response.seatbid, function(seatbid) { 107 | utils._each(seatbid.bid, function(seatbidBid) { 108 | var bidRequest = utils.getBidRequest(seatbidBid.impid), 109 | ad = seatbidBid.adm + utils.createTrackPixelHtml(seatbidBid.nurl); 110 | 111 | ad = ad.replace(auctionIdRe, seatbidBid.impid); 112 | ad = ad.replace(clickUrlRe, ''); 113 | ad = ad.replace(auctionPriceRe, seatbidBid.price); 114 | 115 | let bid = bidfactory.createBid(1); 116 | 117 | bid.bidderCode = BIDDER_CODE; 118 | bid.cpm = seatbidBid.price; 119 | bid.ad = ad; 120 | bid.width = seatbidBid.w; 121 | bid.height = seatbidBid.h; 122 | bidmanager.addBidResponse(bidRequest.placementCode, bid); 123 | }); 124 | }); 125 | }; 126 | 127 | return { 128 | callBids: _callBids 129 | }; 130 | }; 131 | 132 | module.exports = AdbladeAdapter; 133 | -------------------------------------------------------------------------------- /src/adapters/authenticated.js: -------------------------------------------------------------------------------- 1 | var utils = require('../utils.js'); 2 | var adloader = require('../adloader.js'); 3 | var bidmanager = require('../bidmanager.js'); 4 | var bidfactory = require('../bidfactory.js'); 5 | 6 | /* Authenticated bidder factory function 7 | * Use to create a AuthenticatedAdapter object 8 | */ 9 | 10 | 11 | var AdapterAdapter = function AuthenticatedAdapter() { 12 | 13 | function _callBids(params) { 14 | var authReq = params.bids; 15 | var bidsCount = authReq.length; 16 | 17 | //set expected bids count for callback execution 18 | //bidmanager.setExpectedBidsCount('authenticated',bidsCount); 19 | 20 | for (var i = 0; i < bidsCount; i++) { 21 | var bidRequest = tlReq[i]; 22 | var callbackId = bidRequest.bidId; 23 | adloader.loadScript(buildAuthCall(bidRequest, callbackId)); 24 | //store a reference to the bidRequest from the callback id 25 | //bidmanager.pbCallbackMap[callbackId] = bidRequest; 26 | } 27 | 28 | } 29 | 30 | 31 | function buildAuthCall(bid, callbackId) { 32 | //determine tag params 33 | var inventoryCode = utils.getBidIdParameter('inventoryCode', bid.params); 34 | var floor = utils.getBidIdParameter('floor', bid.params); 35 | 36 | 37 | //build our base tag, based on if we are http or https 38 | var authURI = '//x.adjs.net/header/auction?'; 39 | var authCall = document.location.protocol + authURI; 40 | 41 | authCall = utils.tryAppendQueryString(authCall, 'callback', '$$PREBID_GLOBAL$$.AUTHCB'); 42 | authCall = utils.tryAppendQueryString(authCall, 'lib', 'prebid'); 43 | authCall = utils.tryAppendQueryString(authCall, 'v', '$prebid.version$'); 44 | authCall = utils.tryAppendQueryString(authCall, 'callback_id', callbackId); 45 | authCall = utils.tryAppendQueryString(authCall, 'inv_code', inventoryCode); 46 | authCall = utils.tryAppendQueryString(authCall, 'floor', floor); 47 | 48 | //sizes takes a bit more logic 49 | var sizeQueryString = utils.parseSizesInput(bid.sizes); 50 | if (sizeQueryString) { 51 | authCall += 'size=' + sizeQueryString + '&'; 52 | } 53 | 54 | //append referrer 55 | var referrer = utils.getTopWindowUrl(); 56 | authCall = utils.tryAppendQueryString(authCall, 'referrer', referrer); 57 | 58 | //remove the trailing "&" 59 | if (authCall.lastIndexOf('&') === authCall.length - 1) { 60 | authCall = tlCall.substring(0, authCall.length - 1); 61 | } 62 | 63 | // @if NODE_ENV='debug' 64 | utils.logMessage('authCall request built: ' + authCall); 65 | // @endif 66 | 67 | //append a timer here to track latency 68 | bid.startTime = new Date().getTime(); 69 | 70 | return authCall; 71 | 72 | } 73 | 74 | 75 | //expose the callback to the global object: 76 | $$PREBID_GLOBAL$$.AUTHCB = function(tlResponseObj) { 77 | if (authResponseObj && authResponseObj.callback_id) { 78 | var bidObj = utils.getBidRequest(authResponseObj.callback_id); 79 | var placementCode = bidObj && bidObj.placementCode; 80 | 81 | // @if NODE_ENV='debug' 82 | if (bidObj) {utils.logMessage('JSONP callback function called for inventory code: ' + bidObj.params.inventoryCode);} 83 | // @endif 84 | 85 | var bid = []; 86 | if (authResponseObj && authResponseObj.cpm && authResponseObj.cpm !== 0) { 87 | 88 | bid = bidfactory.createBid(1, bidObj); 89 | bid.bidderCode = 'authenticated'; 90 | bid.cpm = authResponseObj.cpm; 91 | bid.ad = authResponseObj.ad; 92 | bid.width = authResponseObj.width; 93 | bid.height = authResponseObj.height; 94 | bid.dealId = authResponseObj.deal_id; 95 | bidmanager.addBidResponse(placementCode, bid); 96 | 97 | } else { 98 | //no response data 99 | // @if NODE_ENV='debug' 100 | if (bidObj) {utils.logMessage('No prebid response from Authenticated for inventory code: ' + bidObj.params.inventoryCode);} 101 | // @endif 102 | bid = bidfactory.createBid(2, bidObj); 103 | bid.bidderCode = 'authenticated'; 104 | bidmanager.addBidResponse(placementCode, bid); 105 | } 106 | 107 | } else { 108 | //no response data 109 | // @if NODE_ENV='debug' 110 | utils.logMessage('No prebid response for placement %%PLACEMENT%%'); 111 | // @endif 112 | 113 | } 114 | 115 | }; 116 | 117 | return { 118 | callBids: _callBids 119 | 120 | }; 121 | }; 122 | module.exports = AuthenticatedAdapter; 123 | -------------------------------------------------------------------------------- /src/adapters/memeglobal.js: -------------------------------------------------------------------------------- 1 | var CONSTANTS = require('../constants.json'); 2 | var utils = require('../utils.js'); 3 | var bidfactory = require('../bidfactory.js'); 4 | var bidmanager = require('../bidmanager.js'); 5 | var adloader = require('../adloader'); 6 | 7 | var defaultPlacementForBadBid = null; 8 | var bidderName = 'memeglobal'; 9 | /** 10 | * Adapter for requesting bids from Meme Global Media Group 11 | * OpenRTB compatible 12 | */ 13 | var MemeGlobalAdapter = function MemeGlobalAdapter() { 14 | var bidder = 'stinger.memeglobal.com/api/v1/services/prebid'; 15 | 16 | function _callBids(params) { 17 | var bids = params.bids; 18 | 19 | if (!bids) return; 20 | 21 | // assign the first adUnit (placement) for bad bids; 22 | defaultPlacementForBadBid = bids[0].placementCode; 23 | 24 | for (var i = 0; i < bids.length; i++) { 25 | _requestBid(bids[i]); 26 | } 27 | } 28 | 29 | function _requestBid(bidReq) { 30 | // build bid request object 31 | var domain = window.location.host; 32 | var page = window.location.host + window.location.pathname + location.search + location.hash; 33 | 34 | var tagId = utils.getBidIdParameter('tagid', bidReq.params); 35 | var bidFloor = Number(utils.getBidIdParameter('bidfloor', bidReq.params)); 36 | var adW = 0; 37 | var adH = 0; 38 | 39 | var bidSizes = Array.isArray(bidReq.params.sizes) ? bidReq.params.sizes : bidReq.sizes; 40 | var sizeArrayLength = bidSizes.length; 41 | if (sizeArrayLength === 2 && typeof bidSizes[0] === 'number' && typeof bidSizes[1] === 'number') { 42 | adW = bidSizes[0]; 43 | adH = bidSizes[1]; 44 | } else { 45 | adW = bidSizes[0][0]; 46 | adH = bidSizes[0][1]; 47 | } 48 | 49 | // build bid request with impressions 50 | var bidRequest = { 51 | id: utils.getUniqueIdentifierStr(), 52 | imp: [{ 53 | id: bidReq.bidId, 54 | banner: { 55 | w: adW, 56 | h: adH 57 | }, 58 | tagid: bidReq.placementCode, 59 | bidfloor: bidFloor 60 | }], 61 | site: { 62 | domain: domain, 63 | page: page, 64 | publisher: { 65 | id: tagId 66 | } 67 | } 68 | }; 69 | 70 | var scriptUrl = '//' + bidder + '?callback=window.$$PREBID_GLOBAL$$.mgres' + 71 | '&src=' + CONSTANTS.REPO_AND_VERSION + 72 | '&br=' + encodeURIComponent(JSON.stringify(bidRequest)); 73 | adloader.loadScript(scriptUrl); 74 | } 75 | 76 | function getBidSetForBidder() { 77 | return $$PREBID_GLOBAL$$._bidsRequested.find(bidSet => bidSet.bidderCode === bidderName); 78 | } 79 | 80 | // expose the callback to the global object: 81 | $$PREBID_GLOBAL$$.mgres = function (bidResp) { 82 | 83 | // valid object? 84 | if ((!bidResp || !bidResp.id) || 85 | (!bidResp.seatbid || bidResp.seatbid.length === 0 || !bidResp.seatbid[0].bid || bidResp.seatbid[0].bid.length === 0)) { 86 | return ; 87 | } 88 | 89 | bidResp.seatbid[0].bid.forEach(function (bidderBid) { 90 | var responseCPM; 91 | var placementCode = ''; 92 | 93 | var bidSet = getBidSetForBidder(); 94 | var bidRequested = bidSet.bids.find(b => b.bidId === bidderBid.impid); 95 | if (bidRequested) { 96 | var bidResponse = bidfactory.createBid(1); 97 | placementCode = bidRequested.placementCode; 98 | bidRequested.status = CONSTANTS.STATUS.GOOD; 99 | responseCPM = parseFloat(bidderBid.price); 100 | if (responseCPM === 0) { 101 | var bid = bidfactory.createBid(2); 102 | bid.bidderCode = bidderName; 103 | bidmanager.addBidResponse(placementCode, bid); 104 | return; 105 | } 106 | bidResponse.placementCode = placementCode; 107 | bidResponse.size = bidRequested.sizes; 108 | var responseAd = bidderBid.adm; 109 | var responseNurl = ''; 110 | bidResponse.creative_id = bidderBid.id; 111 | bidResponse.bidderCode = bidderName; 112 | bidResponse.cpm = responseCPM; 113 | bidResponse.ad = decodeURIComponent(responseAd + responseNurl); 114 | bidResponse.width = parseInt(bidderBid.w); 115 | bidResponse.height = parseInt(bidderBid.h); 116 | bidmanager.addBidResponse(placementCode, bidResponse); 117 | } 118 | }); 119 | }; 120 | 121 | return { 122 | callBids: _callBids 123 | }; 124 | }; 125 | 126 | module.exports = MemeGlobalAdapter; 127 | -------------------------------------------------------------------------------- /src/events.js: -------------------------------------------------------------------------------- 1 | /** 2 | * events.js 3 | */ 4 | var utils = require('./utils'); 5 | var CONSTANTS = require('./constants'); 6 | var slice = Array.prototype.slice; 7 | var push = Array.prototype.push; 8 | 9 | //define entire events 10 | //var allEvents = ['bidRequested','bidResponse','bidWon','bidTimeout']; 11 | var allEvents = utils._map(CONSTANTS.EVENTS, function (v) { 12 | return v; 13 | }); 14 | 15 | var idPaths = CONSTANTS.EVENT_ID_PATHS; 16 | 17 | //keep a record of all events fired 18 | var eventsFired = []; 19 | 20 | module.exports = (function () { 21 | 22 | var _handlers = {}; 23 | var _public = {}; 24 | 25 | /** 26 | * 27 | * @param {String} eventString The name of the event. 28 | * @param {Array} args The payload emitted with the event. 29 | * @private 30 | */ 31 | function _dispatch(eventString, args) { 32 | utils.logMessage('Emitting event for: ' + eventString); 33 | 34 | var eventPayload = args[0] || {}; 35 | var idPath = idPaths[eventString]; 36 | var key = eventPayload[idPath]; 37 | var event = _handlers[eventString] || { que: [] }; 38 | var eventKeys = utils._map(event, function (v, k) { 39 | return k; 40 | }); 41 | 42 | var callbacks = []; 43 | 44 | //record the event: 45 | eventsFired.push({ 46 | eventType: eventString, 47 | args: eventPayload, 48 | id: key 49 | }); 50 | 51 | /** Push each specific callback to the `callbacks` array. 52 | * If the `event` map has a key that matches the value of the 53 | * event payload id path, e.g. `eventPayload[idPath]`, then apply 54 | * each function in the `que` array as an argument to push to the 55 | * `callbacks` array 56 | * */ 57 | if (key && utils.contains(eventKeys, key)) { 58 | push.apply(callbacks, event[key].que); 59 | } 60 | 61 | /** Push each general callback to the `callbacks` array. */ 62 | push.apply(callbacks, event.que); 63 | 64 | /** call each of the callbacks */ 65 | utils._each(callbacks, function (fn) { 66 | if (!fn) return; 67 | try { 68 | fn.apply(null, args); 69 | } 70 | catch (e) { 71 | utils.logError('Error executing handler:', 'events.js', e); 72 | } 73 | }); 74 | } 75 | 76 | function _checkAvailableEvent(event) { 77 | return utils.contains(allEvents, event); 78 | } 79 | 80 | _public.on = function (eventString, handler, id) { 81 | 82 | //check whether available event or not 83 | if (_checkAvailableEvent(eventString)) { 84 | var event = _handlers[eventString] || { que: [] }; 85 | 86 | if (id) { 87 | event[id] = event[id] || { que: [] }; 88 | event[id].que.push(handler); 89 | } else { 90 | event.que.push(handler); 91 | } 92 | 93 | _handlers[eventString] = event; 94 | } else { 95 | utils.logError('Wrong event name : ' + eventString + ' Valid event names :' + allEvents); 96 | } 97 | }; 98 | 99 | _public.emit = function (event) { 100 | var args = slice.call(arguments, 1); 101 | _dispatch(event, args); 102 | }; 103 | 104 | _public.off = function (eventString, handler, id) { 105 | var event = _handlers[eventString]; 106 | 107 | if (utils.isEmpty(event) || utils.isEmpty(event.que) && utils.isEmpty(event[id])) { 108 | return; 109 | } 110 | 111 | if (id && (utils.isEmpty(event[id]) || utils.isEmpty(event[id].que))) { 112 | return; 113 | } 114 | 115 | if (id) { 116 | utils._each(event[id].que, function (_handler) { 117 | var que = event[id].que; 118 | if (_handler === handler) { 119 | que.splice(utils.indexOf.call(que, _handler), 1); 120 | } 121 | }); 122 | } else { 123 | utils._each(event.que, function (_handler) { 124 | var que = event.que; 125 | if (_handler === handler) { 126 | que.splice(utils.indexOf.call(que, _handler), 1); 127 | } 128 | }); 129 | } 130 | 131 | _handlers[eventString] = event; 132 | }; 133 | 134 | _public.get = function () { 135 | return _handlers; 136 | }; 137 | 138 | /** 139 | * This method can return a copy of all the events fired 140 | * @return {Array} array of events fired 141 | */ 142 | _public.getEvents = function () { 143 | var arrayCopy = []; 144 | utils._each(eventsFired, function (value) { 145 | var newProp = utils.extend({}, value); 146 | arrayCopy.push(newProp); 147 | }); 148 | 149 | return arrayCopy; 150 | }; 151 | 152 | return _public; 153 | }()); 154 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Thu Aug 07 2014 09:45:28 GMT-0700 (PDT) 3 | var webpackConfig = require('./webpack.conf'); 4 | webpackConfig.module.postLoaders = [ 5 | { 6 | test: /\.js$/, 7 | exclude: /(node_modules)|(test)|(integrationExamples)|(build)|polyfill.js|(src\/adapters\/analytics\/ga.js)/, 8 | loader: 'istanbul-instrumenter' 9 | } 10 | ]; 11 | 12 | var CI_MODE = process.env.NODE_ENV === 'ci'; 13 | 14 | module.exports = function (config) { 15 | config.set({ 16 | 17 | // base path that will be used to resolve all patterns (eg. files, exclude) 18 | basePath: './', 19 | 20 | // BrowserStack Config 21 | browserStack: { 22 | username: process.env.BROWSERSTACK_USERNAME, 23 | accessKey: process.env.BROWSERSTACK_KEY 24 | }, 25 | 26 | // define browsers 27 | customLaunchers: require('./browsers.json'), 28 | 29 | // frameworks to use 30 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 31 | frameworks: ['es5-shim', 'mocha', 'expect', 'sinon'], 32 | 33 | client: { 34 | mocha: { 35 | reporter: 'html' 36 | } 37 | }, 38 | 39 | // list of files / patterns to load in the browser 40 | files: [ 41 | 'test/**/*_spec.js', 42 | 'test/helpers/karma-init.js' 43 | ], 44 | 45 | // list of files to exclude 46 | exclude: [ 47 | 'test/spec/loaders/**/*.js' 48 | ], 49 | 50 | // preprocess matching files before serving them to the browser 51 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 52 | preprocessors: { 53 | 'test/**/*_spec.js': ['webpack'], 54 | '!test/**/*_spec.js': 'coverage', 55 | 'src/**/*.js': ['webpack', 'coverage'] 56 | }, 57 | 58 | // WebPack Related 59 | webpack: webpackConfig, 60 | webpackMiddleware: { 61 | noInfo: true 62 | }, 63 | 64 | // test results reporter to use 65 | // possible values: 'dots', 'progress' 66 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 67 | reporters: CI_MODE ? ['junit', 'coverage'] : ['progress', 'html', 'coverage'], 68 | 69 | // junit reporter config 70 | junitReporter: { 71 | outputDir: 'test' 72 | }, 73 | 74 | // optionally, configure the reporter 75 | coverageReporter: { 76 | reporters: [ 77 | { type: 'html', dir: './build/coverage/' }, 78 | { type: 'text', dir: './build/coverage/' }, 79 | { type: 'lcov', dir: './build/coverage/lcov', subdir: '.' } 80 | ] 81 | }, 82 | 83 | htmlReporter: { 84 | outputDir: 'build/coverage/karma_html', // where to put the reports 85 | urlFriendlyName: true, // simply replaces spaces with _ for files/dirs 86 | reportName: 'report' // report summary filename; browser info by default 87 | }, 88 | 89 | // web server port 90 | port: 9876, 91 | 92 | // enable / disable colors in the output (reporters and logs) 93 | colors: true, 94 | 95 | // level of logging 96 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 97 | logLevel: config.LOG_INFO, 98 | 99 | // enable / disable watching file and executing tests whenever any file changes 100 | autoWatch: true, 101 | 102 | // start these browsers 103 | // NOTE: these get defined again in gulpfile.js for the gulp tasks 104 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 105 | browsers: ['Chrome', 'Firefox'], 106 | 107 | // Continuous Integration mode 108 | // if true, Karma captures browsers, runs the tests and exits 109 | singleRun: false, 110 | browserDisconnectTimeout : 10000, // default 2000 111 | browserDisconnectTolerance : 1, // default 0 112 | browserNoActivityTimeout : 4*60*1000, //default 10000 113 | captureTimeout : 4*60*1000, //default 60000 114 | 115 | plugins: [ 116 | 'karma-browserstack-launcher', 117 | 'karma-phantomjs-launcher', 118 | 'karma-coverage', 119 | 'karma-es5-shim', 120 | 'karma-mocha', 121 | 'karma-expect', 122 | 'karma-sinon-ie', 123 | 'karma-webpack', 124 | 'karma-junit-reporter', 125 | 'karma-html-reporter', 126 | 'karma-chrome-launcher', 127 | 'karma-sauce-launcher', 128 | 'karma-firefox-launcher', 129 | 'karma-opera-launcher', 130 | 'karma-safari-launcher', 131 | 'karma-script-launcher', 132 | 'karma-requirejs', 133 | 'karma-ie-launcher' 134 | ] 135 | }); 136 | }; 137 | -------------------------------------------------------------------------------- /test/spec/adapters/underdogmedia_spec.js: -------------------------------------------------------------------------------- 1 | /* jshint -W030 */ 2 | 3 | import Adapter from '../../../src/adapters/underdogmedia'; 4 | import bidManager from '../../../src/bidmanager'; 5 | import {expect} from 'chai'; 6 | 7 | describe('underdog media adapter test', () => { 8 | 9 | let adapter; 10 | let server; 11 | 12 | // Minimal stub implementation of underdog media header bid API 13 | // This will prevent the need to load underdog's static library, and to make requests to underdog's server 14 | window.udm_header_lib = { 15 | 16 | BidRequest: function(options){ 17 | return { 18 | send: function(){ 19 | var siteId = options.siteId; 20 | if(siteId == 10272){ 21 | // Only bid on this particular site id 22 | var bids = []; 23 | options.sizes.forEach(function(size){ 24 | bids.push({ 25 | cpm: 3.14, 26 | ad_html: `Ad HTML for site ID ${siteId} size ${size[0]}x${size[1]}`, 27 | width: size[0], 28 | height: size[1] 29 | }); 30 | }); 31 | options.callback({ 32 | bids: bids 33 | }); 34 | } else { 35 | options.callback({ 36 | bids: [] 37 | }); 38 | } 39 | 40 | } 41 | }; 42 | }, 43 | 44 | BidRequestArray: function(arr){ 45 | return { 46 | send: function(){ 47 | arr.forEach(function(bidRequest){ 48 | var req = new window.udm_header_lib.BidRequest(bidRequest); 49 | req.send(); 50 | }); 51 | } 52 | }; 53 | } 54 | }; 55 | 56 | // The third bid here is an invalid site id and should return a 'no-bid'. 57 | function request() { 58 | adapter.callBids({ 59 | bidderCode: 'underdogmedia', 60 | bids: [ 61 | { 62 | bidder: 'underdogmedia', 63 | adUnitCode: 'foo', 64 | sizes: [[728, 90]], 65 | params: { 66 | siteId: '10272' 67 | } 68 | }, 69 | { 70 | bidder: 'underdogmedia', 71 | adUnitCode: 'bar', 72 | sizes: [[300, 250]], 73 | params: { 74 | siteId: '10272' 75 | } 76 | }, 77 | { 78 | bidder: 'underdogmedia', 79 | adUnitCode: 'nothing', 80 | sizes: [[160, 600]], 81 | params: { 82 | siteId: '31337' 83 | } 84 | } 85 | ] 86 | }); 87 | } 88 | 89 | beforeEach(() => { 90 | adapter = new Adapter(); 91 | }); 92 | 93 | afterEach(() => { 94 | }); 95 | 96 | describe('adding bids to the manager', () => { 97 | 98 | let firstBid; 99 | let secondBid; 100 | let thirdBid; 101 | 102 | beforeEach(() => { 103 | sinon.stub(bidManager, 'addBidResponse'); 104 | request(); 105 | firstBid = bidManager.addBidResponse.firstCall.args[1]; 106 | secondBid = bidManager.addBidResponse.secondCall.args[1]; 107 | thirdBid = bidManager.addBidResponse.thirdCall.args[1]; 108 | }); 109 | 110 | afterEach(() => { 111 | bidManager.addBidResponse.restore(); 112 | }); 113 | 114 | it('will add a bid object for each bid', () => { 115 | sinon.assert.calledThrice(bidManager.addBidResponse); 116 | }); 117 | 118 | it('will add the ad html to the bid object', () => { 119 | expect(firstBid).to.have.property('ad', 'Ad HTML for site ID 10272 size 728x90'); 120 | expect(secondBid).to.have.property('ad', 'Ad HTML for site ID 10272 size 300x250'); 121 | expect(thirdBid).to.not.have.property('ad'); 122 | }); 123 | 124 | it('will have the right size attached', () => { 125 | expect(firstBid).to.have.property('width', 728); 126 | expect(firstBid).to.have.property('height', 90); 127 | expect(secondBid).to.have.property('width', 300); 128 | expect(secondBid).to.have.property('height', 250); 129 | }); 130 | 131 | it('will add the CPM to the bid object', () => { 132 | expect(firstBid).to.have.property('cpm', 3.14); 133 | expect(secondBid).to.have.property('cpm', 3.14); 134 | expect(thirdBid).to.not.have.property('cpm'); 135 | }); 136 | 137 | it('will add the bidder code to the bid object', () => { 138 | expect(firstBid).to.have.property('bidderCode', 'underdogmedia'); 139 | expect(secondBid).to.have.property('bidderCode', 'underdogmedia'); 140 | expect(thirdBid).to.have.property('bidderCode', 'underdogmedia'); 141 | }); 142 | 143 | }); 144 | 145 | }); 146 | -------------------------------------------------------------------------------- /test/fixtures/cpmInputsOutputs.json: -------------------------------------------------------------------------------- 1 | { 2 | "cpmInputs": ["17.638", "19.836", "11.501", "14.384", "23.224", "21.279", "8.886", "16.555", "10.579", "1.331", "1.998", "14.988", "14.864", "10.369", "0.262", "5.269", "6.874", "5.598", "7.191", "15.218", "10.958", "4.420", "17.749", "23.808", "12.353", "21.726", "1.562", "18.085", "1.184", "15.470", "13.841", "17.966", "22.150", "9.088", "13.613", "18.384", "13.690", "23.639", "5.085", "5.779", "11.456", "0.315", "18.557", "20.813", "18.813", "10.202", "10.143", "2.483", "16.147", "2.909", "0.652"], 3 | "priceStringOutputs": [{"low":"5.00","med":"17.60","high":"17.63","auto":"17.50","dense":"17.50","custom":""},{"low":"5.00","med":"19.80","high":"19.83","auto":"19.50","dense":"19.50","custom":""},{"low":"5.00","med":"11.50","high":"11.50","auto":"11.50","dense":"11.50","custom":""},{"low":"5.00","med":"14.30","high":"14.38","auto":"14.00","dense":"14.00","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"8.80","high":"8.88","auto":"8.80","dense":"8.50","custom":""},{"low":"5.00","med":"16.50","high":"16.55","auto":"16.50","dense":"16.50","custom":""},{"low":"5.00","med":"10.50","high":"10.57","auto":"10.50","dense":"10.50","custom":""},{"low":"1.00","med":"1.30","high":"1.33","auto":"1.30","dense":"1.33","custom":""},{"low":"1.50","med":"1.90","high":"1.99","auto":"1.95","dense":"1.99","custom":""},{"low":"5.00","med":"14.90","high":"14.98","auto":"14.50","dense":"14.50","custom":""},{"low":"5.00","med":"14.80","high":"14.86","auto":"14.50","dense":"14.50","custom":""},{"low":"5.00","med":"10.30","high":"10.36","auto":"10.00","dense":"10.00","custom":""},{"low":"0.00","med":"0.20","high":"0.26","auto":"0.25","dense":"0.26","custom":""},{"low":"5.00","med":"5.20","high":"5.26","auto":"5.20","dense":"5.25","custom":""},{"low":"5.00","med":"6.80","high":"6.87","auto":"6.80","dense":"6.85","custom":""},{"low":"5.00","med":"5.50","high":"5.59","auto":"5.50","dense":"5.55","custom":""},{"low":"5.00","med":"7.10","high":"7.19","auto":"7.10","dense":"7.15","custom":""},{"low":"5.00","med":"15.20","high":"15.21","auto":"15.00","dense":"15.00","custom":""},{"low":"5.00","med":"10.90","high":"10.95","auto":"10.50","dense":"10.50","custom":""},{"low":"4.00","med":"4.40","high":"4.42","auto":"4.40","dense":"4.40","custom":""},{"low":"5.00","med":"17.70","high":"17.74","auto":"17.50","dense":"17.50","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"12.30","high":"12.35","auto":"12.00","dense":"12.00","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"1.50","med":"1.50","high":"1.56","auto":"1.55","dense":"1.56","custom":""},{"low":"5.00","med":"18.00","high":"18.08","auto":"18.00","dense":"18.00","custom":""},{"low":"1.00","med":"1.10","high":"1.18","auto":"1.15","dense":"1.18","custom":""},{"low":"5.00","med":"15.40","high":"15.47","auto":"15.00","dense":"15.00","custom":""},{"low":"5.00","med":"13.80","high":"13.84","auto":"13.50","dense":"13.50","custom":""},{"low":"5.00","med":"17.90","high":"17.96","auto":"17.50","dense":"17.50","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"9.00","high":"9.08","auto":"9.00","dense":"9.00","custom":""},{"low":"5.00","med":"13.60","high":"13.61","auto":"13.50","dense":"13.50","custom":""},{"low":"5.00","med":"18.30","high":"18.38","auto":"18.00","dense":"18.00","custom":""},{"low":"5.00","med":"13.60","high":"13.69","auto":"13.50","dense":"13.50","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"5.00","high":"5.08","auto":"5.00","dense":"5.05","custom":""},{"low":"5.00","med":"5.70","high":"5.77","auto":"5.70","dense":"5.75","custom":""},{"low":"5.00","med":"11.40","high":"11.45","auto":"11.00","dense":"11.00","custom":""},{"low":"0.00","med":"0.30","high":"0.31","auto":"0.30","dense":"0.31","custom":""},{"low":"5.00","med":"18.50","high":"18.55","auto":"18.50","dense":"18.50","custom":""},{"low":"5.00","med":"20.00","high":"20.00","auto":"20.00","dense":"20.00","custom":""},{"low":"5.00","med":"18.80","high":"18.81","auto":"18.50","dense":"18.50","custom":""},{"low":"5.00","med":"10.20","high":"10.20","auto":"10.00","dense":"10.00","custom":""},{"low":"5.00","med":"10.10","high":"10.14","auto":"10.00","dense":"10.00","custom":""},{"low":"2.00","med":"2.40","high":"2.48","auto":"2.45","dense":"2.48","custom":""},{"low":"5.00","med":"16.10","high":"16.14","auto":"16.00","dense":"16.00","custom":""},{"low":"2.50","med":"2.90","high":"2.90","auto":"2.90","dense":"2.90","custom":""},{"low":"0.50","med":"0.60","high":"0.65","auto":"0.65","dense":"0.65","custom":""}] 4 | } 5 | -------------------------------------------------------------------------------- /src/adapters/sonobi.js: -------------------------------------------------------------------------------- 1 | var bidfactory = require('../bidfactory.js'); 2 | var bidmanager = require('../bidmanager.js'); 3 | var adloader = require('../adloader.js'); 4 | var utils = require('../utils'); 5 | 6 | var SonobiAdapter = function SonobiAdapter(){ 7 | var keymakerAssoc = {}; // Remember placement codes for callback mapping 8 | var bidReqAssoc = {}; // Remember bids for bid complete reporting 9 | 10 | function _phone_in(request){ 11 | var trinity = 'https://apex.go.sonobi.com/trinity.js?key_maker='; 12 | var adSlots = request.bids || []; 13 | var bidderRequestId = request.bidderRequestId; 14 | var ref = (window.frameElement) ? '&ref=' + encodeURI(top.location.host || document.referrer) : ''; 15 | adloader.loadScript(trinity + JSON.stringify(_keymaker(adSlots)) + '&cv=' + _operator(bidderRequestId) + ref ); 16 | } 17 | 18 | function _keymaker(adSlots){ 19 | var keyring = {}; 20 | utils._each(adSlots, function(bidRequest){ 21 | if(bidRequest.params){ 22 | // Optional 23 | var floor = (bidRequest.params.floor) ? bidRequest.params.floor : null; 24 | // Mandatory 25 | var slotIdentifier = (bidRequest.params.ad_unit) ? bidRequest.params.ad_unit : (bidRequest.params.placement_id) ? bidRequest.params.placement_id : null; 26 | var sizes = utils.parseSizesInput(bidRequest.sizes).toString() || null; 27 | var bidId = bidRequest.bidId; 28 | if (utils.isEmpty(sizes)){ 29 | utils.logError('Sonobi adapter expects sizes for ' + bidRequest.placementCode); 30 | } 31 | var args = (sizes) ? ((floor) ? (sizes + '|f=' + floor) : (sizes)) : (floor) ? ('f=' + floor) : ''; 32 | if (/^[\/]?[\d]+[[\/].+[\/]?]?$/.test(slotIdentifier)){ 33 | slotIdentifier = slotIdentifier.charAt(0) === '/' ? slotIdentifier : '/' + slotIdentifier; 34 | keyring[slotIdentifier + '|' + bidId] = args; 35 | keymakerAssoc[slotIdentifier + '|' + bidId] = bidRequest.placementCode; 36 | bidReqAssoc[bidRequest.placementCode] = bidRequest; 37 | } else if (/^[0-9a-fA-F]{20}$/.test(slotIdentifier) && slotIdentifier.length === 20){ 38 | keyring[bidId] = slotIdentifier + '|' + args; 39 | keymakerAssoc[bidId] = bidRequest.placementCode; 40 | bidReqAssoc[bidRequest.placementCode] = bidRequest; 41 | } else { 42 | keymakerAssoc[bidId] = bidRequest.placementCode; 43 | bidReqAssoc[bidRequest.placementCode] = bidRequest; 44 | _failure(bidRequest.placementCode); 45 | utils.logError('The ad unit code or Sonobi Placement id for slot ' + bidRequest.placementCode + ' is invalid'); 46 | } 47 | } 48 | }); 49 | return keyring; 50 | } 51 | 52 | function _operator(bidderRequestId){ 53 | var cb_name = "sbi_" + bidderRequestId; 54 | window[cb_name] = _trinity; 55 | return cb_name; 56 | } 57 | 58 | function _trinity(response){ 59 | var slots = response.slots || {}; 60 | var sbi_dc = response.sbi_dc || ''; 61 | utils._each(slots, function(bid, slot_id){ 62 | var placementCode = keymakerAssoc[slot_id]; 63 | if (bid.sbi_aid && bid.sbi_mouse && bid.sbi_size){ 64 | _success(placementCode, sbi_dc, bid); 65 | } else { 66 | _failure(placementCode); 67 | } 68 | delete keymakerAssoc[slot_id]; 69 | }); 70 | } 71 | 72 | function _seraph(placementCode){ 73 | var theOne = bidReqAssoc[placementCode]; 74 | delete bidReqAssoc[placementCode]; 75 | return theOne; 76 | } 77 | 78 | function _success(placementCode, sbi_dc, bid){ 79 | var goodBid = bidfactory.createBid(1, _seraph(placementCode)); 80 | if(bid.sbi_dozer){ 81 | goodBid.dealId = bid.sbi_dozer; 82 | } 83 | goodBid.bidderCode = 'sonobi'; 84 | goodBid.ad = _creative(sbi_dc, bid.sbi_aid); 85 | goodBid.cpm = Number(bid.sbi_mouse); 86 | goodBid.width = Number(bid.sbi_size.split('x')[0]) || 1; 87 | goodBid.height = Number(bid.sbi_size.split('x')[1]) || 1; 88 | bidmanager.addBidResponse(placementCode, goodBid); 89 | } 90 | 91 | function _failure(placementCode){ 92 | var failBid = bidfactory.createBid(2, _seraph(placementCode)); 93 | failBid.bidderCode = 'sonobi'; 94 | bidmanager.addBidResponse(placementCode, failBid); 95 | } 96 | 97 | function _creative(sbi_dc, sbi_aid){ 98 | var src = 'https://' + sbi_dc + 'apex.go.sonobi.com/sbi.js?aid=' + sbi_aid + '&as=null'; 99 | return ''; 100 | } 101 | 102 | return { 103 | callBids: _phone_in, 104 | formRequest: _keymaker, 105 | parseResponse: _trinity, 106 | success: _success, 107 | failure: _failure 108 | }; 109 | }; 110 | 111 | module.exports = SonobiAdapter; -------------------------------------------------------------------------------- /gulpHelpers.js: -------------------------------------------------------------------------------- 1 | // this will have all of a copy of the normal fs methods as well 2 | const fs = require('fs.extra'); 3 | const path = require('path'); 4 | const argv = require('yargs').argv; 5 | const MANIFEST = 'package.json'; 6 | const exec = require('child_process').exec; 7 | 8 | module.exports = { 9 | parseBrowserArgs: function (argv) { 10 | return (argv.browsers) ? argv.browsers.split(',') : []; 11 | }, 12 | 13 | toCapitalCase: function (str) { 14 | return str.charAt(0).toUpperCase() + str.slice(1); 15 | }, 16 | 17 | jsonifyHTML: function (str) { 18 | console.log(arguments); 19 | return str.replace(/\n/g, '') 20 | .replace(/<\//g, '<\\/') 21 | .replace(/\/>/g, '\\/>'); 22 | }, 23 | 24 | /* 25 | * Get source files for analytics subdirectories in top-level `analytics` 26 | * directory adjacent to Prebid.js. 27 | * Invoke with gulp --analytics 28 | * Returns an array of source files for inclusion in build process 29 | */ 30 | getAnalyticsSources: function(directory) { 31 | if (!argv.analytics) {return [];} // empty arrays won't affect a standard build 32 | 33 | const directoryContents = fs.readdirSync(directory); 34 | return directoryContents 35 | .filter(file => isModuleDirectory(path.join(directory, file))) 36 | .map(moduleDirectory => { 37 | const module = require(path.join(directory, moduleDirectory, MANIFEST)); 38 | return path.join(directory, moduleDirectory, module.main); 39 | }); 40 | 41 | // get only subdirectories that contain package.json with 'main' property 42 | function isModuleDirectory(filePath) { 43 | try { 44 | const manifestPath = path.join(filePath, MANIFEST); 45 | if (fs.statSync(manifestPath).isFile()) { 46 | const module = require(manifestPath); 47 | return module && module.main; 48 | } 49 | } 50 | catch (error) {} 51 | } 52 | }, 53 | 54 | createEnd2EndTestReport : function(targetDestinationDir) { 55 | var browsers = require('./browsers.json'); 56 | var env = ['default']; 57 | var input = 'bs'; 58 | for(var key in browsers) { 59 | if(key.substring(0, input.length) === input) { 60 | env.push(key); 61 | } 62 | } 63 | 64 | //create new directory structure 65 | fs.rmrfSync(targetDestinationDir); 66 | env.forEach(item => { 67 | fs.mkdirpSync(targetDestinationDir + '/' + item); 68 | }); 69 | 70 | //move xml files to newly created directory 71 | var walker = fs.walk('./build/coverage/e2e/reports'); 72 | walker.on("file", function (root, stat, next) { 73 | env.forEach(item => { 74 | if(stat.name.search(item) !== -1) { 75 | var src = root + '/' + stat.name; 76 | var dest = targetDestinationDir + '/' + item + '/' + stat.name; 77 | fs.copy(src, dest, {replace: true}, function(err) { 78 | if(err) { 79 | throw err; 80 | } 81 | }); 82 | } 83 | }); 84 | next(); 85 | }); 86 | 87 | //run junit-viewer to read xml and create html 88 | env.forEach(item => { 89 | //junit-viewer --results="./custom-reports/chrome51" --save="./chrome.html" 90 | var cmd = 'junit-viewer --results="' + targetDestinationDir + '/' + item + '" --save="' + targetDestinationDir + '/' + item +'.html"'; 91 | exec(cmd); 92 | }); 93 | 94 | //create e2e-results.html 95 | var html = 'End to End Testing Result
Note: Refresh in 2-3 seconds if it says "Cannot get ....."
'; 96 | var li = ''; 97 | var tabs = ''; 98 | env.forEach(function(item,i) { 99 | i++; 100 | li = li + '
  • '+item+'
  • '; 101 | tabs = tabs + '
    '; 102 | }); 103 | html = html + '
      ' + li + '
    ' + tabs; 104 | html = html + '
    '; 105 | 106 | var filepath = targetDestinationDir + '/results.html'; 107 | fs.openSync(filepath, 'w+'); 108 | fs.writeFileSync(filepath, html); 109 | } 110 | }; 111 | --------------------------------------------------------------------------------