diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index fd757dce1f5..1dc04a9c2b7 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -6,7 +6,7 @@ Thank you for your pull request. Please make sure this PR is scoped to one chang
- [ ] Bugfix
- [ ] Feature
-- [ ] New bidder adapter
+- [ ] New bidder adapter
- [ ] Code style update (formatting, local variables)
- [ ] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
diff --git a/.jshintignore b/.jshintignore
new file mode 100644
index 00000000000..dc3a5df5ff7
--- /dev/null
+++ b/.jshintignore
@@ -0,0 +1 @@
+# Ingnored files
diff --git a/CHANGELOG b/CHANGELOG
index d03338efd6e..26feedc6151 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,24 @@
+AOL Prebid 1.19.0
+----------------
+Updated to Prebid 0.21.0
+
+
+AOL Prebid 1.18.0
+----------------
+Updated to Prebid 0.20.0
+
+
+AOL Prebid 1.17.0
+----------------
+Added functionality for rendering pixels once.
+
+
+AOL Prebid 1.16.0
+----------------
+Added Audience Network adapter.
+Added creative key field in bid response for Sharethrough adapter.
+
+
AOL Prebid 1.15.0
----------------
Nexage API implemented.
diff --git a/README.md b/README.md
index c56023b7459..52b6fd33b56 100644
--- a/README.md
+++ b/README.md
@@ -36,6 +36,7 @@ This URL is designed specifically for the Container Tag library and it supports
[](http://isitmaintained.com/project/prebid/Prebid.js "Average time to resolve an issue")
[](https://codeclimate.com/github/prebid/Prebid.js)
[](https://coveralls.io/github/prebid/Prebid.js)
+[](https://david-dm.org/prebid/Prebid.js?type=dev)
# Prebid.js
@@ -181,6 +182,8 @@ Many SSPs, bidders, and publishers have contributed to this project. [20+ Bidder
To add a bidder adapter, see the instructions in [How to add a bidder adaptor](http://prebid.org/dev-docs/bidder-adaptor.html).
+Please **do NOT load Prebid.js inside your adapter**. If you do this, we will reject or remove your adapter as appropriate.
+
### Code Quality
Code quality is defined by `.jscs` and `.jshint` files and errors are reported in the terminal.
diff --git a/adapters.json b/adapters.json
index 41948e5920d..bf65a6d033d 100644
--- a/adapters.json
+++ b/adapters.json
@@ -1,23 +1,27 @@
[
"aardvark",
"adblade",
+ "adbund",
"adbutler",
"adequant",
"adform",
"adkernel",
"admedia",
+ "bidfluence",
"vertamedia",
"aol",
"appnexus",
"appnexusAst",
+ "audienceNetwork",
"conversant",
"districtmDMX",
"fidelity",
- "getintent",
"gumgum",
"hiromedia",
"indexExchange",
"kruxlink",
+ "getintent",
+ "inneractive",
"komoona",
"lifestreet",
"mantis",
@@ -25,14 +29,17 @@
"piximedia",
"pubmatic",
"pulsepoint",
+ "pulsepointLite",
"rhythmone",
"rubicon",
"smartyads",
- "smartadserver",
+ "smartadserver",
"sekindoUM",
+ "serverbid",
"sonobi",
"sovrn",
"springserve",
+ "thoughtleadr",
"stickyadstv",
"triplelift",
"twenga",
@@ -51,6 +58,8 @@
"vertoz",
"widespace",
"admixer",
+ "atomx",
+ "tapsense",
{
"appnexus": {
"alias": "brealtime"
@@ -66,6 +75,11 @@
"alias": "defymedia"
}
},
+ {
+ "appnexus": {
+ "alias": "gourmetads"
+ }
+ },
{
"appnexusAst": {
"supportedMediaTypes": ["video"]
@@ -100,6 +114,20 @@
"adkernel": {
"alias": "headbidding"
}
+ },
+ {
+ "getintent": {
+ "supportedMediaTypes" : ["video"]
+ }
+ },
+ {
+ "stickyadstv": {
+ "alias": "freewheel-ssp"
+ }
+ },
+ {
+ "rhythmone": {
+ "supportedMediaTypes": ["video"]
+ }
}
-
]
diff --git a/analytics.json b/analytics.json
new file mode 100644
index 00000000000..36506bd9a4a
--- /dev/null
+++ b/analytics.json
@@ -0,0 +1 @@
+["aol"]
diff --git a/gulpHelpers.js b/gulpHelpers.js
index 342cd04dbb8..6091dc4c495 100644
--- a/gulpHelpers.js
+++ b/gulpHelpers.js
@@ -53,10 +53,10 @@ module.exports = {
createEnd2EndTestReport : function(targetDestinationDir) {
var browsers = require('./browsers.json');
- var env = ['default'];
+ var env = [];
var input = 'bs';
for(var key in browsers) {
- if(key.substring(0, input.length) === input) {
+ if(key.substring(0, input.length) === input && browsers[key].browser !== 'iphone') {
env.push(key);
}
}
diff --git a/gulpfile.js b/gulpfile.js
index ef7c459f6f1..1caafb647f1 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -21,6 +21,7 @@ var header = require('gulp-header');
var zip = require('gulp-zip');
var replace = require('gulp-replace');
var shell = require('gulp-shell');
+var optimizejs = require('gulp-optimize-js');
var CI_MODE = process.env.NODE_ENV === 'ci';
var prebid = require('./package.json');
@@ -90,6 +91,7 @@ gulp.task('webpack', function () {
}
}))
.pipe(header(banner, { prebid: prebid }))
+ .pipe(optimizejs())
// Remove window=window that was used to go around Uglify bug
.pipe(replace(/,?(\/\*!ADAPTER BEGIN \w+\*\/)\s*window\s*=\s*window/g, '$1'))
.pipe(replace(/,?(\/\*!ADAPTER END \w+\*\/)\s*window\s*=\s*window/g, '$1'))
@@ -239,45 +241,46 @@ gulp.task('docs', ['clean-docs'], function () {
});
gulp.task('e2etest', function() {
- var cmd = '--env default';
+ var cmdQueue = [];
if(argv.browserstack) {
var browsers = require('./browsers.json');
- var env = [];
- var input = 'bs';
- for(var key in browsers) {
- if(key.substring(0, input.length) === input) {
- env.push(key);
- }
+ delete browsers['bs_ie_9_windows_7'];
+
+ var cmdStr = ' --config nightwatch.conf.js';
+ if (argv.group) {
+ cmdStr = cmdStr + ' --group ' + argv.group;
}
- cmd = '--env default,' + env.join(',');
- }
+ cmdStr = cmdStr + ' --reporter ./test/spec/e2e/custom-reporter/pbjs-html-reporter.js';
- if(argv.browserstack) {
- cmd = cmd + ' --config nightwatch.conf.js';
- } else {
- cmd = cmd + ' --config nightwatch.json';
- }
+ var startWith = 'bs';
- if (argv.group) {
- cmd = cmd + ' --group ' + argv.group;
+ Object.keys(browsers).filter(function(v){
+ return v.substring(0, startWith.length) === startWith && browsers[v].browser !== 'iphone';
+ }).map(function(v,i,arr) {
+ var newArr = (i%2 === 0) ? arr.slice(i,i+2) : null;
+ if(newArr) {
+ var cmd = 'nightwatch --env ' + newArr.join(',') + cmdStr;
+ cmdQueue.push(cmd);
+ }
+ });
}
- cmd = cmd + ' --reporter ./test/spec/e2e/custom-reporter/pbjs-html-reporter.js';
return gulp.src('')
- .pipe(shell('nightwatch ' + cmd));
+ .pipe(shell(cmdQueue.join(';')));
});
gulp.task('e2etest-report', function() {
+ var reportPort = 9010;
var targetDestinationDir = './e2etest-report';
helpers.createEnd2EndTestReport(targetDestinationDir);
connect.server({
- port: port,
+ port: reportPort,
root: './',
livereload: true
});
setTimeout(function() {
- opens('http://localhost:' + port + '/' + targetDestinationDir.slice(2) + '/results.html');
+ opens('http://localhost:' + reportPort + '/' + targetDestinationDir.slice(2) + '/results.html');
}, 5000);
});
diff --git a/integrationExamples/gpt/audienceNetwork_dfp.html b/integrationExamples/gpt/audienceNetwork_dfp.html
new file mode 100644
index 00000000000..b30df31b276
--- /dev/null
+++ b/integrationExamples/gpt/audienceNetwork_dfp.html
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+ Prebid.js Test
+
+
+
+
+
Audience Network quick start
+
+ - Create a new App at https://developers.facebook.com/apps
+ - Add the Audience Network product to it
+ - Create a new Placement to generate your placementId
+ - To test, ensure the User-Agent request header represents a mobile device
+
+
+
+
diff --git a/integrationExamples/gpt/pbjs_example_gpt.html b/integrationExamples/gpt/pbjs_example_gpt.html
index f38d6dfc13b..5f3b25e1303 100644
--- a/integrationExamples/gpt/pbjs_example_gpt.html
+++ b/integrationExamples/gpt/pbjs_example_gpt.html
@@ -201,14 +201,14 @@
{
bidder: 'memeglobal',
params: {
- tagid: 007, // REQUIRED int. To get one, contact http://www.memeglobal.com
+ tagid: 007, // REQUIRED int. To get one, contact http://www.memeglobal.com
}
},
{
bidder: 'adblade',
params: {
partnerId: 42980,
- bidfloor: 0.01 // OPTIONAL float bid floor in $ CPM
+ bidfloor: 0.01 // OPTIONAL float bid floor in $ CPM
}
},
{
@@ -231,6 +231,13 @@
cur: 'EUR'
}
},
+ {
+ bidder: 'adbund',
+ params: {
+ sid: '110238', // REQUIRED int. To get one, sign up http://www.adbund.com
+ bidfloor: 0.036 // OPTIONAL float bid floor in $ CPM
+ }
+ },
{
bidder: 'smartyads',
params: {
@@ -242,6 +249,12 @@
params: {
zone: '2eb6bd58-865c-47ce-af7f-a918108c3fd2' // REQUIRED zone oid
}
+ },
+ {
+ bidder: 'atomx',
+ params: {
+ id: 7395
+ }
}
]
}, {
@@ -326,22 +339,22 @@
}
},
{
- bidder: 'wideorbit',
+ bidder: 'memeglobal',
params: {
- pbId: 123, // REQUIRED Publisher Id,
- pId: 123456 // REQUIRED Placement Id
+ tagid: 007, // REQUIRED int. To get one, contact http://www.memeglobal.com
}
},
{
- bidder: 'memeglobal',
+ bidder: 'gumgum',
params: {
- tagid: 007, // REQUIRED int. To get one, contact http://www.memeglobal.com
+ inScreen: 'ggumtest' // REQUIRED str Tracking Id
}
},
{
- bidder: 'gumgum',
+ bidder: 'wideorbit',
params: {
- inScreen: 'ggumtest' // REQUIRED str Tracking Id
+ pbId: 123, // REQUIRED Publisher Id,
+ pId: 123456 // REQUIRED Placement Id
}
}
]
@@ -352,7 +365,7 @@
$$PREBID_GLOBAL$$.addAdUnits(adUnits);
//register a callback handler
- $$PREBID_GLOBAL$$.addCallback('adUnitBidsBack', function (adUnitCode) {
+ $$PREBID_GLOBAL$$.addCallback('adUnitBidsBack', function (adUnitCode) {
console.debug('ad unit bids back for:', adUnitCode);
});
diff --git a/integrationExamples/gpt/x-domain/creative.html b/integrationExamples/gpt/x-domain/creative.html
index 68120e0e863..3b0058f2ee8 100644
--- a/integrationExamples/gpt/x-domain/creative.html
+++ b/integrationExamples/gpt/x-domain/creative.html
@@ -5,7 +5,7 @@
var urlParser = document.createElement('a');
urlParser.href = '%%PATTERN:url%%';
var publisherDomain = urlParser.protocol + '//' + urlParser.hostname;
-var adServerDomain = 'https://tpc.googlesyndication.com';
+var adServerDomain = urlParser.protocol + '//tpc.googlesyndication.com';
function renderAd(ev) {
var key = ev.message ? 'message' : 'data';
diff --git a/karma.conf.js b/karma.conf.js
index 91841543472..2c88288e191 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -44,7 +44,8 @@ module.exports = function (config) {
// list of files to exclude
exclude: [
- 'test/spec/loaders/**/*.js'
+ 'test/spec/loaders/**/*.js',
+ 'test/spec/adapters/underdogmedia_spec.js'
],
// preprocess matching files before serving them to the browser
diff --git a/loaders/adapterLoader.js b/loaders/adapterLoader.js
index 9b136593f94..2bdf38e8eb8 100644
--- a/loaders/adapterLoader.js
+++ b/loaders/adapterLoader.js
@@ -8,7 +8,7 @@ const fs = require('fs');
const blockLoader = require('block-loader');
const getAdapters = require('./getAdapters');
-const adapters = getAdapters();
+const adapters = getAdapters('adapters.json', 'adapters');
const files = fs.readdirSync('src/adapters').map((file) => file.replace(/\.[^/.]+$/, ''));
const adapterNames = adapters.filter(getStandardAdapters).filter(getUniques);
//adapters loaded from `srcPath`
@@ -46,13 +46,6 @@ function insertAdapters() {
}
});
-
- if (!inserts.length) {
- console.log('Prebid Warning: no matching adapters found for config, no adapters will be' +
- ' loaded.');
- return '';
- }
-
return '/*!ADAPTER REGISTER DELIMITER*/' + inserts.map(name => {
return `var ${adapterName(name)} = require('./adapters/${name}.js');
diff --git a/loaders/analyticsLoader.js b/loaders/analyticsLoader.js
index 38c7f2f6b86..7e76a2819d6 100644
--- a/loaders/analyticsLoader.js
+++ b/loaders/analyticsLoader.js
@@ -2,7 +2,8 @@
const fs = require('fs');
const blockLoader = require('block-loader');
-let analyticsAdapters = require('../package.json').analytics;
+const getAdapters = require('./getAdapters');
+let analyticsAdapters = getAdapters('analytics.json', 'analyticsAdapters');
var options = {
start: '/** INSERT ANALYTICS - DO NOT EDIT OR REMOVE */',
diff --git a/loaders/getAdapters.js b/loaders/getAdapters.js
index 1aeef7dde2e..b69a534fc7f 100644
--- a/loaders/getAdapters.js
+++ b/loaders/getAdapters.js
@@ -4,8 +4,6 @@ const fs = require('fs');
const path = require('path');
const argv = require('yargs').argv;
-const defaultAdapters = 'adapters.json';
-
function load(file) {
try {
const buffer = fs.readFileSync(file);
@@ -15,8 +13,8 @@ function load(file) {
}
}
-module.exports = function getAdapters() {
- let customAdapters = argv.adapters;
+module.exports = function getAdapters(defaultAdapters, argName) {
+ let customAdapters = argv[argName];
if (!customAdapters) {
return load(defaultAdapters);
@@ -29,7 +27,7 @@ module.exports = function getAdapters() {
return load(customAdapters);
} catch (e) {
console.log(`Prebid Warning: custom adapters config cannot be loaded from ${customAdapters}, ` +
- 'using default adapters.json');
+ `using default ${defaultAdapters}`);
return load(defaultAdapters);
}
};
diff --git a/nightwatch.conf.js b/nightwatch.conf.js
index c0583fee4df..072114953fe 100644
--- a/nightwatch.conf.js
+++ b/nightwatch.conf.js
@@ -1,17 +1,35 @@
module.exports = (function(settings) {
var browsers = require('./browsers.json');
+ delete browsers['bs_ie_9_windows_7'];
+
for(var browser in browsers) {
+ if(browsers[browser].browser === 'iphone') continue;
+
var desiredCapabilities = {
"browserName": browsers[browser].browser,
"version": browsers[browser].browser_version,
- "platform": browsers[browser].os,
+ "platform": browsers[browser].os,
"os": browsers[browser].os,
- "os_version": browsers[browser].os_version,
- "browser": browsers[browser].browser,
- "browser_version": browsers[browser].browser_version,
+ "os_version": browsers[browser].os_version,
+ "browser": browsers[browser].browser,
+ "browser_version": browsers[browser].browser_version,
};
- settings.test_settings[browser] = {}
+ settings.test_settings[browser] = {
+ "silent": true,
+ "exclude":["custom-assertions","custom-commands","common","custom-reporter"],
+ "screenshots" : {
+ "enabled" : false,
+ "path" : ""
+ },
+ "javascriptEnabled": true,
+ "acceptSslCerts": true,
+ "browserstack.local": true,
+ "browserstack.debug": true,
+ "browserstack.selenium_version" : "2.53.0",
+ "browserstack.user": "${BROWSERSTACK_USERNAME}",
+ "browserstack.key": "${BROWSERSTACK_KEY}"
+ };
settings.test_settings[browser]['desiredCapabilities'] = desiredCapabilities;
}
return settings;
diff --git a/package.json b/package.json
index fb3037ec88b..e2d6783ee83 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "prebid.js",
- "version": "0.19.0",
+ "version": "0.21.0",
"description": "Header Bidding Management Library",
"main": "src/prebid.js",
"scripts": {
@@ -17,7 +17,6 @@
"prebid"
],
"globalVarName": "pbjs",
- "analytics": ["aol"],
"author": "the prebid.js contributors",
"contributors": [{
"name": "AOL Platforms",
@@ -27,9 +26,9 @@
"devDependencies": {
"babel-core": "^6.5.2",
"babel-loader": "^6.2.3",
- "babel-plugin-transform-object-assign": "^6.8.0",
- "babel-plugin-transform-es3-property-literals": "^6.8.0",
"babel-plugin-transform-es3-member-expression-literals": "^6.8.0",
+ "babel-plugin-transform-es3-property-literals": "^6.8.0",
+ "babel-plugin-transform-object-assign": "^6.8.0",
"babel-preset-es2015": "^6.5.0",
"block-loader": "^2.1.0",
"chai": "^3.3.0",
@@ -50,6 +49,7 @@
"gulp-jshint": "^1.8.4",
"gulp-karma": "0.0.4",
"gulp-mocha": "^2.2.0",
+ "gulp-optimize-js": "^1.1.0",
"gulp-rename": "^1.2.0",
"gulp-replace": "^0.4.0",
"gulp-shell": "^0.5.2",
@@ -94,7 +94,7 @@
"requirejs": "^2.1.20",
"sinon": "^1.12.1",
"string-replace-webpack-plugin": "0.0.3",
- "uglify-js": "^2.4.15",
+ "uglify-js": "^2.8.10",
"url-parse": "^1.0.5",
"webpack": "^1.12.3",
"webpack-stream": "^3.1.0",
diff --git a/src/adaptermanager.js b/src/adaptermanager.js
index 4c8ae95b3f7..9bb18cfec12 100644
--- a/src/adaptermanager.js
+++ b/src/adaptermanager.js
@@ -26,9 +26,10 @@ function getBids({bidderCode, requestId, bidderRequestId, adUnits}) {
}
sizes = sizeMapping;
}
- return Object.assign(bid, {
+ return Object.assign({}, bid, {
placementCode: adUnit.code,
mediaType: adUnit.mediaType,
+ transactionId : adUnit.transactionId,
sizes: sizes,
bidId: utils.getUniqueIdentifierStr(),
bidderRequestId,
diff --git a/src/adapters/adbund.js b/src/adapters/adbund.js
new file mode 100644
index 00000000000..74809aa5e04
--- /dev/null
+++ b/src/adapters/adbund.js
@@ -0,0 +1,66 @@
+var CONSTANTS = require('../constants.json');
+var utils = require('../utils.js');
+var bidfactory = require('../bidfactory.js');
+var bidmanager = require('../bidmanager.js');
+var adloader = require('../adloader');
+
+var adBundAdapter = function adBundAdapter() {
+ var timezone = (new Date()).getTimezoneOffset();
+ var bidAPIs = [
+ 'http://us-east-engine.adbund.xyz/prebid/ad/get',
+ 'http://us-west-engine.adbund.xyz/prebid/ad/get'
+ ];
+ //Based on the time zone to select the interface to the server
+ var bidAPI = bidAPIs[timezone < 0 ? 0 : 1];
+
+ function _stringify(param) {
+ var result = [];
+ var key;
+ for (key in param) {
+ if (param.hasOwnProperty(key)) {
+ result.push(key + '=' + encodeURIComponent(param[key]));
+ }
+ }
+ return result.join('&');
+ }
+
+ function _createCallback(bid) {
+ return function (data) {
+ var response;
+ if (data && data.cpm) {
+ response = bidfactory.createBid(CONSTANTS.STATUS.GOOD);
+ response.bidderCode = 'adbund';
+ Object.assign(response, data);
+ } else {
+ response = bidfactory.createBid(CONSTANTS.STATUS.NO_BID);
+ response.bidderCode = 'adbund';
+ }
+ bidmanager.addBidResponse(bid.placementCode, response);
+ };
+ }
+
+ function _requestBids(bid) {
+ var info = {
+ referrer: utils.getTopWindowUrl(),
+ domain: utils.getTopWindowLocation().hostname,
+ ua: window.navigator.userAgent
+ };
+ var param = Object.assign({}, bid.params, info);
+ param.sizes = JSON.stringify(param.sizes || bid.sizes);
+ param.callback = '$$PREBID_GLOBAL$$.adbundResponse';
+ $$PREBID_GLOBAL$$.adbundResponse = _createCallback(bid);
+ adloader.loadScript(bidAPI + '?' + _stringify(param));
+ }
+
+ function _callBids(params) {
+ (params.bids || []).forEach(function (bid) {
+ _requestBids(bid);
+ });
+ }
+
+ return {
+ callBids: _callBids
+ };
+};
+
+module.exports = adBundAdapter;
\ No newline at end of file
diff --git a/src/adapters/adkernel.js b/src/adapters/adkernel.js
index b3383f44e9b..99653c6b482 100644
--- a/src/adapters/adkernel.js
+++ b/src/adapters/adkernel.js
@@ -65,6 +65,7 @@ const AdKernelAdapter = function AdKernelAdapter() {
try {
document.body.appendChild(iframe);
} catch (error) {
+ /* istanbul ignore next */
utils.logError(error);
}
}
@@ -176,7 +177,7 @@ const AdKernelAdapter = function AdKernelAdapter() {
* Create bid object for the bid manager
*/
function createBidObject(resp, bid, width, height) {
- return utils.extend(bidfactory.createBid(1, bid), {
+ return Object.assign(bidfactory.createBid(1, bid), {
bidderCode: bid.bidder,
ad: formatAdMarkup(resp),
width: width,
@@ -189,7 +190,7 @@ const AdKernelAdapter = function AdKernelAdapter() {
* Create empty bid object for the bid manager
*/
function createEmptyBidObject(bid) {
- return utils.extend(bidfactory.createBid(2, bid), {
+ return Object.assign(bidfactory.createBid(2, bid), {
bidderCode: bid.bidder
});
}
@@ -200,7 +201,7 @@ const AdKernelAdapter = function AdKernelAdapter() {
function formatAdMarkup(bid) {
var adm = bid.adm;
if ('nurl' in bid) {
- adm += utils.createTrackPixelHtml(bid.nurl);
+ adm += utils.createTrackPixelHtml(`${bid.nurl}&px=1`);
}
return adm;
}
diff --git a/src/adapters/analytics/aol.js b/src/adapters/analytics/aol.js
index 14c89c1f443..c1eeed8ad70 100644
--- a/src/adapters/analytics/aol.js
+++ b/src/adapters/analytics/aol.js
@@ -33,7 +33,7 @@ let auctionSchemaTemplate = template`;pubadid=${'pubadid'};hbauctionid=${'hbauct
let winSchemaTemplate = template`;hbauctioneventts=${'hbauctioneventts'};pubadid=${'pubadid'};hbauctionid=${'hbauctionid'};hbwinner=${'hbwinner'};pubcpm=${'pubcpm'}${'hbdealid'};hbbidid=${'hbbidid'}`;
let bidderSchemaTemplate = template`;hbbidder=${'hbbidder'};hbbid=${'hbbid'};hbstatus=${'hbstatus'};hbtime=${'hbtime'}${'hbdealid'};hbbidid=${'hbbidid'}`;
-export default utils.extend(adapter({
+export default Object.assign(adapter({
url: '',
analyticsType
}), {
diff --git a/src/adapters/analytics/aolPartnersIds.json b/src/adapters/analytics/aolPartnersIds.json
index cd2e2115c80..ab5e74c0f51 100644
--- a/src/adapters/analytics/aolPartnersIds.json
+++ b/src/adapters/analytics/aolPartnersIds.json
@@ -55,5 +55,14 @@
"twenga": 54,
"lifestreet": 55,
"vertamedia": 56,
- "stickyadstv": 57
+ "stickyadstv": 57,
+ "audienceNetwork": 58,
+ "thoughtleadr": 59,
+ "adbund": 60,
+ "tapsense": 61,
+ "serverbid": 62,
+ "bidfluence": 63,
+ "pulsepointLite": 64,
+ "atomx": 65,
+ "inneractive": 66
}
diff --git a/src/adapters/analytics/example2.js b/src/adapters/analytics/example2.js
index 5e5c0dfc197..b9586b5c59a 100644
--- a/src/adapters/analytics/example2.js
+++ b/src/adapters/analytics/example2.js
@@ -5,12 +5,11 @@ import { ajax } from 'src/ajax';
*/
import adapter from 'AnalyticsAdapter';
-const utils = require('../../utils');
const url = 'https://httpbin.org/post';
const analyticsType = 'endpoint';
-export default utils.extend(adapter(
+export default Object.assign(adapter(
{
url,
analyticsType
diff --git a/src/adapters/analytics/roxot.js b/src/adapters/analytics/roxot.js
index e00ef05c20f..2b71dda84db 100644
--- a/src/adapters/analytics/roxot.js
+++ b/src/adapters/analytics/roxot.js
@@ -6,7 +6,7 @@ const utils = require('../../utils');
const url = '//d.rxthdr.com/analytics';
const analyticsType = 'endpoint';
-export default utils.extend(adapter(
+export default Object.assign(adapter(
{
url,
analyticsType
diff --git a/src/adapters/analytics/sharethrough_analytics.js b/src/adapters/analytics/sharethrough_analytics.js
index 6b4f9297346..6eadcbcf60c 100644
--- a/src/adapters/analytics/sharethrough_analytics.js
+++ b/src/adapters/analytics/sharethrough_analytics.js
@@ -6,7 +6,7 @@ const analyticsType = 'endpoint';
const STR_BIDDER_CODE = "sharethrough";
const STR_VERSION = "0.1.0";
-export default utils.extend(adapter(
+export default Object.assign(adapter(
{
emptyUrl,
analyticsType
diff --git a/src/adapters/aol.js b/src/adapters/aol.js
index 687bfab4efc..febaf222c7c 100644
--- a/src/adapters/aol.js
+++ b/src/adapters/aol.js
@@ -3,7 +3,10 @@ const ajax = require('../ajax.js').ajax;
const bidfactory = require('../bidfactory.js');
const bidmanager = require('../bidmanager.js');
const constants = require('../constants.json');
-const events = require('src/events');
+
+$$PREBID_GLOBAL$$.aolGlobals = {
+ pixelsDropped: false
+};
const AolAdapter = function AolAdapter() {
@@ -23,7 +26,7 @@ const AolAdapter = function AolAdapter() {
img: 'IMG'
};
- let DOMReady = (() => {
+ let domReady = (() => {
let readyEventFired = false;
return fn => {
let idempotentFn = () => {
@@ -67,8 +70,11 @@ const AolAdapter = function AolAdapter() {
})();
function dropSyncCookies(pixels) {
- let pixelElements = parsePixelItems(pixels);
- renderPixelElements(pixelElements);
+ if (!$$PREBID_GLOBAL$$.aolGlobals.pixelsDropped) {
+ let pixelElements = parsePixelItems(pixels);
+ renderPixelElements(pixelElements);
+ $$PREBID_GLOBAL$$.aolGlobals.pixelsDropped = true;
+ }
}
function parsePixelItems(pixels) {
@@ -122,7 +128,7 @@ const AolAdapter = function AolAdapter() {
document.readyState === 'complete') {
document.body.appendChild(iframe);
} else {
- DOMReady(() => {
+ domReady(() => {
document.body.appendChild(iframe);
});
}
@@ -227,7 +233,11 @@ const AolAdapter = function AolAdapter() {
if (bid.params.userSyncOn === constants.EVENTS.BID_RESPONSE) {
dropSyncCookies(response.ext.pixels);
} else {
- ad += response.ext.pixels;
+ let formattedPixels = response.ext.pixels.replace(/<\/?script( type=('|")text\/javascript('|")|)?>/g, '');
+
+ ad += '';
}
}
@@ -250,9 +260,9 @@ const AolAdapter = function AolAdapter() {
function _isNexageRequestPost(bid) {
if (bid.params.id && bid.params.imp && bid.params.imp[0]) {
let imp = bid.params.imp[0];
- return imp.id && imp.tagid
- && ((imp.banner && imp.banner.w && imp.banner.h)
- || (imp.video && imp.video.mimes && imp.video.minduration && imp.video.maxduration));
+ return imp.id && imp.tagid &&
+ ((imp.banner && imp.banner.w && imp.banner.h) ||
+ (imp.video && imp.video.mimes && imp.video.minduration && imp.video.maxduration));
}
}
diff --git a/src/adapters/appnexus.js b/src/adapters/appnexus.js
index 4e60c01826a..0a3b8f7284b 100644
--- a/src/adapters/appnexus.js
+++ b/src/adapters/appnexus.js
@@ -98,7 +98,7 @@ AppNexusAdapter = function AppNexusAdapter() {
}
//append custom attributes:
- var paramsCopy = utils.extend({}, bid.params);
+ var paramsCopy = Object.assign({}, bid.params);
//delete attributes already used
delete paramsCopy.placementId;
diff --git a/src/adapters/appnexusAst.js b/src/adapters/appnexusAst.js
index 3258eeb7821..5888811ae9b 100644
--- a/src/adapters/appnexusAst.js
+++ b/src/adapters/appnexusAst.js
@@ -235,6 +235,7 @@ function AppnexusAstAdapter() {
if (ad && status === STATUS.GOOD) {
bid.cpm = ad.cpm;
bid.creative_id = ad.creative_id;
+ bid.dealId = ad.deal_id;
if (ad.rtb.video) {
bid.width = ad.rtb.video.player_width;
diff --git a/src/adapters/atomx.js b/src/adapters/atomx.js
new file mode 100644
index 00000000000..4935382123a
--- /dev/null
+++ b/src/adapters/atomx.js
@@ -0,0 +1,75 @@
+var CONSTANTS = require('../constants.json');
+var bidfactory = require('../bidfactory.js');
+var bidmanager = require('../bidmanager.js');
+var adloader = require('src/adloader.js');
+var Ajax = require('../ajax');
+var utils = require('../utils.js');
+
+/**
+ * Adapter for requesting bids from Atomx.
+ *
+ * @returns {{callBids: _callBids, responseCallback: _responseCallback}}
+ */
+var AtomxAdapter = function AtomxAdapter() {
+ function _callBids(data) {
+ if (!window.atomx_prebid) {
+ adloader.loadScript(window.location.protocol + '//s.ato.mx/b.js', function() { _bid(data); }, true);
+ } else {
+ _bid(data);
+ }
+ }
+
+ function _bid(data) {
+ var url = window.atomx_prebid();
+ var bids = data.bids || [];
+ for (var i = 0, ln = bids.length; i < ln; i++) {
+ var bid = bids[i];
+ if (bid.params && bid.params.id) {
+ Ajax.ajax(url, _responseCallback.bind(this, bid), {
+ id: bid.params.id,
+ size: utils.parseSizesInput(bid.sizes)[0],
+ prebid: bid.placementCode
+ }, {method: 'GET'});
+ } else {
+ var bidObject = bidfactory.createBid(CONSTANTS.STATUS.NO_BID, bid);
+ bidObject.bidderCode = 'atomx';
+ bidmanager.addBidResponse(bid.placementCode, bidObject);
+ }
+ }
+ }
+
+ function _responseCallback(bid, data) {
+ var bidObject;
+ try {
+ data = JSON.parse(data);
+
+ if (data.cpm && data.cpm > 0) {
+ bidObject = bidfactory.createBid(CONSTANTS.STATUS.GOOD, bid);
+ bidObject.bidderCode = 'atomx';
+ bidObject.cpm = data.cpm * 1000;
+ if (data.adm) {
+ bidObject.ad = data.adm;
+ } else {
+ bidObject.adUrl = data.url;
+ }
+ bidObject.width = data.width;
+ bidObject.height = data.height;
+ bidmanager.addBidResponse(bid.placementCode, bidObject);
+ return;
+ }
+ } catch (_error) {
+ utils.logError(_error);
+ }
+
+ bidObject = bidfactory.createBid(CONSTANTS.STATUS.NO_BID, bid);
+ bidObject.bidderCode = 'atomx';
+ bidmanager.addBidResponse(bid.placementCode, bidObject);
+ }
+
+ return {
+ callBids: _callBids,
+ responseCallback: _responseCallback
+ };
+};
+
+module.exports = AtomxAdapter;
diff --git a/src/adapters/audienceNetwork.js b/src/adapters/audienceNetwork.js
new file mode 100644
index 00000000000..8ab96430491
--- /dev/null
+++ b/src/adapters/audienceNetwork.js
@@ -0,0 +1,208 @@
+/**
+ * @file AudienceNetwork adapter.
+ */
+import { ajax } from '../ajax';
+import { createBid } from '../bidfactory';
+import { addBidResponse } from '../bidmanager';
+import { STATUS } from '../constants.json';
+import { format } from '../url';
+import { logError } from '../utils';
+import { createNew } from './adapter';
+
+const baseAdapter = createNew('audienceNetwork');
+const setBidderCode = baseAdapter.setBidderCode;
+const getBidderCode = baseAdapter.getBidderCode;
+
+/**
+ * Does this bid request contain valid parameters?
+ * @param {Object} bid
+ * @returns {Boolean}
+ */
+const validateBidRequest = bid =>
+ typeof bid.params === 'object' &&
+ typeof bid.params.placementId === 'string' &&
+ bid.params.placementId.length > 0 &&
+ Array.isArray(bid.sizes) && bid.sizes.length > 0;
+
+/**
+ * Does this bid request contain valid sizes?
+ * @param {Object} bid
+ * @returns {Boolean}
+ */
+const validateBidRequestSizes = bid => {
+ bid.sizes = bid.sizes.map(flattenSize);
+ return bid.sizes.every( size =>
+ ['native', 'fullwidth', '300x250', '320x50'].includes(size) );
+};
+
+/**
+ * Flattens a 2-element [W, H] array as a 'WxH' string,
+ * otherwise passes value through.
+ * @params {Array|String} size
+ * @returns {String}
+ */
+const flattenSize = size =>
+ (Array.isArray(size) && size.length === 2) ? `${size[0]}x${size[1]}` : size;
+
+/**
+ * Does the search part of the URL contain "anhb_testmode"
+ * and therefore indicate testmode should be used?
+ * @returns {String} "true" or "false"
+ */
+const isTestmode = () => Boolean(
+ window && window.location &&
+ typeof window.location.search === 'string' &&
+ window.location.search.indexOf('anhb_testmode') !== -1
+).toString();
+
+/**
+ * Parse JSON-as-string into an Object, default to empty.
+ * @param {String} JSON-as-string
+ * @returns {Object}
+ */
+const parseJson = jsonAsString => {
+ let data = {};
+ try {
+ data = JSON.parse(jsonAsString);
+ } catch (err) {}
+ return data;
+};
+
+/**
+ * Is this a native advert size?
+ * @param {String} size
+ * @returns {Boolean}
+ */
+const isNative = (size) => ['native', 'fullwidth'].includes(size);
+
+/**
+ * Generate ad HTML for injection into an iframe
+ * @param {String} placementId
+ * @param {String} size
+ * @param {String} bidId
+ * @returns {String} HTML
+ */
+const createAdHtml = (placementId, size, bidId) => {
+ const nativeStyle = isNative(size) ? '' : '';
+ const nativeContainer = isNative(size) ? '' : '';
+ return `${nativeStyle}
+
+${nativeContainer}
`;
+};
+
+/**
+ * Creates a "good" Bid object with the given bid ID and CPM.
+ * @param {String} placementId
+ * @param {String} bidId
+ * @param {String} size
+ * @param {Number} cpmCents
+ * @returns {Object} Bid
+ */
+const createSuccessBidResponse = (placementId, size, bidId, cpmCents) => {
+ const bid = createBid(STATUS.GOOD, { bidId });
+ // Prebid attributes
+ bid.bidderCode = getBidderCode();
+ bid.cpm = cpmCents / 100;
+ bid.ad = createAdHtml(placementId, size, bidId);
+ if (!isNative(size)) {
+ [bid.width, bid.height] = size.split('x').map(Number);
+ }
+ // Audience Network attributes
+ bid.hb_bidder = 'fan';
+ bid.fb_bidid = bidId;
+ bid.fb_format = size;
+ bid.fb_placementid = placementId;
+ return bid;
+};
+
+/**
+ * Creates a "no bid" Bid object.
+ * @returns {Object} Bid
+ */
+const createFailureBidResponse = () => {
+ const bid = createBid(STATUS.NO_BID);
+ bid.bidderCode = getBidderCode();
+ return bid;
+};
+
+/**
+ * Fetch bids for given parameters.
+ * @param {Object} bidRequest
+ * @param {Array} params.bids - list of bids
+ * @param {String} params.bids[].placementCode - Prebid placement identifier
+ * @param {Object} params.bids[].params
+ * @param {String} params.bids[].params.placementId - Audience Network placement identifier
+ * @param {Array} params.bids[].sizes - list of accepted advert sizes
+ * @param {Array|String} params.bids[].sizes[] - one of 'native', '300x250', '300x50', [300, 250], [300, 50]
+ * @returns {void}
+ */
+const callBids = bidRequest => {
+ // Build lists of adUnitCodes, placementids and adformats
+ const adUnitCodes = [];
+ const placementids = [];
+ const adformats = [];
+ bidRequest.bids
+ .filter(validateBidRequest)
+ .filter(validateBidRequestSizes)
+ .forEach( bid => bid.sizes.forEach( size => {
+ adUnitCodes.push(bid.placementCode);
+ placementids.push(bid.params.placementId);
+ adformats.push(size);
+ }));
+
+ if (placementids.length) {
+ // Build URL
+ const testmode = isTestmode();
+ const url = format({
+ protocol: 'https',
+ host: 'an.facebook.com',
+ pathname: '/v2/placementbid.json',
+ search: {
+ sdk: '5.5.web',
+ testmode,
+ placementids,
+ adformats
+ }
+ });
+ // Request
+ ajax(url, res => {
+ // Handle response
+ const data = parseJson(res);
+ if (data.errors && data.errors.length) {
+ const noBid = createFailureBidResponse();
+ adUnitCodes.forEach( adUnitCode => addBidResponse(adUnitCode, noBid) );
+ data.errors.forEach(logError);
+ } else {
+ // For each placementId in bids Object
+ Object.keys(data.bids)
+ // extract Array of bid responses
+ .map( placementId => data.bids[placementId] )
+ // flatten
+ .reduce( (a, b) => a.concat(b), [] )
+ // call addBidResponse
+ .forEach( (bid, i) =>
+ addBidResponse(adUnitCodes[i], createSuccessBidResponse(
+ bid.placement_id, adformats[i], bid.bid_id, bid.bid_price_cents
+ ))
+ );
+ }
+ }, null, { withCredentials: true });
+ } else {
+ // No valid bids
+ logError('No valid bids requested');
+ }
+};
+
+/**
+ * @class AudienceNetwork
+ * @type {Object}
+ * @property {Function} callBids - fetch bids for given parameters
+ * @property {Function} setBidderCode - used for bidder aliasing
+ * @property {Function} getBidderCode - unique 'audienceNetwork' identifier
+ */
+const AudienceNetwork = () => {
+ return { callBids, setBidderCode, getBidderCode };
+};
+module.exports = AudienceNetwork;
diff --git a/src/adapters/bidfluence.js b/src/adapters/bidfluence.js
new file mode 100644
index 00000000000..e5253e09878
--- /dev/null
+++ b/src/adapters/bidfluence.js
@@ -0,0 +1,57 @@
+const bidmanager = require('../bidmanager.js');
+const bidfactory = require('../bidfactory.js');
+const utils = require('../utils.js');
+const adloader = require('../adloader');
+
+var BidfluenceAdapter = function BidfluenceAdapter() {
+
+ const scriptUrl = "//bidfluence.azureedge.net/forge.js";
+
+ $$PREBID_GLOBAL$$.bfPbjsCB = function (bfr) {
+ var bidRequest = utils.getBidRequest(bfr.cbID);
+ var bidObject = null;
+ if (bfr.cpm > 0) {
+ bidObject = bidfactory.createBid(1, bidRequest);
+ bidObject.bidderCode = 'bidfluence';
+ bidObject.cpm = bfr.cpm;
+ bidObject.ad = bfr.ad;
+ bidObject.width = bfr.width;
+ bidObject.height = bfr.height;
+ } else {
+ bidObject = bidfactory.createBid(2, bidRequest);
+ bidObject.bidderCode = 'bidfluence';
+ }
+
+ bidmanager.addBidResponse(bfr.placementCode, bidObject);
+ };
+
+ function _callBids(params) {
+ var bfbids = params.bids || [];
+ for (var i = 0; i < bfbids.length; i++) {
+ var bid = bfbids[i];
+ call(bid);
+ }
+ }
+ function call(bid) {
+
+ var adunitId = utils.getBidIdParameter('adunitId', bid.params);
+ var publisherId = utils.getBidIdParameter('pubId', bid.params);
+ var reservePrice = utils.getBidIdParameter('reservePrice', bid.params);
+ var pbjsBfobj = {
+ placementCode: bid.placementCode,
+ cbID: bid.bidId
+ };
+
+ var cb = function () {
+ /* globals FORGE */
+ FORGE.init([adunitId, publisherId, pbjsBfobj, reservePrice]);
+ };
+
+ adloader.loadScript(scriptUrl, cb);
+ }
+ return {
+ callBids: _callBids
+ };
+};
+
+module.exports = BidfluenceAdapter;
diff --git a/src/adapters/centro.js b/src/adapters/centro.js
index 7e7dc100c77..85303afc823 100644
--- a/src/adapters/centro.js
+++ b/src/adapters/centro.js
@@ -39,12 +39,10 @@ var CentroAdapter = function CentroAdapter() {
utils.logError(LOG_ERROR_MESS.noUnit, bidderCode);
return;
}
- var query = ['s=' + bid.unit];//,'url=www.abc15.com','sz=320x50'];
+ var query = ['s=' + bid.unit, 'adapter=prebid'];//,'url=www.abc15.com','sz=320x50'];
var isDev = bid.unit.toString() === '28136';
- if (bid.page_url) {
- query.push('url=' + encodeURIComponent(bid.page_url));
- }
+ query.push('url=' + encodeURIComponent(bid.page_url || location.href));
//check size format
if (
size instanceof Array &&
@@ -55,8 +53,8 @@ var CentroAdapter = function CentroAdapter() {
query.push('sz=' + size.join('x'));
}
//make handler name for JSONP request
- var handlerName = handlerPrefix + bid.unit + size.join('x');
- query.push('callback=' + handlerName);
+ var handlerName = handlerPrefix + bid.unit + size.join('x') + encodeURIComponent(placementCode);
+ query.push('callback=' + encodeURIComponent('window["' + handlerName + '"]'));
//maybe is needed add some random parameter to disable cache
//query.push('r='+Math.round(Math.random() * 1e5));
@@ -77,7 +75,7 @@ var CentroAdapter = function CentroAdapter() {
var bidObject;
var bid = resp && resp.bid || resp;
- if (bid && bid.adTag && bid.sectionID === unit) {
+ if (bid && bid.adTag && bid.sectionID && bid.sectionID.toString() === unit.toString()) {
bidObject = bidfactory.createBid(1);
bidObject.cpm = bid.value;
bidObject.ad = bid.adTag;
diff --git a/src/adapters/getintent.js b/src/adapters/getintent.js
index 1293d98f903..a98fab952ae 100644
--- a/src/adapters/getintent.js
+++ b/src/adapters/getintent.js
@@ -1,5 +1,7 @@
/*jshint loopfunc: true */
+import { STATUS } from 'src/constants';
+
var bidfactory = require('../bidfactory.js');
var bidmanager = require('../bidmanager.js');
var adloader = require('../adloader.js');
@@ -33,25 +35,34 @@ var GetIntentAdapter = function GetIntentAdapter() {
pid: bidRequest.params.pid, // required
tid: bidRequest.params.tid, // required
known: bidRequest.params.known || 1,
+ is_video: bidRequest.mediaType === 'video',
+ video: bidRequest.params.video || {},
size: bidRequest.sizes[0].join("x"),
};
addOptional(bidRequest.params, request, ['cur', 'floor']);
- window.gi_hb.makeBid(request, function(bidResponse) {
- if (bidResponse.no_bid === 1) {
- var nobid = bidfactory.createBid(2);
- nobid.bidderCode = bidRequest.bidder;
- bidmanager.addBidResponse(bidRequest.placementCode, nobid);
- } else {
- var size = bidResponse.size.split('x');
- var bid = bidfactory.createBid(1);
- bid.bidderCode = bidRequest.bidder;
- bid.cpm = bidResponse.cpm;
- bid.ad = bidResponse.ad;
- bid.width = size[0];
- bid.height = size[1];
- bidmanager.addBidResponse(bidRequest.placementCode, bid);
- }
- });
+ (function (r, br) {
+ window.gi_hb.makeBid(r, function(bidResponse) {
+ if (bidResponse.no_bid === 1) {
+ var nobid = bidfactory.createBid(STATUS.NO_BID);
+ nobid.bidderCode = br.bidder;
+ bidmanager.addBidResponse(br.placementCode, nobid);
+ } else {
+ var bid = bidfactory.createBid(STATUS.GOOD);
+ var size = bidResponse.size.split('x');
+ bid.bidderCode = br.bidder;
+ bid.cpm = bidResponse.cpm;
+ bid.width = size[0];
+ bid.height = size[1];
+ if (br.mediaType === 'video') {
+ bid.vastUrl = bidResponse.vast_url;
+ bid.descriptionUrl = bidResponse.vast_url;
+ } else {
+ bid.ad = bidResponse.ad;
+ }
+ bidmanager.addBidResponse(br.placementCode, bid);
+ }
+ });
+ })(request, bidRequest);
}
}
diff --git a/src/adapters/inneractive.js b/src/adapters/inneractive.js
new file mode 100644
index 00000000000..a4b24eeceec
--- /dev/null
+++ b/src/adapters/inneractive.js
@@ -0,0 +1,462 @@
+import * as utils from '../utils';
+import Adapter from './adapter';
+import {ajax} from '../ajax';
+import bidManager from 'src/bidmanager';
+import bidFactory from 'src/bidfactory';
+import {STATUS} from 'src/constants';
+import {formatQS} from '../url';
+
+/**
+ * @type {{IA_JS: string, ADAPTER_NAME: string, V: string, RECTANGLE_SIZE: {W: number, H: number}, SPOT_TYPES: {INTERSTITIAL: string, RECTANGLE: string, FLOATING: string, BANNER: string}, DISPLAY_AD: number, ENDPOINT_URL: string, EVENTS_ENDPOINT_URL: string, RESPONSE_HEADERS_NAME: {PRICING_VALUE: string, AD_H: string, AD_W: string}}}
+ */
+const CONSTANTS = {
+ ADAPTER_NAME: 'inneractive',
+ V: 'IA-JS-HB-PBJS-1.0',
+ RECTANGLE_SIZE:{W: 300, H: 250},
+
+ SPOT_TYPES: {
+ INTERSTITIAL: 'interstitial',
+ RECTANGLE: 'rectangle',
+ FLOATING: 'floating',
+ BANNER: 'banner'
+ },
+
+ DISPLAY_AD: 20,
+ ENDPOINT_URL: '//ad-tag.inner-active.mobi/simpleM2M/requestJsonAd',
+ EVENTS_ENDPOINT_URL: '//vast-events.inner-active.mobi/Event',
+ RESPONSE_HEADERS_NAME: {
+ PRICING_VALUE: 'X-IA-Pricing-Value',
+ AD_H: 'X-IA-Ad-Height',
+ AD_W: 'X-IA-Ad-Width'
+ }
+};
+
+let iaRef;
+try{
+ iaRef = window.top.document.referrer;
+}catch(e){
+ iaRef = window.document.referrer;
+}
+
+/**
+ * gloable util functions
+ * @type {{defaultsQsParams: {v: (string|string), page: string, mw: boolean, hb: string}, stringToCamel: (function(*)), objectToCamel: (function(*=))}}
+ */
+const Helpers = {
+ defaultsQsParams: {v: CONSTANTS.V,page: encodeURIComponent(utils.getTopWindowUrl()),mw: true, hb: 'prebidjs'},
+ /**
+ * Change string format from underscore to camelcase (e.g., APP_ID to appId)
+ * @param str: string
+ * @returns string
+ */
+ stringToCamel(str){
+ if(str.indexOf('_') === -1){
+ const first = str.charAt(0);
+ if(first !== first.toLowerCase()){
+ str = str.toLowerCase();
+ }
+ return str;
+ }
+
+ str = str.toLowerCase();
+ return str.replace(/(\_[a-z])/g, $1 => $1.toUpperCase().replace('_',''));
+ },
+
+ /**
+ * Change all object keys string format from underscore to camelcase (e.g., {'APP_ID' : ...} to {'appId' : ...})
+ * @param params: object
+ * @returns object
+ */
+ objectToCamel(params){
+ Object.keys(params).forEach(key => {
+ const keyCamelCase = this.stringToCamel(key);
+ if(keyCamelCase !== key){
+ params[keyCamelCase] = params[key];
+ delete params[key];
+ }
+ });
+ return params;
+ }
+};
+
+/**
+ * Tracking pixels for events
+ * @type {{fire: (function(*=))}}
+ */
+const Tracker = {
+ /**
+ * Creates a tracking pixel
+ * @param urls: Array
+ */
+ fire(urls){
+ urls.forEach(url => url && ((new Image(1,1)).src = encodeURI(url)));
+ }
+};
+
+/**
+ * Analytics
+ * @type {{errorEventName: string, pageProtocol: string, getPageProtocol: (function(): string), getEventUrl: (function(*, *=)), reportEvent: (function(string, Object)), defaults: {v: (string|string), page: string, mw: boolean, hb: string}, eventQueryStringParams: (function(Object): string), createTrackingPixel: (function(string))}}
+ */
+const Reporter = {
+ /**
+ * @private
+ */
+ errorEventName: 'HBPreBidError',
+ pageProtocol: '',
+
+ /**
+ * Gets the page protocol based on the document.location.protocol
+ * The returned string is either http:// or https://
+ * @returns {string}
+ */
+ getPageProtocol(){
+ if(!this.pageProtocol){
+ this.pageProtocol = ('http:' === utils.getTopWindowLocation().protocol ? 'http:' : 'https:');
+ }
+ return this.pageProtocol;
+ },
+
+ getEventUrl(evtName, extraDetails){
+ let eventsEndpoint = CONSTANTS.EVENTS_ENDPOINT_URL + '?table=' + ((evtName === this.errorEventName) ? 'mbwError' : 'mbwEvent');
+ let queryStringParams = this.eventQueryStringParams(extraDetails);
+ const appId = extraDetails && extraDetails.appId;
+ let queryStringParamsWithAID = `${queryStringParams}&aid=${appId}_${evtName}_other&evtName=${evtName}`;
+ return eventsEndpoint + '&' + queryStringParamsWithAID;
+ },
+
+ /**
+ * Reports an event to IA's servers.
+ * @param {string} evtName - event name as string.
+ * @param {object} extraDetails - e.g., a JS exception JSON object.
+ * @param shouldSendOnlyToNewEndpoint
+ */
+ reportEvent(evtName, extraDetails) {
+ const url = this.getEventUrl(evtName, extraDetails);
+ this.createTrackingPixel(url);
+ },
+ defaults: Helpers.defaultsQsParams,
+
+ /**
+ * Ia Event Reporting Query String Parameters, not including App Id.
+ * @param {object} extraDetails - e.g., a JS exception JSON object.
+ * @return {string} IA event contcatenated queryString parameters.
+ */
+ eventQueryStringParams(extraDetails) {
+ const toQS = Object.assign({}, this.defaults, {realAppId: extraDetails && extraDetails.appId, timestamp: Date.now()});
+ return formatQS(toQS);
+ },
+
+ /**
+ * Creates a tracking pixel by prepending the page's protocol to the URL sent as the param.
+ * @param {string} urlWithoutProtocol - the URL to send the tracking pixel to, without the protocol as a prefix.
+ */
+ createTrackingPixel(urlWithoutProtocol) {
+ Tracker.fire([this.getPageProtocol() + urlWithoutProtocol]);
+ }
+};
+
+/**
+ * Url generator - generates a request URL
+ * @type {{defaultsParams: *, serverParamNameBySettingParamName: {referrer: string, keywords: string, appId: string, portal: string, age: string, gender: string, isSecured: (boolean|null)}, toServerParams: (function(*)), unwantedValues: *[], getUrlParams: (function(*=))}}
+ */
+const Url = {
+ defaultsParams: Object.assign({}, Helpers.defaultsQsParams, {f: CONSTANTS.DISPLAY_AD,fs: false,ref: iaRef}),
+ serverParamNameBySettingParamName: {
+ referrer: 'ref',
+ keywords: 'k',
+ appId: 'aid',
+ portal: 'po',
+ age: 'a',
+ gender: 'g',
+ },
+ unwantedValues: ['', null, undefined],
+
+ /**
+ * Maps publisher params to server params
+ * @param params: object {k:v}
+ * @returns object {k:v}
+ */
+ toServerParams(params){
+ const serverParams = {};
+ for(const paramName in params){
+ if(params.hasOwnProperty(paramName) && this.serverParamNameBySettingParamName.hasOwnProperty(paramName)){
+ serverParams[this.serverParamNameBySettingParamName[paramName]] = params[paramName];
+ }else{
+ serverParams[paramName] = params[paramName];
+ }
+ }
+
+ serverParams.isSecured = Reporter.getPageProtocol() === 'https:' || null;
+ return serverParams;
+ },
+
+ /**
+ * Prepare querty string to ad server
+ * @param params: object {k:v}
+ * @returns : object {k:v}
+ */
+ getUrlParams(params){
+ const serverParams = this.toServerParams(params);
+ const toQueryString = Object.assign({}, this.defaultsParams, serverParams);
+ for(const paramName in toQueryString){
+ if(toQueryString.hasOwnProperty(paramName) && this.unwantedValues.indexOf(toQueryString[paramName]) !== -1){
+ delete toQueryString[paramName];
+ }
+ }
+ toQueryString.fs = params.spotType === CONSTANTS.SPOT_TYPES.INTERSTITIAL;
+
+ if(params.spotType === CONSTANTS.SPOT_TYPES.RECTANGLE){
+ toQueryString.rw = CONSTANTS.RECTANGLE_SIZE.W;
+ toQueryString.rh = CONSTANTS.RECTANGLE_SIZE.H;
+ }
+
+ if (typeof $$PREBID_GLOBAL$$ !== 'undefined') {
+ toQueryString.bco = $$PREBID_GLOBAL$$.cbTimeout || $$PREBID_GLOBAL$$.bidderTimeout;
+ }
+
+ toQueryString.timestamp = Date.now();
+ delete toQueryString.qa;
+ return toQueryString;
+ }
+};
+
+/**
+ * Http helper to extract metadata
+ * @type {{headers: *[], getBidHeaders: (function(*))}}
+ */
+const Http = {
+ headers: [
+ CONSTANTS.RESPONSE_HEADERS_NAME.PRICING_VALUE,
+ CONSTANTS.RESPONSE_HEADERS_NAME.AD_H,
+ CONSTANTS.RESPONSE_HEADERS_NAME.AD_W
+ ],
+
+ /**
+ * Extract headers data
+ * @param xhr: XMLHttpRequest
+ * @returns {}
+ */
+ getBidHeaders(xhr){
+ const headersData = {};
+ this.headers.forEach(headerName => headersData[headerName] = xhr.getResponseHeader(headerName));
+ return headersData;
+ }
+};
+
+
+/**
+ * InnerActiveAdapter for requesting bids
+ * @class
+ */
+class InnerActiveAdapter{
+ constructor(){
+ this.iaAdapter = Adapter.createNew(CONSTANTS.ADAPTER_NAME);
+ this.bidByBidId = {};
+ }
+
+ /**
+ * validate if bid request is valid
+ * @param adSettings: object
+ * @returns {boolean}
+ * @private
+ */
+ _isValidRequest(adSettings){
+ if(adSettings && adSettings.appId && adSettings.spotType){
+ return true;
+ }
+ utils.logError('bid requires appId');
+ return false;
+ }
+
+ /**
+ * Store the bids in a Map object (k: bidId, v: bid)to check later if won
+ * @param bid
+ * @returns bid object
+ * @private
+ */
+ _storeBidRequestDetails(bid){
+ this.bidByBidId[bid.bidId] = bid;
+ return bid;
+ }
+
+ /**
+ * @param bidStatus: int ("STATUS": {"GOOD": 1,"NO_BID": 2})
+ * @param bidResponse: object
+ * @returns {type[]}
+ * @private
+ */
+ _getBidDetails(bidStatus, bidResponse, bidId){
+ let bid = bidFactory.createBid(bidStatus, bidResponse);
+ bid.code = CONSTANTS.ADAPTER_NAME;
+ bid.bidderCode = bid.code;
+ if (bidStatus === STATUS.GOOD) {
+ bid = Object.assign(bid, bidResponse);
+ this._setBidCpm(bid, bidId);
+ }
+ return bid;
+ }
+
+ _setBidCpm(bid, bidId){
+ const storedBid = this.bidByBidId[bidId];
+ if(storedBid){
+ bid.cpm = storedBid.params && storedBid.params.qa && storedBid.params.qa.cpm || bid.cpm;
+ bid.cpm = (bid.cpm !== null && !isNaN(bid.cpm)) ? parseFloat(bid.cpm) : 0.0;
+ }
+ }
+
+ /**
+ * Validate if response is valid
+ * @param responseAsJson : object
+ * @param headersData: {}
+ * @returns {boolean}
+ * @private
+ */
+ _isValidBidResponse(responseAsJson, headersData){
+ return (responseAsJson && responseAsJson.ad && responseAsJson.ad.html && headersData && headersData[CONSTANTS.RESPONSE_HEADERS_NAME.PRICING_VALUE] > 0);
+ }
+
+ /**
+ * When response is received
+ * @param response: string(json format)
+ * @param xhr: XMLHttpRequest
+ * @param bidId: string
+ * @private
+ */
+ _onResponse(response, xhr, bidId){
+ const bid = this.bidByBidId[bidId];
+ const [w, h] = bid.sizes[0];
+ const size = {w, h};
+ let responseAsJson;
+ const headersData = Http.getBidHeaders(xhr);
+ try {
+ responseAsJson = JSON.parse(response);
+ } catch (error) {
+ utils.logError(error);
+ }
+
+ if (!this._isValidBidResponse(responseAsJson, headersData)) {
+ let errorMessage = `response failed for ${CONSTANTS.ADAPTER_NAME} adapter`;
+ utils.logError(errorMessage);
+ const passback = responseAsJson && responseAsJson.config && responseAsJson.config.passback;
+ if(passback) {
+ Tracker.fire([passback]);
+ }
+ Reporter.reportEvent('HBPreBidNoAd', bid.params);
+ return bidManager.addBidResponse(bid.placementCode, this._getBidDetails(STATUS.NO_BID));
+ }
+ const bidResponse = {
+ cpm: headersData[CONSTANTS.RESPONSE_HEADERS_NAME.PRICING_VALUE]*1000,
+ width: parseFloat(headersData[CONSTANTS.RESPONSE_HEADERS_NAME.AD_W]) || size.w,
+ ad: this._getAd(responseAsJson.ad.html, responseAsJson.config.tracking, bid.params),
+ height: parseFloat(headersData[CONSTANTS.RESPONSE_HEADERS_NAME.AD_H]) || size.h
+ };
+ const auctionBid = this._getBidDetails(STATUS.GOOD, bidResponse, bidId);
+ bid.adId = auctionBid.adId;
+ this.bidByBidId[bidId] = bid;
+ bidManager.addBidResponse(bid.placementCode, auctionBid);
+ }
+
+ /**
+ * Returns the ad HTML template
+ * @param adHtml: string {ad server creative}
+ * @param tracking: object {impressions, clicks}
+ * @param bidParams: object
+ * @returns {string}: create template
+ * @private
+ */
+ _getAd(adHtml, tracking, bidParams){
+
+ let impressionsHtml = '';
+ if(tracking && Array.isArray(tracking.impressions)){
+ let impressions = tracking.impressions;
+ impressions.push(Reporter.getEventUrl('HBPreBidImpression', bidParams, false));
+ impressions.forEach(impression => impression && (impressionsHtml += utils.createTrackPixelHtml(impression)));
+ }
+ adHtml = impressionsHtml + adHtml.replace(/
+
+
+
+
+ ${adHtml}
+
+
+