diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000000..9d8d5165620 --- /dev/null +++ b/.babelrc @@ -0,0 +1 @@ +{ "presets": ["es2015"] } diff --git a/.gitignore b/.gitignore index dc38ee1160a..1e4d5248000 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,18 @@ +# Built Files node_modules/ +build +# Test Files +test/app +gpt.html + +# Dev File + +integrationExamples/gpt/gpt.html +integrationExamples/implementations/ + +# Coverage reports +build/coverage/ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion diff --git a/.jscsrc b/.jscsrc new file mode 100644 index 00000000000..d4cbfa18c97 --- /dev/null +++ b/.jscsrc @@ -0,0 +1,11 @@ +{ + "maxErrors": 1000, + "esnext": true, + "requireTrailingComma": null, + "requireCamelCaseOrUpperCaseIdentifiers": null, + "requireSpacesInAnonymousFunctionExpression": null, + "validateIndentation": 2, + "disallowSpacesInFunctionDeclaration": { + "beforeOpeningRoundBrace": true + } +} diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000000..11b31057327 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,38 @@ +{ + "bitwise": false, + "browser": true, + "curly": false, + "devel": true, + "eqeqeq": true, + "freeze": true, + "immed": true, + "maxdepth": 5, + "newcap": true, + "noarg": true, + "node": true, + "notypeof": true, + "esnext": true, + "trailing": true, + "undef": true, + "unused": true, + "strict": false, + "scripturl": true, + "globals": { + "before": true, + "after": true, + "exports": true, + "pbjs": true, + "pbjsTestOnly": true, + "assert": false, + "expect": false, + "dump": false, + "describe": false, + "it": false, + "xit": false, + "pkg": false, + "sinon": false, + "beforeEach": false, + "afterEach": false, + "JSON": true + } +} diff --git a/README.md b/README.md index 12eba8789bb..ee2f789c6c4 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,13 @@ Prebid.js > Setup and manage header bidding advertising partners without writing code or confusing line items. Prebid.js is open source and free. -Many SSPs, bidders, and publishers have all contributed to this project. +Many SSPs, bidders, and publishers have all contributed to this project. -Check out the overview and documentation at http://prebid.org. +Check out the overview and documentation at http://prebid.org. No more week-long development. Header bidding is made easy by prebid.js :) -**Table of Contents** +**Table of Contents** - [Prebid.js](#) - [Usage](#usage) @@ -23,17 +23,17 @@ No more week-long development. Header bidding is made easy by prebid.js :) - [Configure](#configure) - [Run](#run) - + Usage ---------- -Download the integration example [here](https://github.com/prebid/Prebid.js/blob/master/integrationExamples/gpt/pbjs_example_gpt.html). +Download the integration example [here](https://github.com/prebid/Prebid.js/blob/master/integrationExamples/gpt/pbjs_example_gpt.html). ### Download the latest released code ### [See the releases page here](https://github.com/prebid/Prebid.js/releases) and download a copy. ### Example code ### -**Include the prebid.js libraray** +**Include the prebid.js library** Note that you need to host `prebid.js` locally or on a CDN and update the reference in the code snippet below for `cdn.host.com/prebid.min.js ```javascript (function() { @@ -77,7 +77,8 @@ pbjs.que.push(function(){ ``` **See Console Debug Errors during testing** -By default console errors are supressed. To enabled add `?pbjs_debug=true` to the end of the URL for testing. +By default console errors are suppressed. To enabled add `?pbjs_debug=true` to the end of the URL + for testing. API ---------- @@ -87,28 +88,105 @@ Full Developer API reference: Contribute ---------- +**Note:** You need to have at least `node.js 4.x` or greater installed to be able to run the gulp build commands. + +### Add a Bidder Adapter ### +Follow the [guide outlined here](http://prebid.org/dev-docs/bidder-adaptor.html) to add an adapter. + +### Install ### + $ npm install -### Add an Bidder Adapter ### -Follow the [guide outlined here](http://prebid.org/dev-docs/bidder-adaptor.html) to add an adapter. +If you experience errors, after a version update, try a fresh install: -### install ### - $ sudo npm install + $ rm -rf ./node_modules && npm cache clean && npm install ### Build ### $ gulp build +Runs code quality checks, generates prebid.js files and creates zip archive distributable: + + `./build/dev/prebid.js` Full source code for dev and debug + `./build/dev/prebid.js.map` Source map for dev and debug + `./build/dist/prebid.js` Minified production code + `./prebid.js_.zip` Distributable + +Code quality is defined by `./.jscs` and `./.jshint` files and errors are reported in the +terminal. The build will continue with quality errors, however. If you are contributing code +you can configure your editor with the provided .jscs and .jshint settings. + ### Configure ### -Edit `./integrationExamples/gpt/pbjs_example_gpt.html` +Edit example file `./integrationExamples/gpt/pbjs_example_gpt.html`: + +1. Change `{id}` values appropriately to set up ad units and bidders. + +1. Set path for prebid.js in your example file: + #####Development `pbs.src = './build/dev/prebid.js';` ##### + ```javascript + (function() { + var d = document, pbs = d.createElement('script'), pro = d.location.protocol; + pbs.type = 'text/javascript'; + pbs.src = ((pro === 'https:') ? 'https' : 'http') + ':./build/dev/prebid.js'; + var target = document.getElementsByTagName('head')[0]; + target.insertBefore(pbs, target.firstChild); + })(); + ``` + #####Test/Deploy (default) `pbs.src = './build/dist/prebid.js';`##### + ```javascript + (function() { + var d = document, pbs = d.createElement('script'), pro = d.location.protocol; + pbs.type = 'text/javascript'; + pbs.src = ((pro === 'https:') ? 'https' : 'http') + './build/dist/prebid.js'; + var target = document.getElementsByTagName('head')[0]; + target.insertBefore(pbs, target.firstChild); + })(); + ``` +1. (optional optimization) Edit `./package.json` to set the adapters you want to build with: + + ```json + "adapters": [ + "adform", + "aol", + "appnexus", + "indexExchange", + "openx", + "pubmatic", + "pulsepoint", + "rubicon", + "rubiconLegacy", + "sovrn", + "springserve", + "yieldbot" + ] + ``` -Change `{id}` values appropriately - ### Run ### $ gulp serve -Navigate to http://localhost:9999/integrationExamples/gpt/pbjs_example_gpt.html to run the example file +This runs code quality checks, generates prebid files and starts a webserver at +`http://localhost:9999` serving from project root. Navigate to your example implementation to test, +and if your prebid.js file is sourced from the `./build/dev` directory you will have sourcemaps +available in browser developer tools. + +Navigate to `http://localhost:9999/integrationExamples/gpt/pbjs_example_gpt.html` to run the +example file. + +Navigate to `http://localhost:9999/build/coverage/karma_html/report` to view a test coverage report. + +A watch is also in place that will run continuous tests in the terminal as you edit code and +tests. ### Unit Test In the Browser ### + $ gulp test --watch + +This will run tests and keep the Karma test browser open. If your prebid.js file is sourced from +the build/dev directory you will also have sourcemaps available when viewing browser developer +tools. Access the Karma debug page at: +`http://localhost:9876/debug.html` +View console for test results and developer tools to set breakpoints in source code. -Navigate to http://localhost:9999/test/spec/runner.html to run the test file. +Detailed code coverage reporting can be generated explicitly with `$ gulp test --coverage` and +results found in `./build/coverage`. +### Supported Browsers ### +Prebid.js is supported on IE9+ and modern browsers. diff --git a/bower.json b/bower.json index ec994aee553..693283e4464 100644 --- a/bower.json +++ b/bower.json @@ -1,12 +1,12 @@ { "name": "prebid-js", - "version": "0.4.1", + "version": "0.6.0", "authors": [ "Matt Kendall", "Paul Yang" ], "description": "Setup and manage header bidding advertising partners without writing code or confusing line items. Prebid.js is open source and free", - "main": "dist/prebid.min.js", + "main": "build/dist/prebid.js", "keywords": [ "Prebid" ], diff --git a/dist/prebid.js b/dist/prebid.js deleted file mode 100644 index c4fa62de42b..00000000000 --- a/dist/prebid.js +++ /dev/null @@ -1,4122 +0,0 @@ -/* Prebid.js v0.5.0 -Updated : 2016-01-11 */ -(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0) { - this.timeoutDelay = timeoutDelay; - } - - this.siteID = siteID; - this.impressions = []; - this._parseFnName = undefined; - if (top === self) { - this.sitePage = location.href; - this.topframe = 1; - } else { - this.sitePage = document.referrer; - this.topframe = 0; - } - if (typeof parseFn !== 'undefined') { - if (typeof parseFn === 'function') { - this._parseFnName = "cygnus_index_args.parseFn"; - } else { - throw "Invalid jsonp target function"; - } - } - if (typeof _IndexRequestData.requestCounter === 'undefined') { - _IndexRequestData.requestCounter = Math.floor(Math.random() * 256); - } else { - _IndexRequestData.requestCounter = (_IndexRequestData.requestCounter + 1) % 256; - } - this.requestID = String((new Date().getTime() % 2592000) * 256 + _IndexRequestData.requestCounter + 256); - this.initialized = true; - } - OpenRTBRequest.prototype.serialize = function() { - var json = '{"id":' + this.requestID + ',"site":{"page":"' + quote(this.sitePage) + '"'; - if (typeof document.referrer === 'string') { - json += ',"ref":"' + quote(document.referrer) + '"'; - } - json += '},"imp":['; - for (var i = 0; i < this.impressions.length; i++) { - var impObj = this.impressions[i]; - var ext = []; - json += '{"id":"' + impObj.id + '", "banner":{"w":' + impObj.w + ',"h":' + impObj.h + ',"topframe":' + String(this.topframe) + "}"; - if (typeof impObj.bidfloor === 'number') { - json += ',"bidfloor":' + impObj.bidfloor; - if (typeof impObj.bidfloorcur === 'string') { - json += ',"bidfloorcur":"' + quote(impObj.bidfloorcur) + '"'; - } - } - if (typeof impObj.slotID === 'string' && (!impObj.slotID.match(/^\s*$/))) { - ext.push('"sid":"' + quote(impObj.slotID) + '"'); - } - if (typeof impObj.siteID === 'number') { - ext.push('"siteID":' + impObj.siteID); - } - if (ext.length > 0) { - json += ',"ext": {' + ext.join() + '}'; - } - if (i + 1 == this.impressions.length) { - json += '}'; - } else { - json += '},'; - } - } - json += "]}"; - return json; - }; - OpenRTBRequest.prototype.setPageOverride = function(sitePageOverride) { - if (typeof sitePageOverride === 'string' && (!sitePageOverride.match(/^\s*$/))) { - this.sitePage = sitePageOverride; - return true; - } else { - return false; - } - }; - OpenRTBRequest.prototype.addImpression = function(width, height, bidFloor, bidFloorCurrency, slotID, siteID) { - var impObj = { - 'id': String(this.impressions.length + 1) - }; - if (typeof width !== 'number' || width <= 1) { - return null; - } - if (typeof height !== 'number' || height <= 1) { - return null; - } - if ((typeof slotID === 'string' || typeof slotID === 'number') && String(slotID).length <= 50) { - impObj.slotID = String(slotID); - } - impObj.w = width; - impObj.h = height; - if (bidFloor !== undefined && typeof bidFloor !== 'number') { - return null; - } - if (typeof bidFloor === 'number') { - if (bidFloor < 0) { - return null; - } - impObj.bidfloor = bidFloor; - if (bidFloorCurrency !== undefined && typeof bidFloorCurrency !== 'string') { - return null; - } - impObj.bidfloorcur = bidFloorCurrency; - } - if (typeof siteID !== 'undefined') { - if (typeof siteID === 'number' && siteID % 1 === 0 && siteID >= 0) { - impObj.siteID = siteID; - } else { - return null; - } - } - this.impressions.push(impObj); - return impObj.id; - }; - - OpenRTBRequest.prototype.buildRequest = function() { - if (this.impressions.length === 0 || this.initialized !== true) { - return; - } - var jsonURI = encodeURIComponent(this.serialize()); - var scriptSrc = window.location.protocol === 'https:' ? 'https://as-sec.casalemedia.com' : 'http://as.casalemedia.com'; - scriptSrc += '/headertag?v=9&x3=1&fn=cygnus_index_parse_res&s=' + this.siteID + '&r=' + jsonURI; - if (typeof this.timeoutDelay === "number" && this.timeoutDelay % 1 === 0 && this.timeoutDelay >= 0) { - scriptSrc += '&t=' + this.timeoutDelay; - } - return scriptSrc; - }; - try { - if (typeof cygnus_index_args === 'undefined' || typeof cygnus_index_args.siteID === 'undefined' || typeof cygnus_index_args.slots === 'undefined') { - return; - } - if (typeof _IndexRequestData === 'undefined') { - _IndexRequestData = {}; - _IndexRequestData.impIDToSlotID = {}; - _IndexRequestData.reqOptions = {}; - } - var req = new OpenRTBRequest(cygnus_index_args.siteID, cygnus_index_args.parseFn, cygnus_index_args.timeout); - if (cygnus_index_args.url && typeof cygnus_index_args.url === 'string') { - req.setPageOverride(cygnus_index_args.url); - } - _IndexRequestData.impIDToSlotID[req.requestID] = {}; - _IndexRequestData.reqOptions[req.requestID] = {}; - var slotDef, impID; - - for (var i = 0; i < cygnus_index_args.slots.length; i++) { - slotDef = cygnus_index_args.slots[i]; - - impID = req.addImpression(slotDef.width, slotDef.height, slotDef.bidfloor, slotDef.bidfloorcur, slotDef.id, slotDef.siteID); - if (impID) { - _IndexRequestData.impIDToSlotID[req.requestID][impID] = String(slotDef.id); - } - } - if (typeof cygnus_index_args.targetMode === 'number') { - _IndexRequestData.reqOptions[req.requestID].targetMode = cygnus_index_args.targetMode; - } - if (typeof cygnus_index_args.callback === 'function') { - _IndexRequestData.reqOptions[req.requestID].callback = cygnus_index_args.callback; - } - return req.buildRequest(); - } catch (e) {} -}; - -var IndexExchangeAdapter = function IndexExchangeAdapter() { - var slotIdMap = {}; - var requiredParams = [ - /* 0 */ - 'id', - /* 1 */ - 'siteID' - ]; - var firstAdUnitCode = ''; - - function _callBids(request) { - var bidArr = request.bids; - - if (!utils.hasValidBidRequest(bidArr[0].params, requiredParams, ADAPTER_NAME)) { - return; - } - - cygnus_index_args.slots = []; - var bidCount = 0; - - //Grab the slot level data for cygnus_index_args - for (i = 0; i < bidArr.length; i++) { - var bid = bidArr[i]; - - var width; - var height; - - outer: for (var j = 0; j < bid.sizes.length; j++) { - inner: for (var k = 0; k < cygnus_index_adunits.length; k++) { - if (bid.sizes[j][0] === cygnus_index_adunits[k][0] && - bid.sizes[j][1] === cygnus_index_adunits[k][1]) { - width = bid.sizes[j][0]; - height = bid.sizes[j][1]; - break outer; - } - } - } - - if (bid.params.timeout && typeof cygnus_index_args.timeout === 'undefined') { - cygnus_index_args.timeout = bid.params.timeout; - } - - if (bid.params.siteID && typeof cygnus_index_args.siteID === 'undefined') { - cygnus_index_args.siteID = bid.params.siteID; - } - - if (bid.params.sqps && typeof cygnus_index_args.SQPS === 'undefined') { - cygnus_index_args.slots.push({ - id:"SPQS", - width: bid.params.sqps.width, - height: bid.params.sqps.height, - siteID: bid.params.sqps.siteID || cygnus_index_args.siteID - }); - } - - if (utils.hasValidBidRequest(bid.params, requiredParams, ADAPTER_NAME)) { - firstAdUnitCode = bid.placementCode; - var slotId = bid.params[requiredParams[0]]; - slotIdMap[slotId] = bid; - - if (cygnus_index_primary_request) { - cygnus_index_args.slots.push({ - id: bid.params.id, - width: width, - height: height, - siteID: bid.params.siteID || cygnus_index_args.siteID - }); - - bidCount++; - - if (bid.params.tier2SiteID) { - cygnus_index_args.slots.push({ - id: "T1_"+bid.params.id, - width: width, - height: height, - siteID: bid.params.tier2SiteID - }); - } - if (bid.params.tier3SiteID) { - cygnus_index_args.slots.push({ - id:"T2_"+bid.params.id, - width:width, - height:height, - siteID:bid.params.tier3SiteID - }); - } - } - } - bidmanager.setExpectedBidsCount(ADAPTER_CODE, bidCount); - } - cygnus_index_primary_request = false; - - adloader.loadScript(cygnus_index_start()); - - window.cygnus_index_ready_state = function() { - try { - var indexObj = _IndexRequestData.targetIDToBid; - var lookupObj = cygnus_index_args; - - if (utils.isEmpty(indexObj)) { - var bid = bidfactory.createBid(2); - bid.bidderCode = ADAPTER_CODE; - logErrorBidResponse(); - return; - } - - utils._each(indexObj, function(adContents, cpmAndSlotId) { - utils._each(slotIdMap, function(bid, adSlotId) { - var obj = cpmAndSlotId.split('_'); - var currentId = obj[0]; - var currentCPM = obj[1]; - if (currentId === adSlotId) { - var bidObj = slotIdMap[adSlotId]; - var adUnitCode = bidObj.placementCode; - var slotObj = getSlotObj(cygnus_index_args, adSlotId); - - bid = bidfactory.createBid(1); - bid.cpm = currentCPM / 100; - bid.ad = adContents[0]; - bid.ad_id = adSlotId; - bid.bidderCode = ADAPTER_CODE; - bid.width = slotObj.width; - bid.height = slotObj.height; - bid.siteID = slotObj.siteID; - - bidmanager.addBidResponse(adUnitCode, bid); - } - }); - }); - } catch (e) { - utils.logError('Error calling index adapter', ADAPTER_NAME, e); - logErrorBidResponse(); - } - }; - } - - function getSlotObj(obj, id) { - var arr = obj.slots; - var returnObj = {}; - utils._each(arr, function(value) { - if (value.id === id) { - returnObj = value; - } - }); - return returnObj; - } - - function logErrorBidResponse() { - //no bid response - var bid = bidfactory.createBid(2); - bid.bidderCode = ADAPTER_CODE; - //log error to first add unit - bidmanager.addBidResponse(firstAdUnitCode, bid); - } - - return { - callBids: _callBids - }; - //end of Rubicon bid adaptor -}; - -module.exports = IndexExchangeAdapter; - -},{"../adloader.js":13,"../bidfactory.js":14,"../bidmanager.js":15,"../constants.json":16,"../utils.js":20}],7:[function(require,module,exports){ -var CONSTANTS = require('../constants.json'); -var utils = require('../utils.js'); -var bidfactory = require('../bidfactory.js'); -var bidmanager = require('../bidmanager.js'); -var adloader = require('../adloader'); - -/** - * Adapter for requesting bids from OpenX. - * - * @param {Object} options - Configuration options for OpenX - * @param {string} options.pageURL - Current page URL to send with bid request - * @param {string} options.refererURL - Referer URL to send with bid request - * - * @returns {{callBids: _callBids}} - * @constructor - */ -var OpenxAdapter = function OpenxAdapter(options) { - - var opts = options || {}; - var scriptUrl; - var bids; - - function _callBids(params) { - bids = params.bids || []; - for (var i = 0; i < bids.length; i++) { - var bid = bids[i]; - //load page options from bid request - if (bid.params.pageURL) { - opts.pageURL = bid.params.pageURL; - } - if (bid.params.refererURL) { - opts.refererURL = bid.params.refererURL; - } - if (bid.params.jstag_url) { - scriptUrl = bid.params.jstag_url; - } - if (bid.params.pgid) { - opts.pgid = bid.params.pgid; - } - } - _requestBids(); - } - - function _requestBids() { - - if (scriptUrl) { - adloader.loadScript(scriptUrl, function() { - var i; - var POX = OX(); - - POX.setPageURL(opts.pageURL); - POX.setRefererURL(opts.refererURL); - POX.addPage(opts.pgid); - - // Add each ad unit ID - for (i = 0; i < bids.length; i++) { - POX.addAdUnit(bids[i].params.unit); - } - - POX.addHook(function(response) { - var i; - var bid; - var adUnit; - var adResponse; - - // Map each bid to its response - for (i = 0; i < bids.length; i++) { - bid = bids[i]; - - // Get ad response - adUnit = response.getOrCreateAdUnit(bid.params.unit); - - // If 'pub_rev' (CPM) isn't returned we got an empty response - if (adUnit.get('pub_rev')) { - adResponse = adResponse = bidfactory.createBid(1); - - adResponse.bidderCode = 'openx'; - adResponse.ad_id = adUnit.get('ad_id'); - adResponse.cpm = Number(adUnit.get('pub_rev')) / 1000; - adResponse.ad = adUnit.get('html'); - adResponse.adUrl = adUnit.get('ad_url'); - adResponse.width = adUnit.get('width'); - adResponse.height = adUnit.get('height'); - - bidmanager.addBidResponse(bid.placementCode, adResponse); - } else { - // Indicate an ad was not returned - adResponse = bidfactory.createBid(2); - adResponse.bidderCode = 'openx'; - bidmanager.addBidResponse(bid.placementCode, adResponse); - } - } - }, OX.Hooks.ON_AD_RESPONSE); - - // Make request - POX.load(); - }); - } - } - - return { - callBids: _callBids - }; -}; - -module.exports = OpenxAdapter; -},{"../adloader":13,"../bidfactory.js":14,"../bidmanager.js":15,"../constants.json":16,"../utils.js":20}],8:[function(require,module,exports){ -var CONSTANTS = require('../constants.json'); -var utils = require('../utils.js'); -var bidfactory = require('../bidfactory.js'); -var bidmanager = require('../bidmanager.js'); -var adloader = require('../adloader'); - -/** - * Adapter for requesting bids from Pubmatic. - * - * @returns {{callBids: _callBids}} - * @constructor - */ -var PubmaticAdapter = function PubmaticAdapter() { - - var bids; - var _pm_pub_id; - var _pm_optimize_adslots = []; - - function _callBids(params) { - bids = params.bids; - for (var i = 0; i < bids.length; i++) { - var bid = bids[i]; - bidmanager.pbCallbackMap['' + bid.params.adSlot] = bid; - _pm_pub_id = _pm_pub_id || bid.params.publisherId; - _pm_optimize_adslots.push(bid.params.adSlot); - } - - // Load pubmatic script in an iframe, because they call document.write - _getBids(); - } - - function _getBids() { - - // required variables for pubmatic pre-bid call - window.pm_pub_id = _pm_pub_id; - window.pm_optimize_adslots = _pm_optimize_adslots; - - //create the iframe - var iframe = utils.createInvisibleIframe(); - var elToAppend = document.getElementsByTagName('head')[0]; - //insert the iframe into document - elToAppend.insertBefore(iframe, elToAppend.firstChild); - //todo make this more browser friendly - var iframeDoc = iframe.contentWindow.document; - iframeDoc.write(_createRequestContent()); - iframeDoc.close(); - } - - function _createRequestContent() { - var content = 'inDapIF=true; '; - content += ''; - content += ''; - content += '' + - 'window.pm_pub_id = "%%PM_PUB_ID%%";' + - 'window.pm_optimize_adslots = [%%PM_OPTIMIZE_ADSLOTS%%];'; - content += ''; - - var map = {}; - map['PM_PUB_ID'] = _pm_pub_id; - map['PM_OPTIMIZE_ADSLOTS'] = _pm_optimize_adslots.map(function(adSlot) { - return "'" + adSlot + "'"; - }).join(','); - - content += ''; - content += ''; - content += 'window.parent.pbjs.handlePubmaticCallback({progKeyValueMap: progKeyValueMap, bidDetailsMap: bidDetailsMap})'; - content += ''; - content += ''; - content = utils.replaceTokenInString(content, map, '%%'); - - return content; - } - - pbjs.handlePubmaticCallback = function(response) { - var i; - var adUnit; - var adUnitInfo; - var bid; - var bidResponseMap = (response && response.bidDetailsMap) || {}; - var bidInfoMap = (response && response.progKeyValueMap) || {}; - var dimensions; - - for (i = 0; i < bids.length; i++) { - var adResponse; - bid = bids[i].params; - - adUnit = bidResponseMap[bid.adSlot] || {}; - - // adUnitInfo example: bidstatus=0;bid=0.0000;bidid=39620189@320x50;wdeal= - adUnitInfo = (bidInfoMap[bid.adSlot] || '').split(';').reduce(function(result, pair) { - var parts = pair.split('='); - result[parts[0]] = parts[1]; - return result; - }, {}); - - if (adUnitInfo.bidstatus === '1') { - dimensions = adUnitInfo.bidid.split('@')[1].split('x'); - adResponse = bidfactory.createBid(1); - adResponse.bidderCode = 'pubmatic'; - adResponse.adSlot = bid.adSlot; - adResponse.cpm = Number(adUnitInfo.bid); - adResponse.ad = unescape(adUnit.creative_tag); - adResponse.adUrl = unescape(adUnit.tracking_url); - adResponse.width = dimensions[0]; - adResponse.height = dimensions[1]; - adResponse.dealId = adUnitInfo.wdeal; - - bidmanager.addBidResponse(bids[i].placementCode, adResponse); - } else { - // Indicate an ad was not returned - adResponse = bidfactory.createBid(2); - adResponse.bidderCode = 'pubmatic'; - bidmanager.addBidResponse(bids[i].placementCode, adResponse); - } - } - }; - - return { - callBids: _callBids - }; - -}; - -module.exports = PubmaticAdapter; -},{"../adloader":13,"../bidfactory.js":14,"../bidmanager.js":15,"../constants.json":16,"../utils.js":20}],9:[function(require,module,exports){ -var bidfactory = require('../bidfactory.js'); -var bidmanager = require('../bidmanager.js'); -var adloader = require('../adloader.js'); - -var PulsePointAdapter = function PulsePointAdapter() { - - var getJsStaticUrl = 'http://tag.contextweb.com/getjs.static.js'; - var bidUrl = 'http://tag.contextweb.com/bid'; - - function _callBids(params) { - if(typeof window.pp === 'undefined') { - adloader.loadScript(getJsStaticUrl, function() { bid(params); }); - } else { - bid(params); - } - } - - function bid(params) { - var bids = params.bids; - for (var i = 0; i < bids.length; i++) { - var bidRequest = bids[i]; - var callback = bidResponseCallback(bidRequest); - var ppBidRequest = new window.pp.Ad({ - cf : bidRequest.params.cf, - cp : bidRequest.params.cp, - ct : bidRequest.params.ct, - cn : 1, - ca : window.pp.requestActions.BID, - cu : bidUrl, - adUnitId: bidRequest.placementCode, - callback: callback - }); - ppBidRequest.display(); - } - } - - function bidResponseCallback(bid) { - return function(bidResponse) { - bidResponseAvailable(bid, bidResponse); - }; - } - - function bidResponseAvailable(bidRequest, bidResponse) { - if(bidResponse) { - var adSize = bidRequest.params.cf.toUpperCase().split('X'); - var bid = bidfactory.createBid(1); - bid.bidderCode = bidRequest.bidder; - bid.cpm = bidResponse.bidCpm; - bid.ad = bidResponse.html; - bid.width = adSize[0]; - bid.height = adSize[1]; - bidmanager.addBidResponse(bidRequest.placementCode, bid); - } else { - var passback = bidfactory.createBid(2); - passback.bidderCode = bidRequest.bidder; - bidmanager.addBidResponse(bidRequest.placementCode, passback); - } - } - - return { - callBids: _callBids - }; - -}; - -module.exports = PulsePointAdapter; - -},{"../adloader.js":13,"../bidfactory.js":14,"../bidmanager.js":15}],10:[function(require,module,exports){ -//Factory for creating the bidderAdaptor -var CONSTANTS = require('../constants.json'); -var utils = require('../utils.js'); -var bidfactory = require('../bidfactory.js'); -var bidmanager = require('../bidmanager.js'); - -var RubiconAdapter = function RubiconAdapter() { - // Map size dimensions to size 'ID' - var sizeMap = {}; - - function callBids(params) { - var bidArr = params.bids; - for (var i = 0; i < bidArr.length; i++) { - var bid = bidArr[i]; - //get the first size in the array - //TODO validation - var width = bid.sizes[0][0]; - var height = bid.sizes[0][1]; - var iframeContents = createRequestContent(bid, 'window.parent.pbjs.handleRubiconCallback', width, height); - var iframeId = loadIframeContent(iframeContents); - bid.iframeId = iframeId; - bidmanager.pbCallbackMap[getBidId(bid)] = bid; - } - - } - - // Build an ID that can be used to identify the response to the bid request. There - // may be an identifier we can send that gets sent back to us. - function getBidId(bid) { - return (bid.params ? [bid.params.rp_account, bid.params.rp_site, bid.params.rp_zonesize] : - [bid.account_id, bid.site_id, bid.zone_id, bid.size_id]).join('-'); - - } - - function loadIframeContent(content, callback) { - //create the iframe - var iframe = utils.createInvisibleIframe(); - var elToAppend = document.getElementsByTagName('head')[0]; - //insert the iframe into document - elToAppend.insertBefore(iframe, elToAppend.firstChild); - //todo make this more browser friendly - var iframeDoc = iframe.contentWindow.document; - iframeDoc.write(content); - iframeDoc.close(); - - return iframe.id; - - } - - function createRequestContent(bidOptions, callback, width, height) { - - // Map the size 'ID' to the dimensions - sizeMap[bidOptions.params.rp_zonesize.split('-')[1]] = { - width: width, - height: height - }; - - var content = 'inDapIF=true; '; - content += ''; - content += ''; - - - content += '' + - 'window.rp_account = "%%RP_ACCOUNT%%";' + - 'window.rp_site = "%%RP_SITE%%";' + - 'window.rp_zonesize = "%%RP_ZONESIZE%%";' + - 'window.rp_tracking = "%%RP_TRACKING%%";' + - 'window.rp_visitor = %%RP_VISITOR%%;' + - 'window.rp_width = %%RP_WIDTH%%;' + - 'window.rp_height = %%RP_HEIGHT%%;' + - 'window.rp_adtype = "jsonp";' + - 'window.rp_inventory = %%RP_INVENTORY%% ;' + - 'window.rp_floor=%%RP_FLOOR%%;' + - 'window.rp_fastlane = true;' + - 'window.rp_callback = ' + callback + ';'; - - - var map = {}; - map['RP_ACCOUNT'] = bidOptions.params.rp_account; - map['RP_SITE'] = bidOptions.params.rp_site; - map['RP_ZONESIZE'] = bidOptions.params.rp_zonesize; - map['RP_TRACKING'] = (bidOptions.params.rp_tracking) ? bidOptions.params.rp_tracking : ''; - map['RP_VISITOR'] = bidOptions.params.rp_visitor ? bidOptions.params.rp_visitor : '{}'; - map['RP_WIDTH'] = width; - map['RP_HEIGHT'] = height; - map['RP_INVENTORY'] = bidOptions.params.rp_inventory || '{}'; - map['RP_FLOOR'] = bidOptions.params.rp_floor ? bidOptions.params.rp_floor : '0.00'; - - content += ''; - content += ''; - content += ''; - - content = utils.replaceTokenInString(content, map, '%%'); - - //console.log(content); - - return content; - - } - - window.pbjs = window.pbjs || {que: []}; - window.pbjs.handleRubiconCallback = function(response) { - var placementCode = ''; - - var bid = {}; - if (response && response.status === 'ok') { - try { - var iframeId = ''; - var bidObj = bidmanager.getPlacementIdByCBIdentifer(getBidId(response)); - if (bidObj) { - placementCode = bidObj.placementCode; - bidObj.status = CONSTANTS.STATUS.GOOD; - iframeId = bidObj.iframeId; - } - - if (response.ads && response.ads[0] && response.ads[0].status === 'ok') { - bid = bidfactory.createBid(1); - - var rubiconAd = response.ads[0]; - var size = sizeMap[rubiconAd.size_id]; - var width = 0; - var height = 0; - - var iframeObj = window.frames[iframeId]; - var rubiconObj; - if(iframeObj.contentWindow){ - rubiconObj = iframeObj.contentWindow.RubiconAdServing - }else{ - rubiconObj = iframeObj.window.RubiconAdServing; - } - - if (rubiconObj && rubiconObj.AdSizes) { - /* should return - 1: { - dim: "468x60" - }, - */ - size = rubiconObj.AdSizes[rubiconAd.size_id]; - var sizeArray = size.dim.split('x'); - width = sizeArray[0]; - height = sizeArray[1]; - } - - bid.cpm = rubiconAd.cpm; - bid.ad = ''; - bid.ad_id = rubiconAd.ad_id; - bid.bidderCode = 'rubicon'; - bid.sizeId = rubiconAd.size_id; - bid.width = width; - bid.height = height; - - }else{ - bid = bidfactory.createBid(2); - bid.bidderCode = 'rubicon'; - var bidObj = bidmanager.getPlacementIdByCBIdentifer(getBidId(response)); - if (bidObj) { - placementCode = bidObj.placementCode; - } - } - - } catch (e) { - utils.logError('Error parsing rubicon response bid: ' + e.message); - } - - } else { - //set bid response code to 2 = no response or error - bid = bidfactory.createBid(2); - bid.bidderCode = 'rubicon'; - var bidObj = bidmanager.getPlacementIdByCBIdentifer(getBidId(response)); - if (bidObj) { - placementCode = bidObj.placementCode; - } - - } - - //add the bid response here - bidmanager.addBidResponse(placementCode, bid); - - }; - - return { - callBids: callBids - - }; - //end of Rubicon bid adaptor -}; - -module.exports = RubiconAdapter; - -},{"../bidfactory.js":14,"../bidmanager.js":15,"../constants.json":16,"../utils.js":20}],11:[function(require,module,exports){ -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 defaultPlacementForBadBid = ''; - -/** - * Adapter for requesting bids from Sovrn - */ -var SovrnAdapter = function SovrnAdapter() { - var sovrnUrl = 'ap.lijit.com/rtb/bid'; - - function _callBids(params) { - var sovrnBids = params.bids || []; - // De-dupe by tagid then issue single bid request for all bids - _requestBids(_getUniqueTagids(sovrnBids)); - } - - // filter bids to de-dupe them? - function _getUniqueTagids(bids) { - var key; - var map = {}; - var Tagids = []; - bids.forEach(function(bid) { - map[utils.getBidIdParamater('tagid', bid.params)] = bid; - }); - for (key in map) { - if (map.hasOwnProperty(key)) { - Tagids.push(map[key]); - } - } - return Tagids; - } - - function _requestBids(bidReqs) { - // build bid request object - var domain = window.location.host; - var page = window.location.pathname + location.search + location.hash; - - var sovrnImps = []; - //assign the first adUnit (placement) for bad bids; - defaultPlacementForBadBid = bidReqs[0].placementCode; - - //build impression array for sovrn - utils._each(bidReqs, function(bid) - { - var tagId = utils.getBidIdParamater('tagid', bid.params); - var bidFloor = utils.getBidIdParamater('bidfloor', bid.params); - var adW=0,adH=0; - - //sovrn supports only one size per tagid, so we just take the first size if there are more - //if we are a 2 item array of 2 numbers, we must be a SingleSize array - var sizeArrayLength = bid.sizes.length; - if (sizeArrayLength === 2 && typeof bid.sizes[0] === 'number' && typeof bid.sizes[1] === 'number') { - adW=bid.sizes[0]; - adH=bid.sizes[1]; - } - else - { - adW=bid.sizes[0][0]; - adH=bid.sizes[0][1]; - } - imp = - { - id: utils.getUniqueIdentifierStr(), - banner: { - w: adW, - h: adH - }, - tagid: tagId, - bidfloor: bidFloor - }; - sovrnImps.push(imp); - bidmanager.pbCallbackMap[imp.id] = bid; - }); - - // build bid request with impressions - var sovrnBidReq = { - id: utils.getUniqueIdentifierStr(), - imp: sovrnImps, - site:{ - domain: domain, - page: page - } - }; - - var scriptUrl = '//'+sovrnUrl+'?callback=window.pbjs.sovrnResponse' + - '&br=' + encodeURIComponent(JSON.stringify(sovrnBidReq)); - adloader.loadScript(scriptUrl, null); - } - - //expose the callback to the global object: - pbjs.sovrnResponse = function(sovrnResponseObj) { - var bid = {}; - // valid object? - if (sovrnResponseObj && sovrnResponseObj.id) { - // valid object w/ bid responses? - if (sovrnResponseObj.seatbid && sovrnResponseObj.seatbid.length !==0 && sovrnResponseObj.seatbid[0].bid && sovrnResponseObj.seatbid[0].bid.length !== 0) { - - sovrnResponseObj.seatbid[0].bid.forEach(function(sovrnBid){ - - var responseCPM; - var placementCode = ''; - var id = sovrnBid.impid; - - // try to fetch the bid request we sent Sovrn - var bidObj = bidmanager.getPlacementIdByCBIdentifer(id); - if (bidObj){ - placementCode = bidObj.placementCode; - bidObj.status = CONSTANTS.STATUS.GOOD; - - //place ad response on bidmanager._adResponsesByBidderId - responseCPM = parseFloat(sovrnBid.price); - - if(responseCPM !== 0) { - sovrnBid.placementCode = placementCode; - sovrnBid.size = bidObj.sizes; - var responseAd = sovrnBid.adm; - - // build impression url from response - var responseNurl = ''; - - //store bid response - //bid status is good (indicating 1) - bid = bidfactory.createBid(1); - bid.creative_id = sovrnBid.Id; - bid.bidderCode = 'sovrn'; - bid.cpm = responseCPM; - - //set ad content + impression url - // sovrn returns ' + - ''; - }, - /** - * Bid response builder. - * - * @param {Object} slotCriteria - Yieldbot bid criteria - * @private - */ - buildBid: function(slotCriteria) { - var bid = {}; - - if (slotCriteria && slotCriteria.ybot_ad && slotCriteria.ybot_ad !== 'n') { - - bid = bidfactory.createBid(ybotlib.BID_STATUS.AVAILABLE); - - bid.cpm = parseInt(slotCriteria.ybot_cpm) / 100.0 || 0; // Yieldbot CPM bids are in cents - - var szArr = slotCriteria.ybot_size ? slotCriteria.ybot_size.split('x') : [0,0], - slot = slotCriteria.ybot_slot || '', - sizeStr = slotCriteria.ybot_size || ''; // Creative template needs the dimensions string - - bid.width = szArr[0] || 0; - bid.height = szArr[1] || 0; - - bid.ad = ybotlib.buildCreative(slot, sizeStr); - - // Add Yieldbot parameters to allow publisher bidderSettings.yieldbot specific targeting - for (var k in slotCriteria) { - bid[k] = slotCriteria[k]; - } - - } else { - bid = bidfactory.createBid(ybotlib.BID_STATUS.EMPTY); - } - - bid.bidderCode = 'yieldbot'; - return bid; - }, - /** - * Yieldbot implementation of {@link module:adaptermanger.callBids} - * @param {Object} params - Adapter bid configuration object - * @private - */ - callBids: function(params) { - - var bids = params.bids || [], - ybotq = window.ybotq || []; - - ybotlib.pageLevelOption = false; - - ybotq.push(function () { - var yieldbot = window.yieldbot; - - utils._each(bids, function(v) { - var bid = v, - psn = bid.params && bid.params.psn || 'ERROR_DEFINE_YB_PSN', - slot = bid.params && bid.params.slot || 'ERROR_DEFINE_YB_SLOT'; - - yieldbot.pub(psn); - yieldbot.defineSlot(slot, {sizes: bid.sizes || []}); - - var cbId = utils.getUniqueIdentifierStr(); - bidmanager.pbCallbackMap[cbId] = bid; - ybotlib.definedSlots.push(cbId); - }); - - yieldbot.enableAsync(); - yieldbot.go(); - }); - - ybotq.push(function () { - ybotlib.handleUpdateState(); - }); - - adloader.loadScript('//cdn.yldbt.com/js/yieldbot.intent.js'); - }, - /** - * Yieldbot bid request callback handler. - * - * @see {@link YieldbotAdapter~_callBids} - * @private - */ - handleUpdateState: function() { - var yieldbot = window.yieldbot; - - utils._each(ybotlib.definedSlots, function(v) { - var slot, - criteria, - placementCode, - adapterConfig; - - adapterConfig = bidmanager.getPlacementIdByCBIdentifer(v) || {}; - slot = adapterConfig.params.slot || ''; - criteria = yieldbot.getSlotCriteria(slot); - - placementCode = adapterConfig.placementCode || 'ERROR_YB_NO_PLACEMENT'; - var bid = ybotlib.buildBid(criteria); - - bidmanager.addBidResponse(placementCode, bid); - - }); - } - } - return { - callBids: ybotlib.callBids - }; -}; - -module.exports = YieldbotAdapter; - -},{"../adloader":13,"../bidfactory":14,"../bidmanager":15,"../utils":20}],13:[function(require,module,exports){ -var utils = require('./utils'); -//add a script tag to the page, used to add /jpt call to page -exports.loadScript = function(tagSrc, callback) { - if(!tagSrc){ - utils.logError('Error attempting to request empty URL', 'adloader.js:loadScript'); - return; - } - var jptScript = document.createElement('script'); - jptScript.type = 'text/javascript'; - jptScript.async = true; - - - // Execute a callback if necessary - if (callback && typeof callback === "function") { - if (jptScript.readyState) { - jptScript.onreadystatechange = function() { - if (jptScript.readyState == "loaded" || jptScript.readyState == "complete") { - jptScript.onreadystatechange = null; - callback(); - } - }; - } else { - jptScript.onload = function() { - callback(); - }; - } - } - - //call function to build the JPT call - jptScript.src = tagSrc; - - //add the new script tag to the page - var elToAppend = document.getElementsByTagName('head'); - elToAppend = elToAppend.length ? elToAppend : document.getElementsByTagName('body'); - if (elToAppend.length) { - elToAppend = elToAppend[0]; - elToAppend.insertBefore(jptScript, elToAppend.firstChild); - } -}; - -exports.trackPixel = function(pixelUrl) { - //track a impbus tracking pixel - - //TODO: Decide of tracking via AJAX is sufficent, or do we need to - //run impression trackers via page pixels? - try { - - //add a cachebuster so we don't end up dropping any impressions - pixelUrl += '&rnd=' + Math.random(); - - if (pixelUrl) { - var img = document.createElement('img'); - img.src = pixelUrl; - } - - - } catch (e) { - - } -}; -},{"./utils":20}],14:[function(require,module,exports){ -var utils = require('./utils.js'); - -/** - Required paramaters - bidderCode, - height, - width, - statusCode - Optional paramaters - adId, - cpm, - ad, - adUrl, - dealId, - priceKeyString; - */ -function Bid(statusCode) { - var _bidId = utils.getUniqueIdentifierStr(), - _statusCode = statusCode || 0; - this.bidderCode = ''; - this.width = 0; - this.height = 0; - this.statusMessage = _getStatus(); - this.adId = _bidId; - - function _getStatus() { - switch (_statusCode) { - case 0: - return 'Pending'; - case 1: - return 'Bid available'; - case 2: - return 'Bid returned empty or error response'; - case 3: - return 'Bid timed out'; - } - } - this.getStatusCode = function() { - return _statusCode; - }; - - function _setStatusCode(status) { - this._statusCode = status; - //update status msg - this._statusMessage = this._getStatus(); - } - //returns the size of the bid creative. Concatenation of width and height by ‘x’. - this.getSize = function() { - return this.width + 'x' + this.height; - }; - -} - -// Bid factory function. -exports.createBid = function(statusCde) { - return new Bid(statusCde); -}; - -//module.exports = Bid; -},{"./utils.js":20}],15:[function(require,module,exports){ -var CONSTANTS = require('./constants.json'); -var utils = require('./utils.js'); -var adaptermanager = require('./adaptermanager'); -var events = require('./events'); - -var objectType_function = 'function'; -var objectType_undefined = 'undefined'; - -var externalCallbackByAdUnitArr = []; -var externalCallbackArr = []; -var externalOneTimeCallback = null; -var biddersByPlacementMap = {}; - -var pbCallbackMap = {}; -exports.pbCallbackMap = pbCallbackMap; - -var pbBidResponseByPlacement = {}; -exports.pbBidResponseByPlacement = pbBidResponseByPlacement; - -//this is used to look up the bid by bid ID later -var _adResponsesByBidderId = {}; -exports._adResponsesByBidderId = _adResponsesByBidderId; - -var bidResponseReceivedCount = {}; -exports.bidResponseReceivedCount = bidResponseReceivedCount; - -var expectedBidsCount = {}; - -var _allBidsAvailable = false; - -var _callbackExecuted = false; - -var defaultBidderSettingsMap = {}; -var bidderStartTimes = {}; - -exports.getPlacementIdByCBIdentifer = function(id) { - return pbCallbackMap[id]; -}; - - -exports.getBidResponseByAdUnit = function(adUnitCode) { - return pbBidResponseByPlacement; - -}; - - -exports.clearAllBidResponses = function(adUnitCode) { - _allBidsAvailable = false; - _callbackExecuted = false; - - //init bid response received count - initbidResponseReceivedCount(); - //init expected bids count - initExpectedBidsCount(); - //clear the callback handler flag - externalCallbackArr.called = false; - - for (var prop in this.pbBidResponseByPlacement) { - delete this.pbBidResponseByPlacement[prop]; - } -}; - -/** - * Returns a list of bidders that we haven't received a response yet - * @return {array} [description] - */ -exports.getTimedOutBidders = function(){ - var bidderArr = []; - utils._each(bidResponseReceivedCount,function(count,bidderCode){ - if(count === 0){ - bidderArr.push(bidderCode); - } - }); - - return bidderArr; -}; - -function initbidResponseReceivedCount(){ - - bidResponseReceivedCount = {}; - - for(var i=0; i 0){ - //else put into auction array if cpm > 0 - bidArrayTargeting.push({ - cpm: bid.cpm, - bid: bid - }); - } - //put all bids into bidArray by default - bidResponseArray.push(bidClone); - } - } - - // push the winning bid into targeting map - if (adUnitCode && bidArrayTargeting.length !== 0) { - var winningBid = getWinningBid(bidArrayTargeting); - var keyValues = winningBid.adserverTargeting; - pb_targetingMap[adUnitCode] = utils.extend(pb_targetingMap[adUnitCode], keyValues); - } - - return bidResponseArray; -} - -function getCloneBid(bid) { - var bidClone = {}; - //clone by json parse. This also gets rid of unwanted function properties - if (bid) { - var jsonBid = JSON.stringify(bid); - bidClone = JSON.parse(jsonBid); - - //clean up bid clone - delete bidClone.pbLg; - delete bidClone.pbMg; - delete bidClone.pbHg; - } - return bidClone; -} - -function resetBids() { - bidmanager.clearAllBidResponses(); - pb_bidderMap = {}; - pb_placements = []; - pb_targetingMap = {}; - pb_bidsTimedOut = false; -} - -function requestAllBids(tmout){ - var timeout = tmout; - resetBids(); - init(timeout); -} - - -////////////////////////////////// -// // -// Start Public APIs // -// // -////////////////////////////////// -/** - * This function returns the query string targeting parameters available at this moment for a given ad unit. Note that some bidder's response may not have been received if you call this function too quickly after the requests are sent. - * @param {string} [adunitCode] adUnitCode to get the bid responses for - * @alias module:pbjs.getAdserverTargetingForAdUnitCodeStr - * @return {array} returnObj return bids array - */ -pbjs.getAdserverTargetingForAdUnitCodeStr = function(adunitCode) { - // call to retrieve bids array - if(adunitCode){ - var res = pbjs.getAdserverTargetingForAdUnitCode(adunitCode); - return utils.transformAdServerTargetingObj(res); - } - else{ - utils.logMessage('Need to call getAdserverTargetingForAdUnitCodeStr with adunitCode'); - } -}; -/** - * This function returns the query string targeting parameters available at this moment for a given ad unit. Note that some bidder's response may not have been received if you call this function too quickly after the requests are sent. - * @param {string} [adunitCode] adUnitCode to get the bid responses for - * @alias module:pbjs.getAdserverTargetingForAdUnitCode - * @return {object} returnObj return bids - */ -pbjs.getAdserverTargetingForAdUnitCode = function(adunitCode) { - // call to populate pb_targetingMap - pbjs.getBidResponses(adunitCode); - - if (adunitCode) { - return pb_targetingMap[adunitCode]; - } - return pb_targetingMap; -}; -/** - * returns all ad server targeting for all ad units - * @return {object} Map of adUnitCodes and targeting values [] - * @alias module:pbjs.getAdserverTargeting - */ -pbjs.getAdserverTargeting = function() { - return pbjs.getAdserverTargetingForAdUnitCode(); -}; - -/** - * This function returns the bid responses at the given moment. - * @param {string} [adunitCode] adunitCode adUnitCode to get the bid responses for - * @alias module:pbjs.getBidResponses - * @return {object} map | object that contains the bidResponses - */ -pbjs.getBidResponses = function(adunitCode) { - var bidArrayTargeting = []; - var response = {}; - var bidArray = []; - var returnObj = {}; - - if (adunitCode) { - response = getBidResponsesByAdUnit(adunitCode); - bidArray = []; - if (response && response.bids) { - bidArray = buildBidResponse(response.bids); - } - - returnObj = { - bids: bidArray - }; - - } else { - response = getBidResponsesByAdUnit(); - for (var adUnit in response) { - if (response.hasOwnProperty(adUnit)) { - if (response && response[adUnit] && response[adUnit].bids) { - bidArray = buildBidResponse(response[adUnit].bids); - } - - returnObj[adUnit] = { - bids: bidArray - }; - - } - } - } - - return returnObj; - -}; -/** - * Returns bidResponses for the specified adUnitCode - * @param {String} adUnitCode adUnitCode - * @alias module:pbjs.getBidResponsesForAdUnitCode - * @return {Object} bidResponse object - */ -pbjs.getBidResponsesForAdUnitCode = function(adUnitCode) { - return pbjs.getBidResponses(adUnitCode); -}; -/** - * Set query string targeting on adUnits specified. The logic for deciding query strings is described in the section Configure AdServer Targeting. Note that this function has to be called after all ad units on page are defined. - * @param {array} [codeArr] an array of adUnitodes to set targeting for. - * @alias module:pbjs.setTargetingForAdUnitsGPTAsync - */ -pbjs.setTargetingForAdUnitsGPTAsync = function(codeArr) { - if (!window.googletag || !utils.isFn(window.googletag.pubads) || !utils.isFn(window.googletag.pubads().getSlots)) { - utils.logError('window.googletag is not defined on the page'); - return; - } - - //emit bid timeout event here - timeOutBidders(); - - var adUnitCodesArr = codeArr; - - if (typeof codeArr === objectType_string) { - adUnitCodesArr = [codeArr]; - } else if (typeof codeArr === objectType_object) { - adUnitCodesArr = codeArr; - } - - var placementBids = {}, - i = 0; - if (adUnitCodesArr) { - for (i = 0; i < adUnitCodesArr.length; i++) { - var code = adUnitCodesArr[i]; - //get all the slots from google tag - var slots = window.googletag.pubads().getSlots(); - for (var k = 0; k < slots.length; k++) { - - if (slots[k].getSlotElementId() === code || slots[k].getAdUnitPath() === code) { - placementBids = getBidResponsesByAdUnit(code); - setGPTAsyncTargeting(code, slots[k]); - } - } - } - } else { - //get all the slots from google tag - var slots = window.googletag.pubads().getSlots(); - for (i = 0; i < slots.length; i++) { - var adUnitCode = slots[i].getSlotElementId(); - if (adUnitCode) { - //placementBids = getBidsFromGTPIdentifier(slots[i]); - setGPTAsyncTargeting(adUnitCode, slots[i]); - } - } - } - -}; -/** - * Returns a string identifier (either DivId or adUnitPath) - * @param {[type]} slot [description] - * @return {[type]} [description] - */ -function getTargetingfromGPTIdentifier(slot){ - var targeting = null; - if(slot){ - //first get by elementId - targeting = pbjs.getAdserverTargetingForAdUnitCode(slot.getSlotElementId()); - //if not available, try by adUnitPath - if(!targeting){ - targeting = pbjs.getAdserverTargetingForAdUnitCode(slot.getAdUnitPath()); - } - } - return targeting; -} - -/** - - -/** - * Set query string targeting on all GPT ad units. - * @alias module:pbjs.setTargetingForGPTAsync - */ -pbjs.setTargetingForGPTAsync = function(codeArr) { - pbjs.setTargetingForAdUnitsGPTAsync(codeArr); -}; - -/** - * Returns a bool if all the bids have returned or timed out - * @alias module:pbjs.allBidsAvailable - * @return {bool} all bids available - */ -pbjs.allBidsAvailable = function() { - return bidmanager.allBidsBack(); -}; - -/** - * This function will render the ad (based on params) in the given iframe document passed through. Note that doc SHOULD NOT be the parent document page as we can't doc.write() asynchrounsly - * @param {object} doc document - * @param {string} id bid id to locate the ad - * @alias module:pbjs.renderAd - */ -pbjs.renderAd = function(doc, id) { - utils.logMessage('Calling renderAd with adId :' + id); - if (doc && id) { - try { - //lookup ad by ad Id - var adObject = bidmanager._adResponsesByBidderId[id]; - if (adObject) { - //emit 'bid won' event here - events.emit(BID_WON, adObject); - var height = adObject.height; - var width = adObject.width; - var url = adObject.adUrl; - var ad = adObject.ad; - - if (ad) { - doc.write(ad); - doc.close(); - if (doc.defaultView && doc.defaultView.frameElement) { - doc.defaultView.frameElement.width = width; - doc.defaultView.frameElement.height = height; - } - } - //doc.body.style.width = width; - //doc.body.style.height = height; - else if (url) { - doc.write(''); - doc.close(); - - if (doc.defaultView && doc.defaultView.frameElement) { - doc.defaultView.frameElement.width = width; - doc.defaultView.frameElement.height = height; - } - - } else { - utils.logError('Error trying to write ad. No ad for bid response id: ' + id); - } - - } else { - utils.logError('Error trying to write ad. Cannot find ad by given id : ' + id); - } - - } catch (e) { - utils.logError('Error trying to write ad Id :' + id + ' to the page:' + e.message); - } - } else { - utils.logError('Error trying to write ad Id :' + id + ' to the page. Missing document or adId'); - } - -}; - - -/* - * @deprecated - will be removed next release. Use pbjs.requestBids - */ -pbjs.requestBidsForAdUnit = function(adUnitCode) { - resetBids(); - init(adUnitCode); - -}; - -/** - * @deprecated - will be removed next release. Use pbjs.requestBids - */ -pbjs.requestBidsForAdUnits = function(adUnitsObj) { - if (!adUnitsObj || adUnitsObj.constructor !== Array) { - utils.logError('requestBidsForAdUnits must pass an array of adUnits'); - return; - } - resetBids(); - var adUnitBackup = pbjs.adUnits.slice(0); - pbjs.adUnits = adUnitsObj; - init(); - pbjs.adUnits = adUnitBackup; - -}; - -/** - * Remove adUnit from the pbjs configuration - * @param {String} adUnitCode the adUnitCode to remove - * @alias module:pbjs.removeAdUnit - */ -pbjs.removeAdUnit = function(adUnitCode) { - if (adUnitCode) { - for (var i = 0; i < pbjs.adUnits.length; i++) { - if (pbjs.adUnits[i].code === adUnitCode) { - pbjs.adUnits.splice(i, 1); - } - } - } -}; - - -/** - * Request bids ad-hoc. This function does not add or remove adUnits already configured. - * @param {Object} requestObj - * @param {string[]} requestObj.adUnitCodes adUnit codes to request. Use this or requestObj.adUnits - * @param {object[]} requestObj.adUnits AdUnitObjects to request. Use this or requestObj.adUnitCodes - * @param {number} [requestObj.timeout] Timeout for requesting the bids specified in milliseconds - * @param {function} [requestObj.bidsBackHandler] Callback to execute when all the bid responses are back or the timeout hits. - * @alias module:pbjs.requestBids - */ -pbjs.requestBids = function(requestObj) { - if (!requestObj) { - //utils.logMessage('requesting all bids'); - - requestAllBids(); - - } - else{ - var adUnitCodes = requestObj.adUnitCodes; - var adUnits = requestObj.adUnits; - var timeout = requestObj.timeout; - var bidsBackHandler = requestObj.bidsBackHandler; - var adUnitBackup = pbjs.adUnits.slice(0); - - if (typeof bidsBackHandler === objectType_function) { - bidmanager.addOneTimeCallback(bidsBackHandler); - } - - if (adUnitCodes && utils.isArray(adUnitCodes)) { - resetBids(); - init(timeout, adUnitCodes); - - } else if (adUnits && utils.isArray(adUnits)) { - resetBids(); - pbjs.adUnits = adUnits; - init(timeout); - } else { - //request all ads - requestAllBids(timeout); - } - - pbjs.adUnits = adUnitBackup; - } - -}; - -/** - * - * Add adunit(s) - * @param {(string|string[])} Array of adUnits or single adUnit Object. - * @alias module:pbjs.addAdUnits - */ -pbjs.addAdUnits = function(adUnitArr) { - if (utils.isArray(adUnitArr)) { - //append array to existing - pbjs.adUnits.push.apply(pbjs.adUnits, adUnitArr); - } else if (typeof adUnitArr === objectType_object) { - pbjs.adUnits.push(adUnitArr); - } -}; - - -/** - * Add a callback event - * @param {String} event event to attach callback to Options: "allRequestedBidsBack" | "adUnitBidsBack" - * @param {Function} func function to execute. Paramaters passed into the function: (bidResObj), [adUnitCode]); - * @alias module:pbjs.addCallback - * @returns {String} id for callback - */ -pbjs.addCallback = function(eventStr, func) { - var id = null; - if (!eventStr || !func || typeof func !== objectType_function) { - utils.logError('error registering callback. Check method signature'); - return id; - } - - id = utils.getUniqueIdentifierStr; - bidmanager.addCallback(id, func, eventStr); - return id; -}; - -/** - * Remove a callback event - * @param {string} cbId id of the callback to remove - * @alias module:pbjs.removeCallback - * @returns {String} id for callback - */ -pbjs.removeCallback = function(cbId) { - //todo -}; - -/** - * Wrapper to register bidderAdapter externally (adaptermanager.registerBidAdapter()) - * @param {[type]} bidderAdaptor [description] - * @param {[type]} bidderCode [description] - * @return {[type]} [description] - */ -pbjs.registerBidAdapter = function(bidderAdaptor, bidderCode){ - try{ - adaptermanager.registerBidAdapter(bidderAdaptor(), bidderCode); - } - catch(e){ - utils.logError('Error registering bidder adapter : ' + e.message); - } -}; - -/** - * - */ - pbjs.bidsAvailableForAdapter = function(bidderCode){ - - //TODO getAd - var bids = pb_bidderMap[bidderCode].bids; - - for (var i = 0; i < bids.length; i++) { - var adunitCode = bids[i].placementCode; - var responseObj = bidmanager.pbBidResponseByPlacement[adunitCode]; - - var bid = bidfactory.createBid(1); - // bid.creative_id = adId; - bid.bidderCode = bidderCode; - bid.adUnitCode = adunitCode; - bid.bidder = bidderCode; - // bid.cpm = responseCPM; - // bid.adUrl = jptResponseObj.result.ad; - // bid.width = jptResponseObj.result.width; - // bid.height = jptResponseObj.result.height; - // bid.dealId = jptResponseObj.result.deal_id; - - responseObj.bids.push(bid); - responseObj.bidsReceivedCount++; - bidmanager.pbBidResponseByPlacement[adunitCode] = responseObj; - }; - - bidmanager.increaseBidResponseReceivedCount(bidderCode); -} - -/** - * Wrapper to bidfactory.createBid() - * @param {[type]} statusCode [description] - * @return {[type]} [description] - */ -pbjs.createBid = function(statusCode){ - return bidfactory.createBid(statusCode); -}; - -/** - * Wrapper to bidmanager.addBidResponse - * @param {[type]} adUnitCode [description] - * @param {[type]} bid [description] - */ -pbjs.addBidResponse = function(adUnitCode, bid){ - bidmanager.addBidResponse(adUnitCode, bid); -}; - -/** - * Wrapper to adloader.loadScript - * @param {[type]} tagSrc [description] - * @param {Function} callback [description] - * @return {[type]} [description] - */ -pbjs.loadScript = function(tagSrc, callback){ - adloader.loadScript(tagSrc, callback); -}; - -/** - * This isn't ready yet - * return data for analytics - * @param {Function} [description] - * @return {[type]} [description] - -pbjs.getAnalyticsData = function(){ - var returnObj = {}; - var bidResponses = pbjs.getBidResponses(); - - //create return obj for all adUnits - for(var i=0;i https://developers.google.com/analytics/devguides/collection/ios/v3/limits-quotas?hl=en - _eventCount = 0, - //limit data sent by leaving this false - _enableDistribution = false, - _timedOutBidders = []; - - -/** - * This will enable sending data to google analytics. Only call once, or duplicate data will be sent! - * @param {object} gaOptions to set distribution and GA global (if renamed); - * @return {[type]} [description] - */ -exports.enableAnalytics = function(gaOptions) { - if(typeof gaOptions.global !== 'undefined'){ - _gaGlobal = gaOptions.global; - } - else{ - //default global is window.ga - _gaGlobal = 'ga'; - } - if(typeof gaOptions.enableDistribution !== 'undefined'){ - _enableDistribution = gaOptions.enableDistribution; - } - - var bid = null; - - //first send all events fired before enableAnalytics called - - var existingEvents = events.getEvents(); - utils._each(existingEvents, function(eventObj) { - var args = eventObj.args; - if (!eventObj) { - return; - } - if (eventObj.eventType === BID_REQUESTED) { - //bid is 1st args - bid = args[0]; - sendBidRequestToGa(bid); - } else if (eventObj.eventType === BID_RESPONSE) { - //bid is 2nd args - bid = args[1]; - sendBidResponseToGa(bid); - - } else if (eventObj.eventType === BID_TIMEOUT) { - var bidderArray = args[0]; - _timedOutBidders = bidderArray; - //todo disable event listeners - - } else if (eventObj.eventType === BID_WON) { - bid = args[0]; - sendBidWonToGa(bid); - } - }); - - //Next register event listeners to send data immediately - - //bidRequests - events.on(BID_REQUESTED, function(bidRequestObj) { - sendBidRequestToGa(bidRequestObj); - }); - - //bidResponses - events.on(BID_RESPONSE, function(adunit, bid) { - sendBidResponseToGa(bid); - sendBidTimeouts(bid); - }); - - //bidTimeouts - events.on(BID_TIMEOUT, function(bidderArray) { - _timedOutBidders = bidderArray; - }); - - //wins - events.on(BID_WON, function(bid) { - sendBidWonToGa(bid); - }); -}; - -/** - * Check if gaGlobal or window.ga is defined on page. If defined execute all the GA commands - */ -function checkAnalytics() { - if (_enableCheck && typeof window[_gaGlobal] === 'function' ) { - - for (var i = 0; i < _analyticsQueue.length; i++) { - _analyticsQueue[i].call(); - } - //override push to execute the command immediately from now on - _analyticsQueue.push = function(fn) { - fn.call(); - }; - //turn check into NOOP - _enableCheck = false; - } - utils.logMessage('event count sent to GA: ' + _eventCount); -} - - -function convertToCents(dollars) { - if (dollars) { - return Math.floor(dollars * 100); - } - return 0; -} - -function getLoadTimeDistribution(time) { - var distribution; - if (time >= 0 && time < 200) { - distribution = '0-200ms'; - } else if (time >= 200 && time < 300) { - distribution = '200-300ms'; - } else if (time >= 300 && time < 400) { - distribution = '300-400ms'; - } else if (time >= 400 && time < 500) { - distribution = '400-500ms'; - } else if (time >= 500 && time < 600) { - distribution = '500-600ms'; - } else if (time >= 600 && time < 800) { - distribution = '600-800ms'; - } else if (time >= 800 && time < 1000) { - distribution = '800-1000ms'; - } else if (time >= 1000 && time < 1200) { - distribution = '1000-1200ms'; - } else if (time >= 1200 && time < 1500) { - distribution = '1200-1500ms'; - } else if (time >= 1500 && time < 2000) { - distribution = '1500-2000ms'; - } else if (time >= 2000) { - distribution = '2000ms above'; - } - - return distribution; -} - - -function getCpmDistribution(cpm) { - var distribution; - if (cpm >= 0 && cpm < 0.5) { - distribution = '$0-0.5'; - } else if (cpm >= 0.5 && cpm < 1) { - distribution = '$0.5-1'; - } else if (cpm >= 1 && cpm < 1.5) { - distribution = '$1-1.5'; - } else if (cpm >= 1.5 && cpm < 2) { - distribution = '$1.5-2'; - } else if (cpm >= 2 && cpm < 2.5) { - distribution = '$2-2.5'; - } else if (cpm >= 2.5 && cpm < 3) { - distribution = '$2.5-3'; - } else if (cpm >= 3 && cpm < 4) { - distribution = '$3-4'; - } else if (cpm >= 4 && cpm < 6) { - distribution = '$4-6'; - } else if (cpm >= 6 && cpm < 8) { - distribution = '$6-8'; - } else if (cpm >= 8) { - distribution = '$8 above'; - } - return distribution; -} - - - -function sendBidRequestToGa(bid) { - if (bid && bid.bidderCode) { - _analyticsQueue.push(function() { - _eventCount++; - window[_gaGlobal]('send', 'event', _category, 'Requests', bid.bidderCode, 1, _disibleInteraction); - }); - } - //check the queue - checkAnalytics(); -} - - -function sendBidResponseToGa(bid) { - - if (bid && bid.bidderCode) { - _analyticsQueue.push(function() { - var cpmCents = convertToCents(bid.cpm), - bidder = bid.bidderCode; - if (typeof bid.timeToRespond !== 'undefined' && _enableDistribution) { - _eventCount++; - var dis = getLoadTimeDistribution(bid.timeToRespond); - window[_gaGlobal]('send', 'event', 'Prebid.js Load Time Distribution', dis, bidder, 1, _disibleInteraction); - } - if (bid.cpm > 0) { - _eventCount = _eventCount + 2; - var cpmDis = getCpmDistribution(bid.cpm); - if(_enableDistribution){ - _eventCount++; - window[_gaGlobal]('send', 'event', 'Prebid.js CPM Distribution', cpmDis, bidder, 1, _disibleInteraction); - } - window[_gaGlobal]('send', 'event', _category, 'Bids', bidder, cpmCents, _disibleInteraction); - window[_gaGlobal]('send', 'event', _category, 'Bid Load Time', bidder, bid.timeToRespond, _disibleInteraction); - } - }); - } - //check the queue - checkAnalytics(); -} - -function sendBidTimeouts(bid){ - - if(bid && bid.bidder){ - _analyticsQueue.push(function(){ - utils._each(_timedOutBidders, function(bidderCode){ - if(bid.bidder === bidderCode){ - _eventCount++; - window[_gaGlobal]('send', 'event', _category, 'Timeouts', bidderCode, bid.timeToRespond, _disibleInteraction); - } - }); - }); - } - checkAnalytics(); -} - -function sendBidWonToGa(bid) { - var cpmCents = convertToCents(bid.cpm); - _analyticsQueue.push(function() { - _eventCount++; - window[_gaGlobal]('send', 'event', _category, 'Wins', bid.bidderCode, cpmCents, _disibleInteraction); - }); - checkAnalytics(); -} - -},{"./constants.json":16,"./events":17,"./utils":20}],20:[function(require,module,exports){ -var CONSTANTS = require('./constants.json'); -var objectType_function = 'function'; -var objectType_undefined = 'undefined'; -var objectType_object = 'object'; -var objectType_string = 'string'; -var objectType_number = 'number'; - -var _loggingChecked = false; - -var _lgPriceCap = 5.00; -var _mgPriceCap = 20.00; -var _hgPriceCap = 20.00; - -var t_Arr = 'Array', - t_Str = 'String', - t_Fn = 'Function', - toString = Object.prototype.toString, - hasOwnProperty = Object.prototype.hasOwnProperty, - slice = Array.prototype.slice; - -/* - * Substitues into a string from a given map using the token - * Usage - * var str = 'text %%REPLACE%% this text with %%SOMETHING%%'; - * var map = {}; - * map['replace'] = 'it was subbed'; - * map['something'] = 'something else'; - * console.log(replaceTokenInString(str, map, '%%')); => "text it was subbed this text with something else" - */ -exports.replaceTokenInString = function(str, map, token) { - this._each(map, function (value, key) { - value = (value === undefined) ? '' : value; - - var keyString = token + key.toUpperCase() + token, - re = new RegExp(keyString, 'g'); - - str = str.replace(re, value); - }); - return str; -}; - -/* utility method to get incremental integer starting from 1 */ -var getIncrementalInteger = (function() { - var count = 0; - return function() { - count++; - return count; - }; -})(); - -function _getUniqueIdentifierStr() { - return getIncrementalInteger() + Math.random().toString(16).substr(2); -} - -//generate a random string (to be used as a dynamic JSONP callback) -exports.getUniqueIdentifierStr = _getUniqueIdentifierStr; - -exports.getBidIdParamater = function(key, paramsObj) { - if (paramsObj && paramsObj[key]) { - return paramsObj[key]; - } - return ''; -}; - -exports.tryAppendQueryString = function(existingUrl, key, value) { - if (value) { - return existingUrl += key + '=' + encodeURIComponent(value) + '&'; - } - return existingUrl; -}; - - -//parse a query string object passed in bid params -//bid params should be an object such as {key: "value", key1 : "value1"} -exports.parseQueryStringParameters = function(queryObj) { - var result = ""; - for (var k in queryObj){ - if (queryObj.hasOwnProperty(k)) - result += k + "=" + encodeURIComponent(queryObj[k]) + "&"; - } - return result; -}; - - -//transform an AdServer targeting bids into a query string to send to the adserver -//bid params should be an object such as {key: "value", key1 : "value1"} -exports.transformAdServerTargetingObj = function(adServerTargeting) { - var result = ""; - if (!adServerTargeting) - return ""; - for (var k in adServerTargeting) - if (adServerTargeting.hasOwnProperty(k)) - result += k + "=" + encodeURIComponent(adServerTargeting[k]) + "&"; - return result; -}; - -//Copy all of the properties in the source objects over to the target object -//return the target object. -exports.extend = function(target, source){ - target = target || {}; - - this._each(source,function(value,prop){ - if (typeof source[prop] === objectType_object) { - target[prop] = this.extend(target[prop], source[prop]); - } else { - target[prop] = source[prop]; - } - }); - return target; -}; - -//parse a GPT-Style General Size Array or a string like "300x250" into a format -//suitable for passing to a GPT tag, may include size and/or promo sizes -exports.parseSizesInput = function(sizeObj) { - var sizeQueryString; - var parsedSizes = []; - - //if a string for now we can assume it is a single size, like "300x250" - if (typeof sizeObj === objectType_string) { - //multiple sizes will be comma-separated - var sizes = sizeObj.split(','); - //regular expression to match strigns like 300x250 - //start of line, at least 1 number, an "x" , then at least 1 number, and the then end of the line - var sizeRegex = /^(\d)+x(\d)+$/i; - if (sizes) { - for (var curSizePos in sizes) { - if (hasOwn(sizes, curSizePos) && sizes[curSizePos].match(sizeRegex)) { - parsedSizes.push(sizes[curSizePos]); - } - } - } - } else if (typeof sizeObj === objectType_object) { - var sizeArrayLength = sizeObj.length; - //don't process empty array - if (sizeArrayLength > 0) { - //if we are a 2 item array of 2 numbers, we must be a SingleSize array - if (sizeArrayLength === 2 && typeof sizeObj[0] === objectType_number && typeof sizeObj[1] === objectType_number) { - parsedSizes.push(this.parseGPTSingleSizeArray(sizeObj)); - } else { - //otherwise, we must be a MultiSize array - for (var i = 0; i < sizeArrayLength; i++) { - parsedSizes.push(this.parseGPTSingleSizeArray(sizeObj[i])); - } - - } - } - } - - - //combine string into proper querystring for impbus - var parsedSizesLength = parsedSizes.length; - if (parsedSizesLength > 0) { - //first value should be "size" - sizeQueryString = 'size=' + parsedSizes[0]; - if (parsedSizesLength > 1) { - //any subsequent values should be "promo_sizes" - sizeQueryString += '&promo_sizes='; - for (var j = 1; j < parsedSizesLength; j++) { - sizeQueryString += parsedSizes[j] += ','; - } - //remove trailing comma - if (sizeQueryString && sizeQueryString.charAt(sizeQueryString.length - 1) === ',') { - sizeQueryString = sizeQueryString.slice(0, sizeQueryString.length - 1); - } - } - } - - return sizeQueryString; - -}; - -//parse a GPT style sigle size array, (i.e [300,250]) -//into an AppNexus style string, (i.e. 300x250) -exports.parseGPTSingleSizeArray = function(singleSize) { - //if we aren't exactly 2 items in this array, it is invalid - if (this.isArray(singleSize) && singleSize.length === 2 && (!isNaN(singleSize[0]) && !isNaN(singleSize[1]))) { - return singleSize[0] + 'x' + singleSize[1]; - } -}; - -exports.getTopWindowUrl = function() { - try { - return window.top.location.href; - } catch (e) { - return window.location.href; - } -}; - -exports.logMessage = function(msg) { - if (debugTurnedOn() && hasConsoleLogger()) { - console.log('MESSAGE: ' + msg); - } -}; - -function hasConsoleLogger() { - return (window.console && window.console.log); -} -exports.hasConsoleLogger = hasConsoleLogger; - -var errLogFn = (function (hasLogger) { - if (!hasLogger) return ''; - return window.console.error ? 'error' : 'log'; -}(hasConsoleLogger())); - -var debugTurnedOn = function() { - if (pbjs.logging === false && _loggingChecked === false) { - pbjs.logging = getParameterByName(CONSTANTS.DEBUG_MODE).toUpperCase() === 'TRUE'; - _loggingChecked = true; - } - - if (pbjs.logging) { - return true; - } - return false; - -}; -exports.debugTurnedOn = debugTurnedOn; - -exports.logError = function(msg, code, exception) { - var errCode = code || 'ERROR'; - if (debugTurnedOn() && hasConsoleLogger()) { - console[errLogFn].call(console, errCode + ': ' + msg, exception || ''); - } -}; - -exports.createInvisibleIframe = function _createInvisibleIframe() { - var f = document.createElement('iframe'); - f.id = _getUniqueIdentifierStr(); - f.height = 0; - f.width = 0; - f.border = '0px'; - f.hspace = '0'; - f.vspace = '0'; - f.marginWidth = '0'; - f.marginHeight = '0'; - f.style.border = '0'; - f.scrolling = 'no'; - f.frameBorder = '0'; - f.src = 'about:self'; - f.style = 'display:none'; - return f; -}; - -/* - * Check if a given paramater name exists in query string - * and if it does return the value - */ -var getParameterByName = function(name) { - var regexS = '[\\?&]' + name + '=([^&#]*)', - regex = new RegExp(regexS), - results = regex.exec(window.location.search); - if (results === null) { - return ''; - } - return decodeURIComponent(results[1].replace(/\+/g, ' ')); -}; - -exports.getPriceBucketString = function(cpm) { - var low = '', - med = '', - high = '', - cpmFloat = 0, - returnObj = { - low: low, - med: med, - high: high - }; - try { - cpmFloat = parseFloat(cpm); - if (cpmFloat) { - //round to closet .5 - if (cpmFloat > _lgPriceCap) { - returnObj.low = _lgPriceCap.toFixed(2); - } else { - returnObj.low = (Math.floor(cpm * 2) / 2).toFixed(2); - } - - //round to closet .1 - if (cpmFloat > _mgPriceCap) { - returnObj.med = _mgPriceCap.toFixed(2); - } else { - returnObj.med = (Math.floor(cpm * 10) / 10).toFixed(2); - } - - //round to closet .01 - if (cpmFloat > _hgPriceCap) { - returnObj.high = _hgPriceCap.toFixed(2); - } else { - returnObj.high = (Math.floor(cpm * 100) / 100).toFixed(2); - } - } - } catch (e) { - this.logError('Exception parsing CPM :' + e.message); - } - return returnObj; - -}; - -/** - * This function validates paramaters. - * @param {object[string]} paramObj [description] - * @param {string[]} requiredParamsArr [description] - * @return {bool} Bool if paramaters are valid - */ -exports.hasValidBidRequest = function(paramObj, requiredParamsArr, adapter){ - - for(var i = 0; i < requiredParamsArr.length; i++){ - var found = false; - - this._each(paramObj, function (value, key) { - if (key === requiredParamsArr[i]) { - found = true; - } - }); - - if(!found){ - this.logError('Params are missing for bid request. One of these required paramaters are missing: ' + requiredParamsArr, adapter); - return false; - } - } - - return true; -}; - -// Handle addEventListener gracefully in older browsers -exports.addEventHandler = function(element, event, func) { - if (element.addEventListener) { - element.addEventListener(event, func, true); - } else if (element.attachEvent) { - element.attachEvent('on' + event, func); - } - }; - /** - * Return if the object is of the - * given type. - * @param {*} object to test - * @param {String} _t type string (e.g., Array) - * @return {Boolean} if object is of type _t - */ -exports.isA = function(object, _t) { - return toString.call(object) === '[object ' + _t + ']'; -}; - -exports.isFn = function (object) { - return this.isA(object, t_Fn); -}; - -exports.isStr = function (object) { - return this.isA(object, t_Str); -}; - -exports.isArray = function (object) { - return this.isA(object, t_Arr); -}; - -/** - * Return if the object is "empty"; - * this includes falsey, no keys, or no items at indices - * @param {*} object object to test - * @return {Boolean} if object is empty - */ -exports.isEmpty = function(object) { - if (!object) return true; - if (this.isArray(object) || this.isStr(object)) return !(object.length > 0); - for (var k in object) { - if (hasOwnProperty.call(object, k)) return false; - } - return true; - }; - - /** - * Iterate object with the function - * falls back to es5 `forEach` - * @param {Array|Object} object - * @param {Function(value, key, object)} fn - */ -exports._each = function(object, fn) { - if (this.isEmpty(object)) return; - if (this.isFn(object.forEach)) return object.forEach(fn, this); - - var k = 0, - l = object.length; - - if (l > 0) { - for (; k < l; k++) fn(object[k], k, object); - } else { - for (k in object) { - if (hasOwnProperty.call(object, k)) fn.call(this, object[k], k); - } - } - }; - -exports.contains = function(a, obj) { - if(this.isEmpty(a)){ - return false; - } - if (this.isFn(a.indexOf)) { - return a.indexOf(obj) !== -1; - } - var i = a.length; - while (i--) { - if (a[i] === obj) { - return true; - } - } - return false; -}; - -/** - * Map an array or object into another array - * given a function - * @param {Array|Object} object - * @param {Function(value, key, object)} callback - * @return {Array} - */ -exports._map = function (object, callback) { - if (this.isEmpty(object)) return []; - if (this.isFn(object.map)) return object.map(callback); - var output = []; - this._each(object, function (value, key) { - output.push(callback(value, key, object)); - }); - return output; -}; - -var hasOwn = function(objectToCheck, propertyToCheckFor) { - if (objectToCheck.hasOwnProperty) { - return objectToCheck.hasOwnProperty(propertyToCheckFor); - } else { - return (typeof objectToCheck[propertyToCheckFor] !== UNDEFINED) && (objectToCheck.constructor.prototype[propertyToCheckFor] !== objectToCheck[propertyToCheckFor]); - } -}; -},{"./constants.json":16}]},{},[18]) \ No newline at end of file diff --git a/dist/prebid.min.js b/dist/prebid.min.js deleted file mode 100644 index c313f8de088..00000000000 --- a/dist/prebid.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/* Prebid.js v0.5.0 -Updated : 2016-01-11 */ -!function e(t,r,i){function n(s,d){if(!r[s]){if(!t[s]){var o="function"==typeof require&&require;if(!d&&o)return o(s,!0);if(a)return a(s,!0);throw new Error("Cannot find module '"+s+"'")}var c=r[s]={exports:{}};t[s][0].call(c.exports,function(e){var r=t[s][1][e];return n(r?r:e)},c,c.exports,e,t,r,i)}return r[s].exports}for(var a="function"==typeof require&&require,s=0;s=i){s=!0,d=p[o],a.trackPixel(d);break}s||(d=l,a.trackPixel(d)),n.logMessage("latency for placement code : "+r+" : "+i+" ms. Tracking URL Fired : "+d)}}function c(e,t){var r=n.getBidIdParamater("placementId",e.params),i=n.getBidIdParamater("memberId",e.params),a=n.getBidIdParamater("invCode",e.params),s=n.getBidIdParamater("query",e.params),d=n.getBidIdParamater("referrer",e.params),o=n.getBidIdParamater("alt_referrer",e.params),c="http"+("https:"===document.location.protocol?"s://secure.adnxs.com/jpt?":"://ib.adnxs.com/jpt?");c=n.tryAppendQueryString(c,"callback","pbjs.handleAnCB"),c=n.tryAppendQueryString(c,"callback_uid",t),c=n.tryAppendQueryString(c,"psa","0"),c=n.tryAppendQueryString(c,"id",r),c=n.tryAppendQueryString(c,"member_id",i),c=n.tryAppendQueryString(c,"code",a);var u=n.parseSizesInput(e.sizes);u&&(c+=u+"&");var p=n.parseQueryStringParameters(s);p&&(c+=p);var l=n.extend({},e.params);delete l.placementId,delete l.memberId,delete l.invCode,delete l.query,delete l.referrer,delete l.alt_referrer;var f=n.parseQueryStringParameters(l);return f&&(c+=f),""===d&&(d=n.getTopWindowUrl()),c=n.tryAppendQueryString(c,"referrer",d),c=n.tryAppendQueryString(c,"alt_referrer",o),c.lastIndexOf("&")===c.length-1&&(c=c.substring(0,c.length-1)),e.startTime=(new Date).getTime(),c}var u=o.createNew("appnexus"),p=[];p[100]=e(21139),p[200]=e(21140),p[300]=e(21141),p[400]=e(21142),p[500]=e(21143),p[600]=e(21144),p[700]=e(21145),p[800]=e(21146),p[1e3]=e(21147),p[1300]=e(21148),p[1600]=e(21149),p[2e3]=e(21150),p[5e3]=e(21151),p[1e4]=e(21152);{var l=e(21154);e(21153)}return u.callBids=function(e){var t=u.getBidderCode(),r=e.bids,i=r.length;s.setExpectedBidsCount(t,i);for(var d=0;i>d;d++){var o=r[d],p=n.getUniqueIdentifierStr();a.loadScript(c(o,p)),s.pbCallbackMap[p]=o}},pbjs.handleAnCB=function(e){var r;if(e&&e.callback_uid){var n,a=e.callback_uid,o="",c=s.getPlacementIdByCBIdentifer(a);if(c){r=c.bidder,o=c.placementCode,c.status=i.STATUS.GOOD;try{t(c.startTime,(new Date).getTime(),o)}catch(u){}}var p=[];if(e.result&&e.result.cpm&&0!==e.result.cpm){n=parseInt(e.result.cpm,10),n/=1e4;var l=(e.result.ad,e.result.creative_id);p=d.createBid(1),p.creative_id=l,p.bidderCode=r,p.cpm=n,p.adUrl=e.result.ad,p.width=e.result.width,p.height=e.result.height,p.dealId=e.result.deal_id,s.addBidResponse(o,p)}else p=d.createBid(2),p.bidderCode=r,s.addBidResponse(o,p)}},{callBids:u.callBids,setBidderCode:u.setBidderCode,createNew:r.createNew,buildJPTCall:c}};r.createNew=function(){return new c}},{"../adloader.js":13,"../bidfactory.js":14,"../bidmanager.js":15,"../constants.json":16,"../utils.js":20,"./adapter.js":2}],5:[function(e,t,r){var i=(e("../constants.json"),e("../utils.js"),e("../bidfactory.js")),n=e("../bidmanager.js"),a=e("../adloader"),s=function(){function e(e){s=e.bids||[],t(s).forEach(r)}function t(e){var t,r={},i=[];e.forEach(function(e){r[e.params.nid]=e});for(t in r)r.hasOwnProperty(t)&&i.push(r[t]);return i}function r(e){var t=e.params.varname,r="//rtax.criteo.com/delivery/rta/rta.js?netId="+encodeURI(e.params.nid)+"&cookieName="+encodeURI(e.params.cookiename)+"&rnd="+Math.floor(99999999999*Math.random())+"&varName="+encodeURI(t);a.loadScript(r,function(r){var a,d=window[t];s.forEach(function(t){t.params.nid===e.params.nid&&(d?(a=i.createBid(1),a.bidderCode="criteo",a.keys=d.split(";")):(a=i.createBid(2),a.bidderCode="criteo"),n.addBidResponse(t.placementCode,a))})})}var s;return{callBids:e}};t.exports=s},{"../adloader":13,"../bidfactory.js":14,"../bidmanager.js":15,"../constants.json":16,"../utils.js":20}],6:[function(e,t,r){var n=(e("../constants.json"),e("../utils.js")),a=e("../bidfactory.js"),s=e("../bidmanager.js"),d=e("../adloader.js"),o="INDEXEXCHANGE",c="indexExchange",u=!0,p=function(){};window.cygnus_index_args={};var l=[[728,90],[120,600],[300,250],[160,600],[336,280],[234,60],[300,600],[300,50],[320,50],[970,250],[300,1050],[970,90],[180,150]],f=function(){function e(e){var t=n[e];return"string"==typeof t?t:"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)}function t(t){return i.lastIndex=0,i.test(t)?t.replace(i,e):t}function r(e,t,r){if(this.initialized=!1,"number"!=typeof e||e%1!==0||0>e)throw"Invalid Site ID";if("number"==typeof r&&r%1===0&&r>=0&&(this.timeoutDelay=r),this.siteID=e,this.impressions=[],this._parseFnName=void 0,top===self?(this.sitePage=location.href,this.topframe=1):(this.sitePage=document.referrer,this.topframe=0),"undefined"!=typeof t){if("function"!=typeof t)throw"Invalid jsonp target function";this._parseFnName="cygnus_index_args.parseFn"}_IndexRequestData.requestCounter="undefined"==typeof _IndexRequestData.requestCounter?Math.floor(256*Math.random()):(_IndexRequestData.requestCounter+1)%256,this.requestID=String((new Date).getTime()%2592e3*256+_IndexRequestData.requestCounter+256),this.initialized=!0}cygnus_index_args.parseFn=p;var i=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,n={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};r.prototype.serialize=function(){var e='{"id":'+this.requestID+',"site":{"page":"'+t(this.sitePage)+'"';"string"==typeof document.referrer&&(e+=',"ref":"'+t(document.referrer)+'"'),e+='},"imp":[';for(var r=0;r0&&(e+=',"ext": {'+n.join()+"}"),e+=r+1==this.impressions.length?"}":"},"}return e+="]}"},r.prototype.setPageOverride=function(e){return"string"!=typeof e||e.match(/^\s*$/)?!1:(this.sitePage=e,!0)},r.prototype.addImpression=function(e,t,r,i,n,a){var s={id:String(this.impressions.length+1)};if("number"!=typeof e||1>=e)return null;if("number"!=typeof t||1>=t)return null;if(("string"==typeof n||"number"==typeof n)&&String(n).length<=50&&(s.slotID=String(n)),s.w=e,s.h=t,void 0!==r&&"number"!=typeof r)return null;if("number"==typeof r){if(0>r)return null;if(s.bidfloor=r,void 0!==i&&"string"!=typeof i)return null;s.bidfloorcur=i}if("undefined"!=typeof a){if(!("number"==typeof a&&a%1===0&&a>=0))return null;s.siteID=a}return this.impressions.push(s),s.id},r.prototype.buildRequest=function(){if(0!==this.impressions.length&&this.initialized===!0){var e=encodeURIComponent(this.serialize()),t="https:"===window.location.protocol?"https://as-sec.casalemedia.com":"http://as.casalemedia.com";return t+="/headertag?v=9&x3=1&fn=cygnus_index_parse_res&s="+this.siteID+"&r="+e,"number"==typeof this.timeoutDelay&&this.timeoutDelay%1===0&&this.timeoutDelay>=0&&(t+="&t="+this.timeoutDelay),t}};try{if("undefined"==typeof cygnus_index_args||"undefined"==typeof cygnus_index_args.siteID||"undefined"==typeof cygnus_index_args.slots)return;"undefined"==typeof _IndexRequestData&&(_IndexRequestData={},_IndexRequestData.impIDToSlotID={},_IndexRequestData.reqOptions={});var a=new r(cygnus_index_args.siteID,cygnus_index_args.parseFn,cygnus_index_args.timeout);cygnus_index_args.url&&"string"==typeof cygnus_index_args.url&&a.setPageOverride(cygnus_index_args.url),_IndexRequestData.impIDToSlotID[a.requestID]={},_IndexRequestData.reqOptions[a.requestID]={};for(var s,d,o=0;o { anchor.addEventListener('click', e => { e.preventDefault(); const redact = new URLSearchParams(window.location.search).get('redact'); const hasExistingParams = anchor.href.includes('?'); window.location.href = anchor.href + (hasExistingParams ? `&redact=${redact}` : `?redact=${redact}`); }); }); }); ';e+="",e+="";var t={};return t.PM_PUB_ID=d,t.PM_OPTIMIZE_ADSLOTS=o.map(function(e){return"'"+e+"'"}).join(","),e+='',e+="",e+="",e=i.replaceTokenInString(e,t,"%%")}var s,d,o=[];return pbjs.handlePubmaticCallback=function(e){var t,r,i,d,o,c=e&&e.bidDetailsMap||{},u=e&&e.progKeyValueMap||{};for(t=0;t { anchor.addEventListener('click', e => { e.preventDefault(); const redact = new URLSearchParams(window.location.search).get('redact'); const hasExistingParams = anchor.href.includes('?'); window.location.href = anchor.href + (hasExistingParams ? `&redact=${redact}` : `?redact=${redact}`); }); }); }); ';a+="",a+="",a+='',a+="",a=n.replaceTokenInString(a,s,"%%")}var o={};return window.pbjs=window.pbjs||{que:[]},window.pbjs.handleRubiconCallback=function(e){var r="",d={};if(e&&"ok"===e.status)try{var c="",u=s.getPlacementIdByCBIdentifer(t(e));if(u&&(r=u.placementCode,u.status=i.STATUS.GOOD,c=u.iframeId),e.ads&&e.ads[0]&&"ok"===e.ads[0].status){d=a.createBid(1);var p,l=e.ads[0],f=o[l.size_id],g=0,m=0,b=window.frames[c];if(p=b.contentWindow?b.contentWindow.RubiconAdServing:b.window.RubiconAdServing,p&&p.AdSizes){f=p.AdSizes[l.size_id];var h=f.dim.split("x");g=h[0],m=h[1]}d.cpm=l.cpm,d.ad="",d.ad_id=l.ad_id,d.bidderCode="rubicon",d.sizeId=l.size_id,d.width=g,d.height=m}else{d=a.createBid(2),d.bidderCode="rubicon";var u=s.getPlacementIdByCBIdentifer(t(e));u&&(r=u.placementCode)}}catch(v){n.logError("Error parsing rubicon response bid: "+v.message)}else{d=a.createBid(2),d.bidderCode="rubicon";var u=s.getPlacementIdByCBIdentifer(t(e));u&&(r=u.placementCode)}s.addBidResponse(r,d)},{callBids:e}};t.exports=d},{"../bidfactory.js":14,"../bidmanager.js":15,"../constants.json":16,"../utils.js":20}],11:[function(e,t,r){var i=e("../constants.json"),n=e("../utils.js"),a=e("../bidfactory.js"),s=e("../bidmanager.js"),d=e("../adloader"),o="",c=function(){function e(e){var i=e.bids||[];r(t(i))}function t(e){var t,r={},i=[];e.forEach(function(e){r[n.getBidIdParamater("tagid",e.params)]=e});for(t in r)r.hasOwnProperty(t)&&i.push(r[t]);return i}function r(e){var t=window.location.host,r=window.location.pathname+location.search+location.hash,i=[];o=e[0].placementCode,n._each(e,function(e){var t=n.getBidIdParamater("tagid",e.params),r=n.getBidIdParamater("bidfloor",e.params),a=0,d=0,o=e.sizes.length;2===o&&"number"==typeof e.sizes[0]&&"number"==typeof e.sizes[1]?(a=e.sizes[0],d=e.sizes[1]):(a=e.sizes[0][0],d=e.sizes[0][1]),imp={id:n.getUniqueIdentifierStr(),banner:{w:a,h:d},tagid:t,bidfloor:r},i.push(imp),s.pbCallbackMap[imp.id]=e});var a={id:n.getUniqueIdentifierStr(),imp:i,site:{domain:t,page:r}},u="//"+c+"?callback=window.pbjs.sovrnResponse&br="+encodeURIComponent(JSON.stringify(a));d.loadScript(u,null)}var c="ap.lijit.com/rtb/bid";return pbjs.sovrnResponse=function(e){var t={};e&&e.id&&e.seatbid&&0!==e.seatbid.length&&e.seatbid[0].bid&&0!==e.seatbid[0].bid.length?e.seatbid[0].bid.forEach(function(e){var r,n="",d=e.impid,o=s.getPlacementIdByCBIdentifer(d);if(o)if(n=o.placementCode,o.status=i.STATUS.GOOD,r=parseFloat(e.price),0!==r){e.placementCode=n,e.size=o.sizes;var c=e.adm,u='';t=a.createBid(1),t.creative_id=e.Id,t.bidderCode="sovrn",t.cpm=r,t.ad=decodeURIComponent(c+u);var p=o.sizes.length;2===p&&"number"==typeof o.sizes[0]&&"number"==typeof o.sizes[1]?(t.width=o.sizes[0],t.height=o.sizes[1]):(t.width=o.sizes[0][0],t.height=o.sizes[0][1]),s.addBidResponse(n,t)}else t=a.createBid(2),t.bidderCode="sovrn",s.addBidResponse(n,t);else t=a.createBid(2),t.bidderCode="sovrn",s.addBidResponse(n,t)}):(t=a.createBid(2),t.bidderCode="sovrn",s.addBidResponse(o,t))},{callBids:e}};t.exports=c},{"../adloader":13,"../bidfactory.js":14,"../bidmanager.js":15,"../constants.json":16,"../utils.js":20}],12:[function(e,t,r){var i=e("../adloader"),n=e("../bidfactory"),a=e("../bidmanager"),s=e("../utils"),d=function(){window.ybotq=window.ybotq||[];var e={BID_STATUS:{PENDING:0,AVAILABLE:1,EMPTY:2},definedSlots:[],pageLevelOption:!1,buildCreative:function(e,t){return'"},buildBid:function(t){var r={};if(t&&t.ybot_ad&&"n"!==t.ybot_ad){r=n.createBid(e.BID_STATUS.AVAILABLE),r.cpm=parseInt(t.ybot_cpm)/100||0;var i=t.ybot_size?t.ybot_size.split("x"):[0,0],a=t.ybot_slot||"",s=t.ybot_size||"";r.width=i[0]||0,r.height=i[1]||0,r.ad=e.buildCreative(a,s);for(var d in t)r[d]=t[d]}else r=n.createBid(e.BID_STATUS.EMPTY);return r.bidderCode="yieldbot",r},callBids:function(t){var r=t.bids||[],n=window.ybotq||[];e.pageLevelOption=!1,n.push(function(){var t=window.yieldbot;s._each(r,function(r){var i=r,n=i.params&&i.params.psn||"ERROR_DEFINE_YB_PSN",d=i.params&&i.params.slot||"ERROR_DEFINE_YB_SLOT";t.pub(n),t.defineSlot(d,{sizes:i.sizes||[]});var o=s.getUniqueIdentifierStr();a.pbCallbackMap[o]=i,e.definedSlots.push(o)}),t.enableAsync(),t.go()}),n.push(function(){e.handleUpdateState()}),i.loadScript("//cdn.yldbt.com/js/yieldbot.intent.js")},handleUpdateState:function(){var t=window.yieldbot;s._each(e.definedSlots,function(r){var i,n,s,d;d=a.getPlacementIdByCBIdentifer(r)||{},i=d.params.slot||"",n=t.getSlotCriteria(i),s=d.placementCode||"ERROR_YB_NO_PLACEMENT";var o=e.buildBid(n);a.addBidResponse(s,o)})}};return{callBids:e.callBids}};t.exports=d},{"../adloader":13,"../bidfactory":14,"../bidmanager":15,"../utils":20}],13:[function(e,t,r){var i=e("./utils");r.loadScript=function(e,t){if(!e)return void i.logError("Error attempting to request empty URL","adloader.js:loadScript");var r=document.createElement("script");r.type="text/javascript",r.async=!0,t&&"function"==typeof t&&(r.readyState?r.onreadystatechange=function(){("loaded"==r.readyState||"complete"==r.readyState)&&(r.onreadystatechange=null,t())}:r.onload=function(){t()}),r.src=e;var n=document.getElementsByTagName("head");n=n.length?n:document.getElementsByTagName("body"),n.length&&(n=n[0],n.insertBefore(r,n.firstChild))},r.trackPixel=function(e){try{if(e+="&rnd="+Math.random()){var t=document.createElement("img");t.src=e}}catch(r){}}},{"./utils":20}],14:[function(e,t,r){function i(e){function t(){switch(i){case 0:return"Pending";case 1:return"Bid available";case 2:return"Bid returned empty or error response";case 3:return"Bid timed out"}}var r=n.getUniqueIdentifierStr(),i=e||0;this.bidderCode="",this.width=0,this.height=0,this.statusMessage=t(),this.adId=r,this.getStatusCode=function(){return i},this.getSize=function(){return this.width+"x"+this.height}}var n=e("./utils.js");r.createBid=function(e){return new i(e)}},{"./utils.js":20}],15:[function(e,t,r){function i(){T={};for(var e=0;et)&&(e=!1)}),e}function f(e){var t=e.bidderCode,r=e.cpm;if(t&&pbjs.bidderSettings&&pbjs.bidderSettings[t]&&typeof pbjs.bidderSettings[t].bidCpmAdjustment===h)try{r=pbjs.bidderSettings[t].bidCpmAdjustment.call(null,e.cpm)}catch(i){m.logError("Error during bid adjustment","bidmanager.js",i)}0!==r&&(e.cpm=r)}var g=e("./constants.json"),m=e("./utils.js"),b=(e("./adaptermanager"),e("./events")),h="function",v="undefined",y=[],_=[],B=null,I={},w={};r.pbCallbackMap=w;var E={};r.pbBidResponseByPlacement=E;var C={};r._adResponsesByBidderId=C;var T={};r.bidResponseReceivedCount=T;var S={},R=!1,A=!1,j={},D={};r.getPlacementIdByCBIdentifer=function(e){return w[e]},r.getBidResponseByAdUnit=function(e){return E},r.clearAllBidResponses=function(e){R=!1,A=!1,i(),a(),_.called=!1;for(var t in this.pbBidResponseByPlacement)delete this.pbBidResponseByPlacement[t]},r.getTimedOutBidders=function(){var e=[];return m._each(T,function(t,r){0===t&&e.push(r)}),e},r.increaseBidResponseReceivedCount=function(e){n(e)},r.setExpectedBidsCount=function(e,t){S[e]=t},r.getExpectedBidsCount=s,r.addBidResponse=function(e,t){var r={};if(t){t.requestTimestamp=D[t.bidderCode],t.responseTimestamp=(new Date).getTime(),t.timeToRespond=t.responseTimestamp-t.requestTimestamp,n(t.bidderCode),2===t.getStatusCode()&&(t.cpm=0),b.emit(g.EVENTS.BID_ADJUSTMENT,t),b.emit(g.EVENTS.BID_RESPONSE,e,t);var i=m.getPriceBucketString(t.cpm,t.height,t.width);t.pbLg=i.low,t.pbMg=i.med,t.pbHg=i.high,t.adUnitCode=e,t.bidder=t.bidderCode;var a={};t.bidderCode&&0!==t.cpm&&(a=this.getKeyValueTargetingPairs(t.bidderCode,t),t.adserverTargeting=a),t.adId&&(C[t.adId]=t),e&&E[e]?(r=E[e],r.bids.push(t),r.bidsReceivedCount++):m.logError("Internal error in bidmanager.addBidResponse. Params: "+e+" & "+t)}else r=this.createEmptyBidResponseObj();E[e]=r,this.checkIfAllBidsAreIn(e)},r.createEmptyBidResponseObj=function(){return{bids:[],allBidsAvailable:!1,bidsReceivedCount:0}},r.getKeyValueTargetingPairs=function(e,t){var r={},i=pbjs.bidderSettings||{};return e&&t&&i&&i[e]&&i[e][g.JSON_MAPPING.ADSERVER_TARGETING]?(d(r,i[e],t),t.alwaysUseBid=i[e].alwaysUseBid):j[e]?(d(r,j[e],t),t.alwaysUseBid=j[e].alwaysUseBid):t&&i&&(i[g.JSON_MAPPING.BD_SETTING_STANDARD]||(i[g.JSON_MAPPING.BD_SETTING_STANDARD]={adserverTargeting:[{key:"hb_bidder",val:function(e){return e.bidderCode}},{key:"hb_adid",val:function(e){return e.adId}},{key:"hb_pb",val:function(e){return e.pbMg}},{key:"hb_size",val:function(e){return e.size}}]}),d(r,i[g.JSON_MAPPING.BD_SETTING_STANDARD],t)),r},r.registerDefaultBidderSetting=function(e,t){j[e]=t},r.registerBidRequestTime=function(e,t){D[e]=t},r.executeCallback=function(){if(typeof pbjs.registerBidCallbackHandler===h&&!A)try{pbjs.registerBidCallbackHandler(),A=!0}catch(e){A=!0,m.logError("Exception trying to execute callback handler registered : "+e.message)}if(_.called!==!0){var t=[];c(_,t),_.called=!0}if(B){var t=[],r=pbjs.getBidResponses();t.push(r),c(B,t),B=null}},r.allBidsBack=function(){return R},r.setBidderMap=function(e){I=e},r.checkIfAllBidsAreIn=function(e){R=l(),p(e),R&&this.executeCallback()},r.addOneTimeCallback=function(e){B=e},r.addCallback=function(e,t,r){t.id=e,g.CB.TYPE.ALL_BIDS_BACK===r?_.push(t):g.CB.TYPE.AD_UNIT_BIDS_BACK===r&&y.push(t)},b.on(g.EVENTS.BID_ADJUSTMENT,function(e){f(e)})},{"./adaptermanager":1,"./constants.json":16,"./events":17,"./utils.js":20}],16:[function(e,t,r){t.exports={JSON_MAPPING:{PL_CODE:"code",PL_SIZE:"sizes",PL_BIDS:"bids",BD_BIDDER:"bidder",BD_ID:"paramsd",BD_PL_ID:"placementId",ADSERVER_TARGETING:"adserverTargeting",BD_SETTING_STANDARD:"standard"},DEBUG_MODE:"pbjs_debug",STATUS:{GOOD:"good",TIMEOUT:"timed out"},CB:{TYPE:{ALL_BIDS_BACK:"allRequestedBidsBack", -AD_UNIT_BIDS_BACK:"adUnitBidsBack"}},objectType_function:"function",objectType_undefined:"undefined",objectType_object:"object",objectType_string:"string",objectType_number:"number",EVENTS:{BID_ADJUSTMENT:"bidAdjustment",BID_TIMEOUT:"bidTimeout",BID_REQUESTED:"bidRequested",BID_RESPONSE:"bidResponse",BID_WON:"bidWon"}}},{}],17:[function(e,t,r){var i=e("./utils"),n=e("./constants"),a=Array.prototype.slice,s=i._map(n.EVENTS,function(e){return e}),d=[];t.exports=function(){function e(e,t){i.logMessage("Emitting event for: "+e),d.push({eventType:e,args:t}),i._each(r[e],function(e){if(e)try{e.apply(null,t)}catch(r){i.logError("Error executing handler:","events.js",r)}})}function t(e){return i.contains(s,e)}var r={},n={};return n.on=function(e,n){t(e)?(r[e]=r[e]||[],r[e].push(n)):i.logError("Wrong event name : "+e+" Valid event names :"+s)},n.emit=function(t){var r=a.call(arguments,1);e(t,r)},n.off=function(e,t,n){i.isEmpty(r[e])||i._each(r[e],function(e){null!==e[t]&&void 0!==e[t]&&("undefined"==typeof n||e[t]===n)&&(e[t]=null)})},n.get=function(){return r},n.getEvents=function(){var e=[];return i._each(d,function(t){var r=i.extend({},t);e.push(r)}),e},n}()},{"./constants":16,"./utils":20}],18:[function(e,t,r){function i(){for(var e=0;e0&&i.push({cpm:s.cpm,bid:s}),t.push(n)}}if(r&&0!==i.length){var d=u(i),o=d.adserverTargeting;O[r]=_.extend(O[r],o)}return t}function g(e){var t={};if(e){var r=JSON.stringify(e);t=JSON.parse(r),delete t.pbLg,delete t.pbMg,delete t.pbHg}return t}function m(){B.clearAllBidResponses(),N={},U=[],O={},q=!1}function b(e){var t=e;m(),n(t)}function h(e){var t=null;return e&&(t=v.getAdserverTargetingForAdUnitCode(e.getSlotElementId()),t||(t=v.getAdserverTargetingForAdUnitCode(e.getAdUnitPath()))),t}window.pbjs=window.pbjs||{},window.pbjs.que=window.pbjs.que||[];var v=window.pbjs,y=e("./constants.json"),_=e("./utils.js"),B=e("./bidmanager.js"),I=e("./adaptermanager"),w=e("./bidfactory"),E=e("./adloader"),C=e("./ga"),T=e("./events"),S="function",R="undefined",A="object",j="string",D=y.EVENTS.BID_WON,P=y.EVENTS.BID_TIMEOUT,x=[],U=[],N={},O={},M={},q=!1;v.bidderTimeout=v.bidderTimeout||3e3,v.logging=v.logging||!1,v.libLoaded=!0,v.adUnits=v.adUnits||[],v.que.push=function(e){if(typeof e===S)try{e.call()}catch(t){_.logError("Error processing command :"+t.message)}else _.logError("Commands written into pbjs.que.push must wrapped in a function")},v.getAdserverTargetingForAdUnitCodeStr=function(e){if(e){var t=v.getAdserverTargetingForAdUnitCode(e);return _.transformAdServerTargetingObj(t)}_.logMessage("Need to call getAdserverTargetingForAdUnitCodeStr with adunitCode")},v.getAdserverTargetingForAdUnitCode=function(e){return v.getBidResponses(e),e?O[e]:O},v.getAdserverTargeting=function(){return v.getAdserverTargetingForAdUnitCode()},v.getBidResponses=function(e){var t={},r=[],i={};if(e)t=l(e),r=[],t&&t.bids&&(r=f(t.bids)),i={bids:r};else{t=l();for(var n in t)t.hasOwnProperty(n)&&(t&&t[n]&&t[n].bids&&(r=f(t[n].bids)),i[n]={bids:r})}return i},v.getBidResponsesForAdUnitCode=function(e){return v.getBidResponses(e)},v.setTargetingForAdUnitsGPTAsync=function(e){if(!window.googletag||!_.isFn(window.googletag.pubads)||!_.isFn(window.googletag.pubads().getSlots))return void _.logError("window.googletag is not defined on the page");s();var t=e;typeof e===j?t=[e]:typeof e===A&&(t=e);var r={},i=0;if(t)for(i=0;i'),e.close(),e.defaultView&&e.defaultView.frameElement&&(e.defaultView.frameElement.width=n,e.defaultView.frameElement.height=i)):_.logError("Error trying to write ad. No ad for bid response id: "+t)}else _.logError("Error trying to write ad. Cannot find ad by given id : "+t)}catch(d){_.logError("Error trying to write ad Id :"+t+" to the page:"+d.message)}else _.logError("Error trying to write ad Id :"+t+" to the page. Missing document or adId")},v.requestBidsForAdUnit=function(e){m(),n(e)},v.requestBidsForAdUnits=function(e){if(!e||e.constructor!==Array)return void _.logError("requestBidsForAdUnits must pass an array of adUnits");m();var t=v.adUnits.slice(0);v.adUnits=e,n(),v.adUnits=t},v.removeAdUnit=function(e){if(e)for(var t=0;t=0&&200>e?t="0-200ms":e>=200&&300>e?t="200-300ms":e>=300&&400>e?t="300-400ms":e>=400&&500>e?t="400-500ms":e>=500&&600>e?t="500-600ms":e>=600&&800>e?t="600-800ms":e>=800&&1e3>e?t="800-1000ms":e>=1e3&&1200>e?t="1000-1200ms":e>=1200&&1500>e?t="1200-1500ms":e>=1500&&2e3>e?t="1500-2000ms":e>=2e3&&(t="2000ms above"),t}function s(e){var t;return e>=0&&.5>e?t="$0-0.5":e>=.5&&1>e?t="$0.5-1":e>=1&&1.5>e?t="$1-1.5":e>=1.5&&2>e?t="$1.5-2":e>=2&&2.5>e?t="$2-2.5":e>=2.5&&3>e?t="$2.5-3":e>=3&&4>e?t="$3-4":e>=4&&6>e?t="$4-6":e>=6&&8>e?t="$6-8":e>=8&&(t="$8 above"),t}function d(e){e&&e.bidderCode&&y.push(function(){w++,window[_]("send","event",I,"Requests",e.bidderCode,1,v)}),i()}function o(e){e&&e.bidderCode&&y.push(function(){var t=n(e.cpm),r=e.bidderCode;if("undefined"!=typeof e.timeToRespond&&E){w++;var i=a(e.timeToRespond);window[_]("send","event","Prebid.js Load Time Distribution",i,r,1,v)}if(e.cpm>0){w+=2;var d=s(e.cpm);E&&(w++,window[_]("send","event","Prebid.js CPM Distribution",d,r,1,v)),window[_]("send","event",I,"Bids",r,t,v),window[_]("send","event",I,"Bid Load Time",r,e.timeToRespond,v)}}),i()}function c(e){e&&e.bidder&&y.push(function(){l._each(C,function(t){e.bidder===t&&(w++,window[_]("send","event",I,"Timeouts",t,e.timeToRespond,v))})}),i()}function u(e){var t=n(e.cpm);y.push(function(){w++,window[_]("send","event",I,"Wins",e.bidderCode,t,v)}),i()}var p=e("./events"),l=e("./utils"),f=e("./constants.json"),g=f.EVENTS.BID_REQUESTED,m=f.EVENTS.BID_TIMEOUT,b=f.EVENTS.BID_RESPONSE,h=f.EVENTS.BID_WON,v={nonInteraction:!0},y=[],_=null,B=!0,I="Prebid.js Bids",w=0,E=!1,C=[];r.enableAnalytics=function(e){_="undefined"!=typeof e.global?e.global:"ga","undefined"!=typeof e.enableDistribution&&(E=e.enableDistribution);var t=null,r=p.getEvents();l._each(r,function(e){var r=e.args;if(e)if(e.eventType===g)t=r[0],d(t);else if(e.eventType===b)t=r[1],o(t);else if(e.eventType===m){var i=r[0];C=i}else e.eventType===h&&(t=r[0],u(t))}),p.on(g,function(e){d(e)}),p.on(b,function(e,t){o(t),c(t)}),p.on(m,function(e){C=e}),p.on(h,function(e){u(e)})}},{"./constants.json":16,"./events":17,"./utils":20}],20:[function(e,t,r){function i(){return v()+Math.random().toString(16).substr(2)}function n(){return window.console&&window.console.log}{var a=e("./constants.json"),s="object",d="string",o="number",c=!1,u=5,p=20,l=20,f="Array",g="String",m="Function",b=Object.prototype.toString,h=Object.prototype.hasOwnProperty;Array.prototype.slice}r.replaceTokenInString=function(e,t,r){return this._each(t,function(t,i){t=void 0===t?"":t;var n=r+i.toUpperCase()+r,a=new RegExp(n,"g");e=e.replace(a,t)}),e};var v=function(){var e=0;return function(){return e++,e}}();r.getUniqueIdentifierStr=i,r.getBidIdParamater=function(e,t){return t&&t[e]?t[e]:""},r.tryAppendQueryString=function(e,t,r){return r?e+=t+"="+encodeURIComponent(r)+"&":e},r.parseQueryStringParameters=function(e){var t="";for(var r in e)e.hasOwnProperty(r)&&(t+=r+"="+encodeURIComponent(e[r])+"&");return t},r.transformAdServerTargetingObj=function(e){var t="";if(!e)return"";for(var r in e)e.hasOwnProperty(r)&&(t+=r+"="+encodeURIComponent(e[r])+"&");return t},r.extend=function(e,t){return e=e||{},this._each(t,function(r,i){e[i]=typeof t[i]===s?this.extend(e[i],t[i]):t[i]}),e},r.parseSizesInput=function(e){var t,r=[];if(typeof e===d){var i=e.split(","),n=/^(\d)+x(\d)+$/i;if(i)for(var a in i)I(i,a)&&i[a].match(n)&&r.push(i[a])}else if(typeof e===s){var c=e.length;if(c>0)if(2===c&&typeof e[0]===o&&typeof e[1]===o)r.push(this.parseGPTSingleSizeArray(e));else for(var u=0;c>u;u++)r.push(this.parseGPTSingleSizeArray(e[u]))}var p=r.length;if(p>0&&(t="size="+r[0],p>1)){t+="&promo_sizes=";for(var l=1;p>l;l++)t+=r[l]+=",";t&&","===t.charAt(t.length-1)&&(t=t.slice(0,t.length-1))}return t},r.parseGPTSingleSizeArray=function(e){return!this.isArray(e)||2!==e.length||isNaN(e[0])||isNaN(e[1])?void 0:e[0]+"x"+e[1]},r.getTopWindowUrl=function(){try{return window.top.location.href}catch(e){return window.location.href}},r.logMessage=function(e){_()&&n()&&console.log("MESSAGE: "+e)},r.hasConsoleLogger=n;var y=function(e){return e?window.console.error?"error":"log":""}(n()),_=function(){return pbjs.logging===!1&&c===!1&&(pbjs.logging="TRUE"===B(a.DEBUG_MODE).toUpperCase(),c=!0),pbjs.logging?!0:!1};r.debugTurnedOn=_,r.logError=function(e,t,r){var i=t||"ERROR";_()&&n()&&console[y].call(console,i+": "+e,r||"")},r.createInvisibleIframe=function(){var e=document.createElement("iframe");return e.id=i(),e.height=0,e.width=0,e.border="0px",e.hspace="0",e.vspace="0",e.marginWidth="0",e.marginHeight="0",e.style.border="0",e.scrolling="no",e.frameBorder="0",e.src="about:self",e.style="display:none",e};var B=function(e){var t="[\\?&]"+e+"=([^&#]*)",r=new RegExp(t),i=r.exec(window.location.search);return null===i?"":decodeURIComponent(i[1].replace(/\+/g," "))};r.getPriceBucketString=function(e){var t="",r="",i="",n=0,a={low:t,med:r,high:i};try{n=parseFloat(e),n&&(a.low=n>u?u.toFixed(2):(Math.floor(2*e)/2).toFixed(2),a.med=n>p?p.toFixed(2):(Math.floor(10*e)/10).toFixed(2),a.high=n>l?l.toFixed(2):(Math.floor(100*e)/100).toFixed(2))}catch(s){this.logError("Exception parsing CPM :"+s.message)}return a},r.hasValidBidRequest=function(e,t,r){for(var i=0;i0);for(var t in e)if(h.call(e,t))return!1;return!0},r._each=function(e,t){if(!this.isEmpty(e)){if(this.isFn(e.forEach))return e.forEach(t,this);var r=0,i=e.length;if(i>0)for(;i>r;r++)t(e[r],r,e);else for(r in e)h.call(e,r)&&t.call(this,e[r],r)}},r.contains=function(e,t){if(this.isEmpty(e))return!1;if(this.isFn(e.indexOf))return-1!==e.indexOf(t);for(var r=e.length;r--;)if(e[r]===t)return!0;return!1},r._map=function(e,t){if(this.isEmpty(e))return[];if(this.isFn(e.map))return e.map(t);var r=[];return this._each(e,function(i,n){r.push(t(i,n,e))}),r};var I=function(e,t){return e.hasOwnProperty?e.hasOwnProperty(t):typeof e[t]!==UNDEFINED&&e.constructor.prototype[t]!==e[t]}},{"./constants.json":16}]},{},[18]); \ No newline at end of file diff --git a/gulpHelpers.js b/gulpHelpers.js new file mode 100644 index 00000000000..fbf1896dc7d --- /dev/null +++ b/gulpHelpers.js @@ -0,0 +1,16 @@ +module.exports = { + parseBrowserArgs: function (argv) { + return (argv.browsers) ? argv.browsers.split(',') : []; + }, + + toCapitalCase: function (str) { + return str.charAt(0).toUpperCase() + str.slice(1); + }, + + jsonifyHTML: function (str) { + console.log(arguments); + return str.replace(/\n/g, '') + .replace(/<\//g, '<\\/') + .replace(/\/>/g, '\\/>'); + } +}; diff --git a/gulpfile.js b/gulpfile.js index 1b8c43bfabf..02318079992 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,221 +1,144 @@ +'use strict'; + +var argv = require('yargs').argv; var gulp = require('gulp'); +var gutil = require('gulp-util'); +var connect = require('gulp-connect'); +var webpack = require('webpack-stream'); var uglify = require('gulp-uglify'); -var rename = require('gulp-rename'); var jshint = require('gulp-jshint'); +var clean = require('gulp-clean'); +var karma = require('gulp-karma'); +var opens = require('open'); +var webpackConfig = require('./webpack.conf.js'); +var helpers = require('./gulpHelpers'); +var del = require('del'); +var gulpJsdoc2md = require('gulp-jsdoc-to-markdown'); +var concat = require('gulp-concat'); var jscs = require('gulp-jscs'); var header = require('gulp-header'); -var del = require('del'); -var ecstatic = require('ecstatic'); -var gulpBrowserify = require('gulp-browserify'); -var gutil = require("gulp-util"); -var gulpJsdoc2md = require("gulp-jsdoc-to-markdown"); -var concat = require("gulp-concat"); var zip = require('gulp-zip'); -var mocha = require('gulp-mocha'); -var preprocessify = require('preprocessify'); -/** for automated unit testing */ -var browserSync = require("browser-sync"), - browserify = require("browserify"), - source = require("vinyl-source-stream"), - mochaPhantomJS = require("gulp-mocha-phantomjs"); - - -var releaseDir = './dist/'; -var csaSrcLocation = './src/prebid.js'; - -var pkg = require('./package.json'); +var CI_MODE = process.env.NODE_ENV === 'ci'; +var prebid = require('./package.json'); var dateString = 'Updated : ' + (new Date()).toISOString().substring(0, 10); -var packageNameVersion = pkg.name + '_' + pkg.version; - -var banner = '/* <%= pkg.name %> v<%= pkg.version %> \n' + dateString + ' */\n'; - -//basic/quick tests go here, to be run on every build -gulp.task('runBasicTests', function() { - return gulp.src('test/*', {read: false}) - .pipe(mocha()); -}); +var packageNameVersion = prebid.name + '_' + prebid.version; +var banner = '/* <%= prebid.name %> v<%= prebid.version %> \n' + dateString + ' */\n'; +// Tasks +gulp.task('default', ['clean', 'quality', 'webpack']); -gulp.task('jscs', function() { - return gulp.src(csaSrcLocation) - .pipe(jscs({ - 'configPath': 'styleRules.jscs.json' - })); +gulp.task('serve', ['clean', 'quality', 'devpack', 'webpack', 'watch', 'test']); +gulp.task('build', ['clean', 'quality', 'webpack', 'devpack', 'zip']); +gulp.task('clean', function () { + return gulp.src(['build'], { + read: false + }) + .pipe(clean()); }); -//run code quality checks here -gulp.task('jshint', function() { - - gulp.src(['./src/*.js', './src/adapters/*.js']) - .pipe(jshint({ - 'bitwise': 'true', - 'curly': 'true', - 'scripturl': 'false', - //'enforceall':'false', - //since we are never orriding Prootype (we control all this code) - //I am ok letting this be false in the name of faster code execution - 'forin': false, - 'eqeqeq': true, - //'es3':true, - //'es5':true, - 'freeze': true, - 'futurehostile': true, - 'latedef': true, - 'maxerr': '1000', - 'noarg': true, - 'nocomma': true, - 'nonbsp': true, - 'nonew': true, - 'notypeof': true, - //excessive parens are ok as long as they increase code readability - //and help to prevent errors, especially when helping to provide - //structure to long conditonal statements - 'singleGroups': false, - 'undef': true, - 'unused': true, - 'globals': { - 'require': false, - 'module' : true, - 'exports' : true, - 'pbjs' : true, - 'googletag': true, - 'ActiveXObject': true - }, - 'browser': true, - 'devel': true - - })) - .pipe(jshint.reporter('jshint-stylish')) - .pipe(jshint.reporter('fail')); - +gulp.task('devpack', function () { + webpackConfig.devtool = 'source-map'; + return gulp.src(['src/prebid.js']) + .pipe(webpack(webpackConfig)) + .pipe(gulp.dest('build/dev')) + .pipe(connect.reload()); }); -gulp.task('clean-dist', function(cb) { - del([releaseDir + '']); - cb(); +gulp.task('webpack', function () { -}); + // change output filename if argument --tag given + if (argv.tag && argv.tag.length) { + webpackConfig.output.filename = 'prebid.' + argv.tag + '.js'; + } -gulp.task('codeQuality', ['jshint', 'jscs'], function() {}); + webpackConfig.devtool = null; - -gulp.task('default', ['build'], function() {}); - -gulp.task('serve', ['build-dev', 'watch', 'browser-sync'], function () { - var port = 9999; - require('http').createServer(ecstatic({ - root: __dirname - })).listen(port); - console.log('Server started at http://localhost:' + port + '/'); + return gulp.src(['src/prebid.js']) + .pipe(webpack(webpackConfig)) + .pipe(uglify()) + .pipe(header(banner, { prebid: prebid })) + .pipe(gulp.dest('build/dist')) + .pipe(connect.reload()); }); -gulp.task('build-dev', ['jscs', 'clean-dist', 'browserify', 'unit-tests'], function () { - gulp.src(['src/prebid.js']) - .pipe(gulpBrowserify({ - debug: false - })) - .pipe(header(banner, { - pkg: pkg - })) - .pipe(gulp.dest(releaseDir)); - +//zip up for release +gulp.task('zip', ['jscs', 'clean', 'webpack'], function () { + return gulp.src(['build/dist/*', 'integrationExamples/gpt/*']) + .pipe(zip(packageNameVersion + '.zip')) + .pipe(gulp.dest('./')); }); -gulp.task('build-minify', ['clean-dist', 'quality'], function(cb){ - - gulp.src(['src/prebid.js']) - .pipe(gulpBrowserify({ - transform : preprocessify({ NODE_ENV: 'production'}) - } - - )) - //unminified version: - .pipe(header(banner, {pkg: pkg})) - .pipe(gulp.dest(releaseDir)) - //minified version - .pipe(uglify()) - .pipe(header(banner, {pkg: pkg})) - .pipe(rename({ - basename: 'prebid.min', - extname: '.js' - })) - .pipe(gulp.dest(releaseDir)); - //notify that release is ready - cb(); +// Karma Continuous Testing +// Pass your browsers by using --browsers=chrome,firefox,ie9 +// Run CI by passing --watch +gulp.task('test', function () { + var defaultBrowsers = CI_MODE ? ['PhantomJS'] : ['Chrome']; + var browserArgs = helpers.parseBrowserArgs(argv).map(helpers.toCapitalCase); + + return gulp.src('lookAtKarmaConfJS') + .pipe(karma({ + browsers: (browserArgs.length > 0) ? browserArgs : defaultBrowsers, + configFile: 'karma.conf.js', + action: (argv.watch) ? 'watch' : 'run' + })); }); -gulp.task('build', ['clean-dist', 'quality', 'build-minify', 'zip']); - -gulp.task('quality', ['jscs'], function(cb){ - cb(); +// Small task to load coverage reports in the browser +gulp.task('coverage', function (done) { + var coveragePort = 1999; + + connect.server({ + port: 1999, + root: 'build/coverage', + livereload: false + }); + opens('http://localhost:' + coveragePort + '/coverage/'); + done(); }); +// Watch Task with Live Reload gulp.task('watch', function () { - // gulp.watch(['src/**/*.js'], ['build-dev']); - gulp.watch(["test/tests.js", "src/*","test/spec/*.js"], ["browserify", "unit-tests"]); + + gulp.watch(['test/spec/**/*.js'], ['quality', 'webpack', 'devpack', 'test']); + gulp.watch(['integrationExamples/gpt/*.html'], ['test']); + gulp.watch(['src/**/*.js'], ['quality', 'webpack', 'devpack', 'test']); + connect.server({ + port: 9999, + root: './', + livereload: true + }); }); +gulp.task('quality', ['hint', 'jscs']); -gulp.task('clean-docs', function(){ - del(['docs']); +gulp.task('hint', function () { + return gulp.src('src/**/*.js') + .pipe(jshint('.jshintrc')) + .pipe(jshint.reporter('default')); }); -gulp.task("docs", ['clean-docs'], function() { - return gulp.src("src/prebid.js") - .pipe(concat("readme.md")) - .pipe(gulpJsdoc2md()) - .on("error", function(err){ - gutil.log("jsdoc2md failed:", err.message); - }) - .pipe(gulp.dest("docs")); -}); - -//zip up for release -gulp.task('zip', ['quality', 'clean-dist', 'build-minify'], function () { - return gulp.src(['dist/*', 'integrationExamples/gpt/*']) - .pipe(zip(packageNameVersion + '.zip')) - .pipe(gulp.dest('./')); +gulp.task('jscs', function () { + return gulp.src('src/**/*.js') + .pipe(jscs({ + configPath: '.jscsrc' + })) + .pipe(jscs.reporter()); }); -gulp.task("browser-sync", function () { - "use strict"; - browserSync({ - server: { - //serve tests and the root as base dirs - baseDir: ["./test/", "./"], - //make tests.html the index file - index: "automatedRunnner.html" - } - }); +gulp.task('clean-docs', function () { + del(['docs']); }); -//see http://fettblog.eu/gulp-browserify-multiple-bundles/ -gulp.task("browserify", function() { - "use strict"; - var files =[ - "./test/test.js", - "./test/spec/api_spec.js", - "./test/spec/utils_spec.js", - "./test/spec/adUnits_spec.js", - "./test/spec/aliasBidder_spec.js" - ]; - - return browserify({ entries: [files] }) - .bundle() - .on("error", function (err) { - console.log(err.toString()); - this.emit("end"); - }) - .pipe(source("tests-browserify.js")) - .pipe(gulp.dest("test/")) - .pipe(browserSync.reload({stream:true})); +gulp.task('docs', ['clean-docs'], function () { + return gulp.src('src/prebid.js') + .pipe(concat('readme.md')) + .pipe(gulpJsdoc2md()) + .on('error', function (err) { + gutil.log('jsdoc2md failed:', err.message); + }) + .pipe(gulp.dest('docs')); }); -gulp.task("unit-tests", function () { - "use strict"; - return gulp.src("./test/automatedRunnner.html") - .pipe(mochaPhantomJS()); -}); diff --git a/integrationExamples/gpt/gpt_aliasingBidder.html b/integrationExamples/gpt/gpt_aliasingBidder.html index 4d6c2833a46..979343a382e 100644 --- a/integrationExamples/gpt/gpt_aliasingBidder.html +++ b/integrationExamples/gpt/gpt_aliasingBidder.html @@ -37,7 +37,7 @@ (function() { var d = document, pbs = d.createElement("script"), pro = d.location.protocal; pbs.type = "text/javascript"; - pbs.src = '/dist/prebid.js'; + pbs.src = '/build/dist/prebid.js'; var target = document.getElementsByTagName("head")[0]; target.insertBefore(pbs, target.firstChild); })(); diff --git a/integrationExamples/gpt/pbjs_example_gpt.html b/integrationExamples/gpt/pbjs_example_gpt.html index 24196e5aa51..b433fceeab3 100644 --- a/integrationExamples/gpt/pbjs_example_gpt.html +++ b/integrationExamples/gpt/pbjs_example_gpt.html @@ -2,295 +2,365 @@ - - - - - console.log('Are all bids available? : ' + pbjs.allBidsAvailable()); - console.log('Targeting params:') - console.log(pbjs.getAdserverTargeting()); + @@ -298,33 +368,44 @@ -

Prebid.js Test

+

Prebid.js Test

-
- -
+
+ +
-
- -
+
+ +
'; - content += ''; - content += ''; - content += '' + - 'window.pm_pub_id = "%%PM_PUB_ID%%";' + - 'window.pm_optimize_adslots = [%%PM_OPTIMIZE_ADSLOTS%%];'; - content += ''; - - var map = {}; - map['PM_PUB_ID'] = _pm_pub_id; - map['PM_OPTIMIZE_ADSLOTS'] = _pm_optimize_adslots.map(function(adSlot) { - return "'" + adSlot + "'"; - }).join(','); - - content += ''; - content += ''; - content += 'window.parent.pbjs.handlePubmaticCallback({progKeyValueMap: progKeyValueMap, bidDetailsMap: bidDetailsMap})'; - content += ''; - content += ''; - content = utils.replaceTokenInString(content, map, '%%'); - - return content; - } - - pbjs.handlePubmaticCallback = function(response) { - var i; - var adUnit; - var adUnitInfo; - var bid; - var bidResponseMap = (response && response.bidDetailsMap) || {}; - var bidInfoMap = (response && response.progKeyValueMap) || {}; - var dimensions; - - for (i = 0; i < bids.length; i++) { - var adResponse; - bid = bids[i].params; - - adUnit = bidResponseMap[bid.adSlot] || {}; - - // adUnitInfo example: bidstatus=0;bid=0.0000;bidid=39620189@320x50;wdeal= - adUnitInfo = (bidInfoMap[bid.adSlot] || '').split(';').reduce(function(result, pair) { - var parts = pair.split('='); - result[parts[0]] = parts[1]; - return result; - }, {}); - - if (adUnitInfo.bidstatus === '1') { - dimensions = adUnitInfo.bidid.split('@')[1].split('x'); - adResponse = bidfactory.createBid(1); - adResponse.bidderCode = 'pubmatic'; - adResponse.adSlot = bid.adSlot; - adResponse.cpm = Number(adUnitInfo.bid); - adResponse.ad = unescape(adUnit.creative_tag); - adResponse.adUrl = unescape(adUnit.tracking_url); - adResponse.width = dimensions[0]; - adResponse.height = dimensions[1]; - adResponse.dealId = adUnitInfo.wdeal; - - bidmanager.addBidResponse(bids[i].placementCode, adResponse); - } else { - // Indicate an ad was not returned - adResponse = bidfactory.createBid(2); - adResponse.bidderCode = 'pubmatic'; - bidmanager.addBidResponse(bids[i].placementCode, adResponse); - } - } - }; - - return { - callBids: _callBids - }; + var bids; + var _pm_pub_id; + var _pm_optimize_adslots = []; + + function _callBids(params) { + bids = params.bids; + for (var i = 0; i < bids.length; i++) { + var bid = bids[i]; + bidmanager.pbCallbackMap['' + bid.params.adSlot] = bid; + _pm_pub_id = _pm_pub_id || bid.params.publisherId; + _pm_optimize_adslots.push(bid.params.adSlot); + } + + // Load pubmatic script in an iframe, because they call document.write + _getBids(); + } + + function _getBids() { + + // required variables for pubmatic pre-bid call + window.pm_pub_id = _pm_pub_id; + window.pm_optimize_adslots = _pm_optimize_adslots; + + //create the iframe + var iframe = utils.createInvisibleIframe(); + var elToAppend = document.getElementsByTagName('head')[0]; + + //insert the iframe into document + elToAppend.insertBefore(iframe, elToAppend.firstChild); + + var iframeDoc = utils.getIframeDocument(iframe); + iframeDoc.write(_createRequestContent()); + iframeDoc.close(); + } + + function _createRequestContent() { + var content = 'inDapIF=true; '; + content += ''; + content += ''; + content += '' + + 'window.pm_pub_id = "%%PM_PUB_ID%%";' + + 'window.pm_optimize_adslots = [%%PM_OPTIMIZE_ADSLOTS%%];'; + content += ''; + + var map = {}; + map.PM_PUB_ID = _pm_pub_id; + map.PM_OPTIMIZE_ADSLOTS = _pm_optimize_adslots.map(function (adSlot) { + return "'" + adSlot + "'"; + }).join(','); + + content += ''; + content += ''; + content += 'window.parent.pbjs.handlePubmaticCallback({progKeyValueMap: progKeyValueMap,' + + ' bidDetailsMap: bidDetailsMap})'; + content += ''; + content += ''; + content = utils.replaceTokenInString(content, map, '%%'); + + return content; + } + + pbjs.handlePubmaticCallback = function (response) { + var i; + var adUnit; + var adUnitInfo; + var bid; + var bidResponseMap = (response && response.bidDetailsMap) || {}; + var bidInfoMap = (response && response.progKeyValueMap) || {}; + var dimensions; + + for (i = 0; i < bids.length; i++) { + var adResponse; + bid = bids[i].params; + + adUnit = bidResponseMap[bid.adSlot] || {}; + + // adUnitInfo example: bidstatus=0;bid=0.0000;bidid=39620189@320x50;wdeal= + + // if using DFP GPT, the params string comes in the format: + // "bidstatus;1;bid;5.0000;bidid;hb_test@468x60;wdeal;" + // the code below detects and handles this. + if (bidInfoMap[bid.adSlot].indexOf('=') === -1) { + bidInfoMap[bid.adSlot] = bidInfoMap[bid.adSlot].replace(/([a-z]+);(.[^;]*)/ig, '$1=$2'); + } + + adUnitInfo = (bidInfoMap[bid.adSlot] || '').split(';').reduce(function (result, pair) { + var parts = pair.split('='); + result[parts[0]] = parts[1]; + return result; + }, {}); + + if (adUnitInfo.bidstatus === '1') { + dimensions = adUnitInfo.bidid.split('@')[1].split('x'); + adResponse = bidfactory.createBid(1); + adResponse.bidderCode = 'pubmatic'; + adResponse.adSlot = bid.adSlot; + adResponse.cpm = Number(adUnitInfo.bid); + adResponse.ad = unescape(adUnit.creative_tag); // jshint ignore:line + adResponse.ad += utils.createTrackPixelHtml(decodeURIComponent(adUnit.tracking_url)); + adResponse.width = dimensions[0]; + adResponse.height = dimensions[1]; + adResponse.dealId = adUnitInfo.wdeal; + + bidmanager.addBidResponse(bids[i].placementCode, adResponse); + } else { + // Indicate an ad was not returned + adResponse = bidfactory.createBid(2); + adResponse.bidderCode = 'pubmatic'; + bidmanager.addBidResponse(bids[i].placementCode, adResponse); + } + } + }; + + return { + callBids: _callBids + }; }; -module.exports = PubmaticAdapter; \ No newline at end of file +module.exports = PubmaticAdapter; diff --git a/src/adapters/pulsepoint.js b/src/adapters/pulsepoint.js index df4b4953969..318fbde501a 100644 --- a/src/adapters/pulsepoint.js +++ b/src/adapters/pulsepoint.js @@ -4,62 +4,62 @@ var adloader = require('../adloader.js'); var PulsePointAdapter = function PulsePointAdapter() { - var getJsStaticUrl = 'http://tag.contextweb.com/getjs.static.js'; - var bidUrl = 'http://tag.contextweb.com/bid'; - - function _callBids(params) { - if(typeof window.pp === 'undefined') { - adloader.loadScript(getJsStaticUrl, function() { bid(params); }); - } else { - bid(params); - } - } - - function bid(params) { - var bids = params.bids; - for (var i = 0; i < bids.length; i++) { - var bidRequest = bids[i]; - var callback = bidResponseCallback(bidRequest); - var ppBidRequest = new window.pp.Ad({ - cf : bidRequest.params.cf, - cp : bidRequest.params.cp, - ct : bidRequest.params.ct, - cn : 1, - ca : window.pp.requestActions.BID, - cu : bidUrl, - adUnitId: bidRequest.placementCode, - callback: callback - }); - ppBidRequest.display(); - } - } + var getJsStaticUrl = window.location.protocol + '//tag.contextweb.com/getjs.static.js'; + var bidUrl = window.location.protocol + '//bid.contextweb.com/header/tag'; - function bidResponseCallback(bid) { - return function(bidResponse) { - bidResponseAvailable(bid, bidResponse); - }; + function _callBids(params) { + if (typeof window.pp === 'undefined') { + adloader.loadScript(getJsStaticUrl, function () { bid(params); }); + } else { + bid(params); } + } - function bidResponseAvailable(bidRequest, bidResponse) { - if(bidResponse) { - var adSize = bidRequest.params.cf.toUpperCase().split('X'); - var bid = bidfactory.createBid(1); - bid.bidderCode = bidRequest.bidder; - bid.cpm = bidResponse.bidCpm; - bid.ad = bidResponse.html; - bid.width = adSize[0]; - bid.height = adSize[1]; - bidmanager.addBidResponse(bidRequest.placementCode, bid); - } else { - var passback = bidfactory.createBid(2); - passback.bidderCode = bidRequest.bidder; - bidmanager.addBidResponse(bidRequest.placementCode, passback); - } + function bid(params) { + var bids = params.bids; + for (var i = 0; i < bids.length; i++) { + var bidRequest = bids[i]; + var callback = bidResponseCallback(bidRequest); + var ppBidRequest = new window.pp.Ad({ + cf: bidRequest.params.cf, + cp: bidRequest.params.cp, + ct: bidRequest.params.ct, + cn: 1, + ca: window.pp.requestActions.BID, + cu: bidUrl, + adUnitId: bidRequest.placementCode, + callback: callback + }); + ppBidRequest.display(); } + } - return { - callBids: _callBids + function bidResponseCallback(bid) { + return function (bidResponse) { + bidResponseAvailable(bid, bidResponse); }; + } + + function bidResponseAvailable(bidRequest, bidResponse) { + if (bidResponse) { + var adSize = bidRequest.params.cf.toUpperCase().split('X'); + var bid = bidfactory.createBid(1); + bid.bidderCode = bidRequest.bidder; + bid.cpm = bidResponse.bidCpm; + bid.ad = bidResponse.html; + bid.width = adSize[0]; + bid.height = adSize[1]; + bidmanager.addBidResponse(bidRequest.placementCode, bid); + } else { + var passback = bidfactory.createBid(2); + passback.bidderCode = bidRequest.bidder; + bidmanager.addBidResponse(bidRequest.placementCode, passback); + } + } + + return { + callBids: _callBids + }; }; diff --git a/src/adapters/rubicon.js b/src/adapters/rubicon.js index 1462e53f266..8e9e5ec97d7 100644 --- a/src/adapters/rubicon.js +++ b/src/adapters/rubicon.js @@ -1,188 +1,299 @@ -//Factory for creating the bidderAdaptor -var CONSTANTS = require('../constants.json'); -var utils = require('../utils.js'); -var bidfactory = require('../bidfactory.js'); -var bidmanager = require('../bidmanager.js'); - +/** + * @file Rubicon (Rubicon) adapter + */ + +// jshint ignore:start +var utils = require('../utils'); +var bidmanager = require('../bidmanager'); +var bidfactory = require('../bidfactory'); +var adloader = require('../adloader'); + +/** + * @class RubiconAdapter + * Prebid adapter for Rubicon's header bidding client + */ var RubiconAdapter = function RubiconAdapter() { - // Map size dimensions to size 'ID' - var sizeMap = {}; - - function callBids(params) { - var bidArr = params.bids; - for (var i = 0; i < bidArr.length; i++) { - var bid = bidArr[i]; - //get the first size in the array - //TODO validation - var width = bid.sizes[0][0]; - var height = bid.sizes[0][1]; - var iframeContents = createRequestContent(bid, 'window.parent.pbjs.handleRubiconCallback', width, height); - var iframeId = loadIframeContent(iframeContents); - bid.iframeId = iframeId; - bidmanager.pbCallbackMap[getBidId(bid)] = bid; - } - - } - - // Build an ID that can be used to identify the response to the bid request. There - // may be an identifier we can send that gets sent back to us. - function getBidId(bid) { - return (bid.params ? [bid.params.rp_account, bid.params.rp_site, bid.params.rp_zonesize] : - [bid.account_id, bid.site_id, bid.zone_id, bid.size_id]).join('-'); - - } - - function loadIframeContent(content, callback) { - //create the iframe - var iframe = utils.createInvisibleIframe(); - var elToAppend = document.getElementsByTagName('head')[0]; - //insert the iframe into document - elToAppend.insertBefore(iframe, elToAppend.firstChild); - //todo make this more browser friendly - var iframeDoc = iframe.contentWindow.document; - iframeDoc.write(content); - iframeDoc.close(); - - return iframe.id; - - } - - function createRequestContent(bidOptions, callback, width, height) { - - // Map the size 'ID' to the dimensions - sizeMap[bidOptions.params.rp_zonesize.split('-')[1]] = { - width: width, - height: height - }; - - var content = 'inDapIF=true; '; - content += ''; - content += ''; - - - content += '' + - 'window.rp_account = "%%RP_ACCOUNT%%";' + - 'window.rp_site = "%%RP_SITE%%";' + - 'window.rp_zonesize = "%%RP_ZONESIZE%%";' + - 'window.rp_tracking = "%%RP_TRACKING%%";' + - 'window.rp_visitor = %%RP_VISITOR%%;' + - 'window.rp_width = %%RP_WIDTH%%;' + - 'window.rp_height = %%RP_HEIGHT%%;' + - 'window.rp_adtype = "jsonp";' + - 'window.rp_inventory = %%RP_INVENTORY%% ;' + - 'window.rp_floor=%%RP_FLOOR%%;' + - 'window.rp_fastlane = true;' + - 'window.rp_callback = ' + callback + ';'; - - - var map = {}; - map['RP_ACCOUNT'] = bidOptions.params.rp_account; - map['RP_SITE'] = bidOptions.params.rp_site; - map['RP_ZONESIZE'] = bidOptions.params.rp_zonesize; - map['RP_TRACKING'] = (bidOptions.params.rp_tracking) ? bidOptions.params.rp_tracking : ''; - map['RP_VISITOR'] = bidOptions.params.rp_visitor ? bidOptions.params.rp_visitor : '{}'; - map['RP_WIDTH'] = width; - map['RP_HEIGHT'] = height; - map['RP_INVENTORY'] = bidOptions.params.rp_inventory || '{}'; - map['RP_FLOOR'] = bidOptions.params.rp_floor ? bidOptions.params.rp_floor : '0.00'; - - content += ''; - content += ''; - content += ''; - - content = utils.replaceTokenInString(content, map, '%%'); - - //console.log(content); - - return content; - - } - - window.pbjs = window.pbjs || {que: []}; - window.pbjs.handleRubiconCallback = function(response) { - var placementCode = ''; - - var bid = {}; - if (response && response.status === 'ok') { - try { - var iframeId = ''; - var bidObj = bidmanager.getPlacementIdByCBIdentifer(getBidId(response)); - if (bidObj) { - placementCode = bidObj.placementCode; - bidObj.status = CONSTANTS.STATUS.GOOD; - iframeId = bidObj.iframeId; - } - - if (response.ads && response.ads[0] && response.ads[0].status === 'ok') { - bid = bidfactory.createBid(1); - - var rubiconAd = response.ads[0]; - var size = sizeMap[rubiconAd.size_id]; - var width = 0; - var height = 0; - - var iframeObj = window.frames[iframeId]; - var rubiconObj; - if(iframeObj.contentWindow){ - rubiconObj = iframeObj.contentWindow.RubiconAdServing - }else{ - rubiconObj = iframeObj.window.RubiconAdServing; - } - - if (rubiconObj && rubiconObj.AdSizes) { - /* should return - 1: { - dim: "468x60" - }, - */ - size = rubiconObj.AdSizes[rubiconAd.size_id]; - var sizeArray = size.dim.split('x'); - width = sizeArray[0]; - height = sizeArray[1]; - } - - bid.cpm = rubiconAd.cpm; - bid.ad = ''; - bid.ad_id = rubiconAd.ad_id; - bid.bidderCode = 'rubicon'; - bid.sizeId = rubiconAd.size_id; - bid.width = width; - bid.height = height; - - }else{ - bid = bidfactory.createBid(2); - bid.bidderCode = 'rubicon'; - var bidObj = bidmanager.getPlacementIdByCBIdentifer(getBidId(response)); - if (bidObj) { - placementCode = bidObj.placementCode; - } - } - - } catch (e) { - utils.logError('Error parsing rubicon response bid: ' + e.message); - } - - } else { - //set bid response code to 2 = no response or error - bid = bidfactory.createBid(2); - bid.bidderCode = 'rubicon'; - var bidObj = bidmanager.getPlacementIdByCBIdentifer(getBidId(response)); - if (bidObj) { - placementCode = bidObj.placementCode; - } - - } - - //add the bid response here - bidmanager.addBidResponse(placementCode, bid); - - }; - - return { - callBids: callBids - - }; - //end of Rubicon bid adaptor + var RUBICONTAG_URL = (window.location.protocol) + '//ads.rubiconproject.com/header/'; + var RUBICON_OK_STATUS = 'ok'; + var RUBICON_BIDDER_CODE = 'rubicon'; + var RUBICON_SIZE_MAP = { + '728x90': 2, + '160x600': 9, + '300x600': 10, + '300x250': 15, + '320x50': 43, + '300x1050': 54, + '970x250': 57 + }; + var RUBICON_INITIALIZED = 0; + + // the fastlane creative code + var RUBICON_CREATIVE_START = ''; + + // pre-initialize the rubicon object + // needs to be attached to the window + window.rubicontag = window.rubicontag || {}; + window.rubicontag.cmd = window.rubicontag.cmd || []; + + // timestamp for logging + var _bidStart = null; + var bidCount = 0; + + /** + * Create an error bid + * @param {String} placement - the adunit path + * @param {Object} response - the (error) response from fastlane + * @return {Bid} a bid, for prebid + */ + function _errorBid(response, ads) { + var bidResponse = bidfactory.createBid(2); + bidResponse.bidderCode = RUBICON_BIDDER_CODE; + + // use the raw ads as the 'error' + bidResponse.error = ads; + return bidResponse; + } + + /** + * Sort function for CPM + * @param {Object} adA + * @param {Object} adB + * @return {Float} sort order value + */ + function _adCpmSort(adA, adB) { + return (adB.cpm || 0.0) - (adA.cpm || 0.0); + } + + /** + * Produce the code to render a creative + * @param {String} elemId the element passed to rubicon; this is essentially the ad-id + * @param {Array} size array of width, height + * @return {String} creative + */ + function _creative(elemId, size) { + + // convert the size to a rubicon sizeId + var sizeId = RUBICON_SIZE_MAP[size.join('x')]; + + if (!sizeId) { + utils.logError( + 'fastlane: missing sizeId for size: ' + size.join('x') + ' could not render creative', + RUBICON_BIDDER_CODE, RUBICON_SIZE_MAP); + return ''; + } + + return RUBICON_CREATIVE_START + elemId + '", "' + sizeId + RUBICON_CREATIVE_END; + } + + /** + * Create a (successful) bid for a unit, + * based on the given response + * @param {String} placement placement code/unit path + * @param {Object} response the response from rubicon + * @return {Bid} a bid objectj + */ + function _makeBid(response, ads) { + + // if there are multiple ads, sort by CPM + ads = ads.sort(_adCpmSort); + + var bidResponse = bidfactory.createBid(1); + var ad = ads[0]; + var size = ad.dimensions; + + if (!size) { + // this really shouldn't happen + utils.logError('no dimensions given', RUBICON_BIDDER_CODE, ad); + return _errorBid(response, ads); + } + + bidResponse.bidderCode = RUBICON_BIDDER_CODE; + bidResponse.cpm = ad.cpm; + + // the element id is what the iframe will use to render + // itself using the rubicontag.renderCreative API + bidResponse.ad = _creative(response.getElementId(), size); + bidResponse.width = size[0]; + bidResponse.height = size[1]; + return bidResponse; + } + + /** + * Add a success/error bid based + * on the response from rubicon + * @param {Object} response -- AJAX response from fastlane + */ + function _addBid(response, ads) { + // get the bid for the placement code + var bid; + if (!ads || ads.length === 0) { + bid = _errorBid(response, ads); + } else { + bid = _makeBid(response, ads); + } + + bidmanager.addBidResponse(response.getElementId(), bid); + } + + /** + * Helper to queue functions on rubicontag + * ready/available + * @param {Function} callback + */ + function _rready(callback) { + window.rubicontag.cmd.push(callback); + } + + /** + * download the rubicontag sdk + * @param {Object} options + * @param {String} options.accountId + * @param {Function} callback + */ + function _initSDK(options, done) { + if (RUBICON_INITIALIZED) { + return; + } + + RUBICON_INITIALIZED = 1; + + var accountId = options.accountId; + var scripttUrl = RUBICONTAG_URL + accountId + '.js'; + + adloader.loadScript(scripttUrl, done); + } + + /** + * map the sizes in `bid.sizes` to Rubicon specific keys + * @param {object} array of bids + * @return {[type]} [description] + */ + function _mapSizes(bids) { + utils._each(bids, function (bid) { + if (bid.params.sizes) { + return; + } + + //return array like ['300x250', '728x90'] + var parsedSizes = utils.parseSizesInput(bid.sizes); + + //iterate the bid.sizes array to lookup codes + var tempSize = []; + for (var i = 0; i < parsedSizes.length; i++) { + var rubiconKey = RUBICON_SIZE_MAP[parsedSizes[i]]; + if (rubiconKey) { + tempSize.push(rubiconKey); + } + } + + bid.params.sizes = tempSize; + }); + } + + /** + * Define the slot using the rubicontag.defineSlot API + * @param {Object} Bidrequest + * @returns {RubiconSlot} Instance of RubiconSlot + */ + function _defineSlot(bid) { + var userId = bid.params.userId; + var position = bid.params.position; + var visitor = bid.params.visitor || []; + var keywords = bid.params.keywords || []; + var inventory = bid.params.inventory || []; + var slot = window.rubicontag.defineSlot({ + siteId: bid.params.siteId, + zoneId: bid.params.zoneId, + sizes: bid.params.sizes, + id: bid.placementCode + }); + + slot.clearTargeting(); + + if (userId) { + window.rubicontag.setUserKey(userId); + } + + if (position) { + slot.setPosition(position); + } + + for (var key in visitor) { + slot.addFPV(key, visitor[key]); + } + + for (var key in inventory) { + slot.addFPI(key, inventory[key]); + } + + slot.addKW(keywords); + + return slot; + } + + /** + * Handle the bids received (from rubicon) + * @param {array} slots + */ + function _bidsReady(slots) { + // NOTE: we don't really need to do anything, + // because right now we're shimming XMLHttpRequest.open, + // but in the future we'll get data from rubicontag here + utils.logMessage('Rubicon Project bidding complete: ' + ((new Date).getTime() - _bidStart)); + + utils._each(slots, function (slot) { + _addBid(slot, slot.getRawResponses()); + }); + } + + /** + * Request the specified bids from + * Rubicon + * @param {Object} params the bidder-level params (from prebid) + * @param {Array} params.bids the bids requested + */ + function _callBids(params) { + + // start the timer; want to measure from + // even just loading the SDK + _bidStart = (new Date).getTime(); + + _mapSizes(params.bids); + + if (utils.isEmpty(params.bids)) { + return; + } + + // on the first bid, set up the SDK + if (!RUBICON_INITIALIZED) { + _initSDK(params.bids[0].params); + } + + _rready(function () { + var slots = []; + var bids = params.bids; + + for (var key in bids) { + slots.push(_defineSlot(bids[key])); + } + + var parameters = { slots: slots }; + var callback = function () { + _bidsReady(slots); + }; + + window.rubicontag.run(callback, parameters); + }); + } + + return { + /** + * @public callBids + * the interface to Prebid + */ + callBids: _callBids + }; }; module.exports = RubiconAdapter; diff --git a/src/adapters/sovrn.js b/src/adapters/sovrn.js index bafee8a2bc3..0d616d54c3c 100644 --- a/src/adapters/sovrn.js +++ b/src/adapters/sovrn.js @@ -4,180 +4,171 @@ var bidfactory = require('../bidfactory.js'); var bidmanager = require('../bidmanager.js'); var adloader = require('../adloader'); -var defaultPlacementForBadBid = ''; +var allPlacementCodes; /** * Adapter for requesting bids from Sovrn */ var SovrnAdapter = function SovrnAdapter() { - var sovrnUrl = 'ap.lijit.com/rtb/bid'; - - function _callBids(params) { - var sovrnBids = params.bids || []; - // De-dupe by tagid then issue single bid request for all bids - _requestBids(_getUniqueTagids(sovrnBids)); - } - - // filter bids to de-dupe them? - function _getUniqueTagids(bids) { - var key; - var map = {}; - var Tagids = []; - bids.forEach(function(bid) { - map[utils.getBidIdParamater('tagid', bid.params)] = bid; - }); - for (key in map) { - if (map.hasOwnProperty(key)) { - Tagids.push(map[key]); - } - } - return Tagids; - } - - function _requestBids(bidReqs) { - // build bid request object - var domain = window.location.host; - var page = window.location.pathname + location.search + location.hash; - - var sovrnImps = []; - //assign the first adUnit (placement) for bad bids; - defaultPlacementForBadBid = bidReqs[0].placementCode; - - //build impression array for sovrn - utils._each(bidReqs, function(bid) - { - var tagId = utils.getBidIdParamater('tagid', bid.params); - var bidFloor = utils.getBidIdParamater('bidfloor', bid.params); - var adW=0,adH=0; - - //sovrn supports only one size per tagid, so we just take the first size if there are more - //if we are a 2 item array of 2 numbers, we must be a SingleSize array - var sizeArrayLength = bid.sizes.length; - if (sizeArrayLength === 2 && typeof bid.sizes[0] === 'number' && typeof bid.sizes[1] === 'number') { - adW=bid.sizes[0]; - adH=bid.sizes[1]; - } - else - { - adW=bid.sizes[0][0]; - adH=bid.sizes[0][1]; - } - imp = - { - id: utils.getUniqueIdentifierStr(), - banner: { - w: adW, - h: adH - }, - tagid: tagId, - bidfloor: bidFloor - }; - sovrnImps.push(imp); - bidmanager.pbCallbackMap[imp.id] = bid; - }); - - // build bid request with impressions - var sovrnBidReq = { - id: utils.getUniqueIdentifierStr(), - imp: sovrnImps, - site:{ - domain: domain, - page: page - } - }; - - var scriptUrl = '//'+sovrnUrl+'?callback=window.pbjs.sovrnResponse' + - '&br=' + encodeURIComponent(JSON.stringify(sovrnBidReq)); - adloader.loadScript(scriptUrl, null); - } - - //expose the callback to the global object: - pbjs.sovrnResponse = function(sovrnResponseObj) { - var bid = {}; - // valid object? - if (sovrnResponseObj && sovrnResponseObj.id) { - // valid object w/ bid responses? - if (sovrnResponseObj.seatbid && sovrnResponseObj.seatbid.length !==0 && sovrnResponseObj.seatbid[0].bid && sovrnResponseObj.seatbid[0].bid.length !== 0) { - - sovrnResponseObj.seatbid[0].bid.forEach(function(sovrnBid){ - - var responseCPM; - var placementCode = ''; - var id = sovrnBid.impid; - - // try to fetch the bid request we sent Sovrn - var bidObj = bidmanager.getPlacementIdByCBIdentifer(id); - if (bidObj){ - placementCode = bidObj.placementCode; - bidObj.status = CONSTANTS.STATUS.GOOD; - - //place ad response on bidmanager._adResponsesByBidderId - responseCPM = parseFloat(sovrnBid.price); - - if(responseCPM !== 0) { - sovrnBid.placementCode = placementCode; - sovrnBid.size = bidObj.sizes; - var responseAd = sovrnBid.adm; - - // build impression url from response - var responseNurl = ''; - - //store bid response - //bid status is good (indicating 1) - bid = bidfactory.createBid(1); - bid.creative_id = sovrnBid.Id; - bid.bidderCode = 'sovrn'; - bid.cpm = responseCPM; - - //set ad content + impression url - // sovrn returns ' + - ''; - }, - /** - * Bid response builder. - * - * @param {Object} slotCriteria - Yieldbot bid criteria - * @private - */ - buildBid: function(slotCriteria) { - var bid = {}; - - if (slotCriteria && slotCriteria.ybot_ad && slotCriteria.ybot_ad !== 'n') { - - bid = bidfactory.createBid(ybotlib.BID_STATUS.AVAILABLE); - - bid.cpm = parseInt(slotCriteria.ybot_cpm) / 100.0 || 0; // Yieldbot CPM bids are in cents - - var szArr = slotCriteria.ybot_size ? slotCriteria.ybot_size.split('x') : [0,0], - slot = slotCriteria.ybot_slot || '', - sizeStr = slotCriteria.ybot_size || ''; // Creative template needs the dimensions string - - bid.width = szArr[0] || 0; - bid.height = szArr[1] || 0; - - bid.ad = ybotlib.buildCreative(slot, sizeStr); - - // Add Yieldbot parameters to allow publisher bidderSettings.yieldbot specific targeting - for (var k in slotCriteria) { - bid[k] = slotCriteria[k]; - } - - } else { - bid = bidfactory.createBid(ybotlib.BID_STATUS.EMPTY); - } - - bid.bidderCode = 'yieldbot'; - return bid; - }, - /** - * Yieldbot implementation of {@link module:adaptermanger.callBids} - * @param {Object} params - Adapter bid configuration object - * @private - */ - callBids: function(params) { - - var bids = params.bids || [], - ybotq = window.ybotq || []; - - ybotlib.pageLevelOption = false; - - ybotq.push(function () { - var yieldbot = window.yieldbot; - - utils._each(bids, function(v) { - var bid = v, - psn = bid.params && bid.params.psn || 'ERROR_DEFINE_YB_PSN', - slot = bid.params && bid.params.slot || 'ERROR_DEFINE_YB_SLOT'; - - yieldbot.pub(psn); - yieldbot.defineSlot(slot, {sizes: bid.sizes || []}); - - var cbId = utils.getUniqueIdentifierStr(); - bidmanager.pbCallbackMap[cbId] = bid; - ybotlib.definedSlots.push(cbId); - }); - - yieldbot.enableAsync(); - yieldbot.go(); - }); - - ybotq.push(function () { - ybotlib.handleUpdateState(); - }); - - adloader.loadScript('//cdn.yldbt.com/js/yieldbot.intent.js'); - }, - /** - * Yieldbot bid request callback handler. - * - * @see {@link YieldbotAdapter~_callBids} - * @private - */ - handleUpdateState: function() { - var yieldbot = window.yieldbot; - - utils._each(ybotlib.definedSlots, function(v) { - var slot, - criteria, - placementCode, - adapterConfig; - - adapterConfig = bidmanager.getPlacementIdByCBIdentifer(v) || {}; - slot = adapterConfig.params.slot || ''; - criteria = yieldbot.getSlotCriteria(slot); - - placementCode = adapterConfig.placementCode || 'ERROR_YB_NO_PLACEMENT'; - var bid = ybotlib.buildBid(criteria); - - bidmanager.addBidResponse(placementCode, bid); - - }); + window.ybotq = window.ybotq || []; + + var ybotlib = { + BID_STATUS: { + PENDING: 0, + AVAILABLE: 1, + EMPTY: 2 + }, + definedSlots: [], + pageLevelOption: false, + /** + * Builds the Yieldbot creative tag. + * + * @param {String} slot - The slot name to bid for + * @param {String} size - The dimenstions of the slot + * @private + */ + buildCreative: function (slot, size) { + return '' + + ''; + }, + /** + * Bid response builder. + * + * @param {Object} slotCriteria - Yieldbot bid criteria + * @private + */ + buildBid: function (slotCriteria) { + var bid = {}; + + if (slotCriteria && slotCriteria.ybot_ad && slotCriteria.ybot_ad !== 'n') { + + bid = bidfactory.createBid(ybotlib.BID_STATUS.AVAILABLE); + + bid.cpm = parseInt(slotCriteria.ybot_cpm) / 100.0 || 0; // Yieldbot CPM bids are in cents + + var szArr = slotCriteria.ybot_size ? slotCriteria.ybot_size.split('x') : [0, 0]; + var slot = slotCriteria.ybot_slot || ''; + var sizeStr = slotCriteria.ybot_size || ''; // Creative template needs the dimensions string + + bid.width = szArr[0] || 0; + bid.height = szArr[1] || 0; + + bid.ad = ybotlib.buildCreative(slot, sizeStr); + + // Add Yieldbot parameters to allow publisher bidderSettings.yieldbot specific targeting + for (var k in slotCriteria) { + bid[k] = slotCriteria[k]; } + + } else { + bid = bidfactory.createBid(ybotlib.BID_STATUS.EMPTY); + } + + bid.bidderCode = 'yieldbot'; + return bid; + }, + /** + * Yieldbot implementation of {@link module:adaptermanger.callBids} + * @param {Object} params - Adapter bid configuration object + * @private + */ + callBids: function (params) { + + var bids = params.bids || []; + var ybotq = window.ybotq || []; + + ybotlib.pageLevelOption = false; + + ybotq.push(function () { + var yieldbot = window.yieldbot; + + utils._each(bids, function (v) { + var bid = v; + var psn = bid.params && bid.params.psn || 'ERROR_DEFINE_YB_PSN'; + var slot = bid.params && bid.params.slot || 'ERROR_DEFINE_YB_SLOT'; + + yieldbot.pub(psn); + yieldbot.defineSlot(slot, { sizes: bid.sizes || [] }); + + var cbId = utils.getUniqueIdentifierStr(); + bidmanager.pbCallbackMap[cbId] = bid; + ybotlib.definedSlots.push(cbId); + }); + + yieldbot.enableAsync(); + yieldbot.go(); + }); + + ybotq.push(function () { + ybotlib.handleUpdateState(); + }); + + adloader.loadScript('//cdn.yldbt.com/js/yieldbot.intent.js'); + }, + /** + * Yieldbot bid request callback handler. + * + * @see {@link YieldbotAdapter~_callBids} + * @private + */ + handleUpdateState: function () { + var yieldbot = window.yieldbot; + + utils._each(ybotlib.definedSlots, function (v) { + var slot; + var criteria; + var placementCode; + var adapterConfig; + + adapterConfig = bidmanager.getPlacementIdByCBIdentifer(v) || {}; + slot = adapterConfig.params.slot || ''; + criteria = yieldbot.getSlotCriteria(slot); + + placementCode = adapterConfig.placementCode || 'ERROR_YB_NO_PLACEMENT'; + var bid = ybotlib.buildBid(criteria); + + bidmanager.addBidResponse(placementCode, bid); + + }); } - return { - callBids: ybotlib.callBids - }; + }; + return { + callBids: ybotlib.callBids + }; }; module.exports = YieldbotAdapter; diff --git a/src/adloader.js b/src/adloader.js index 746211a50c0..509f222c5c3 100644 --- a/src/adloader.js +++ b/src/adloader.js @@ -1,60 +1,97 @@ var utils = require('./utils'); +let _requestCache = {}; + //add a script tag to the page, used to add /jpt call to page -exports.loadScript = function(tagSrc, callback) { - if(!tagSrc){ - utils.logError('Error attempting to request empty URL', 'adloader.js:loadScript'); - return; - } - var jptScript = document.createElement('script'); - jptScript.type = 'text/javascript'; - jptScript.async = true; - - - // Execute a callback if necessary - if (callback && typeof callback === "function") { - if (jptScript.readyState) { - jptScript.onreadystatechange = function() { - if (jptScript.readyState == "loaded" || jptScript.readyState == "complete") { - jptScript.onreadystatechange = null; - callback(); - } - }; - } else { - jptScript.onload = function() { - callback(); - }; - } - } - - //call function to build the JPT call - jptScript.src = tagSrc; - - //add the new script tag to the page - var elToAppend = document.getElementsByTagName('head'); - elToAppend = elToAppend.length ? elToAppend : document.getElementsByTagName('body'); - if (elToAppend.length) { - elToAppend = elToAppend[0]; - elToAppend.insertBefore(jptScript, elToAppend.firstChild); - } +exports.loadScript = function (tagSrc, callback, cacheRequest) { + if (!tagSrc) { + utils.logError('Error attempting to request empty URL', 'adloader.js:loadScript'); + return; + } + + if (cacheRequest) { + if (_requestCache[tagSrc]) { + if (_requestCache[tagSrc].loaded) { + //invokeCallbacks immediately + callback(); + } else { + //queue the callback + _requestCache[tagSrc].callbacks.push(callback); + } + } else { + _requestCache[tagSrc] = { + loaded:false, + callbacks:[] + }; + _requestCache[tagSrc].callbacks.push(callback); + requestResource(tagSrc, function() { + _requestCache[tagSrc].loaded = true; + try { + for (let i = 0; i < _requestCache[tagSrc].callbacks.length; i++) { + _requestCache[tagSrc].callbacks[i](); + } + } + catch (e) { + utils.logError('Error executing callback', 'adloader.js:loadScript', e); + } + }); + } + } + + //trigger one time request + else { + requestResource(tagSrc, callback); + } + }; -exports.trackPixel = function(pixelUrl) { - //track a impbus tracking pixel +function requestResource(tagSrc, callback) { + var jptScript = document.createElement('script'); + jptScript.type = 'text/javascript'; + jptScript.async = true; + + // Execute a callback if necessary + if (callback && typeof callback === 'function') { + if (jptScript.readyState) { + jptScript.onreadystatechange = function () { + if (jptScript.readyState === 'loaded' || jptScript.readyState === 'complete') { + jptScript.onreadystatechange = null; + callback(); + } + }; + } else { + jptScript.onload = function () { + callback(); + }; + } + } - //TODO: Decide of tracking via AJAX is sufficent, or do we need to - //run impression trackers via page pixels? - try { + jptScript.src = tagSrc; - //add a cachebuster so we don't end up dropping any impressions - pixelUrl += '&rnd=' + Math.random(); + //add the new script tag to the page + var elToAppend = document.getElementsByTagName('head'); + elToAppend = elToAppend.length ? elToAppend : document.getElementsByTagName('body'); + if (elToAppend.length) { + elToAppend = elToAppend[0]; + elToAppend.insertBefore(jptScript, elToAppend.firstChild); + } +} - if (pixelUrl) { - var img = document.createElement('img'); - img.src = pixelUrl; - } +//track a impbus tracking pixel +//TODO: Decide if tracking via AJAX is sufficent, or do we need to +//run impression trackers via page pixels? +exports.trackPixel = function (pixelUrl) { + let delimiter; + let trackingPixel; + if (!pixelUrl || typeof (pixelUrl) !== 'string') { + utils.logMessage('Missing or invalid pixelUrl.'); + return; + } - } catch (e) { + delimiter = pixelUrl.indexOf('?') > 0 ? '&' : '?'; - } -}; \ No newline at end of file + //add a cachebuster so we don't end up dropping any impressions + trackingPixel = pixelUrl + delimiter + 'rnd=' + Math.floor(Math.random() * 1E7); + (new Image()).src = trackingPixel; + return trackingPixel; +}; diff --git a/src/bidfactory.js b/src/bidfactory.js index 364c38d28e8..639cbc966b6 100644 --- a/src/bidfactory.js +++ b/src/bidfactory.js @@ -1,59 +1,54 @@ var utils = require('./utils.js'); /** - Required paramaters - bidderCode, - height, - width, - statusCode - Optional paramaters - adId, - cpm, - ad, - adUrl, - dealId, - priceKeyString; + Required paramaters + bidderCode, + height, + width, + statusCode + Optional paramaters + adId, + cpm, + ad, + adUrl, + dealId, + priceKeyString; */ function Bid(statusCode) { - var _bidId = utils.getUniqueIdentifierStr(), - _statusCode = statusCode || 0; - this.bidderCode = ''; - this.width = 0; - this.height = 0; - this.statusMessage = _getStatus(); - this.adId = _bidId; + var _bidId = utils.getUniqueIdentifierStr(); + var _statusCode = statusCode || 0; - function _getStatus() { - switch (_statusCode) { - case 0: - return 'Pending'; - case 1: - return 'Bid available'; - case 2: - return 'Bid returned empty or error response'; - case 3: - return 'Bid timed out'; - } - } - this.getStatusCode = function() { - return _statusCode; - }; + this.bidderCode = ''; + this.width = 0; + this.height = 0; + this.statusMessage = _getStatus(); + this.adId = _bidId; - function _setStatusCode(status) { - this._statusCode = status; - //update status msg - this._statusMessage = this._getStatus(); - } - //returns the size of the bid creative. Concatenation of width and height by ‘x’. - this.getSize = function() { - return this.width + 'x' + this.height; - }; + function _getStatus() { + switch (_statusCode) { + case 0: + return 'Pending'; + case 1: + return 'Bid available'; + case 2: + return 'Bid returned empty or error response'; + case 3: + return 'Bid timed out'; + } + } + + this.getStatusCode = function () { + return _statusCode; + }; + + //returns the size of the bid creative. Concatenation of width and height by ‘x’. + this.getSize = function () { + return this.width + 'x' + this.height; + }; } // Bid factory function. -exports.createBid = function(statusCde) { - return new Bid(statusCde); +exports.createBid = function (statusCde) { + return new Bid(statusCde); }; - -//module.exports = Bid; \ No newline at end of file diff --git a/src/bidmanager.js b/src/bidmanager.js index 09ddb7604c0..525021c29c2 100644 --- a/src/bidmanager.js +++ b/src/bidmanager.js @@ -1,6 +1,5 @@ var CONSTANTS = require('./constants.json'); var utils = require('./utils.js'); -var adaptermanager = require('./adaptermanager'); var events = require('./events'); var objectType_function = 'function'; @@ -33,359 +32,347 @@ var _callbackExecuted = false; var defaultBidderSettingsMap = {}; var bidderStartTimes = {}; -exports.getPlacementIdByCBIdentifer = function(id) { - return pbCallbackMap[id]; +exports.getPlacementIdByCBIdentifer = function (id) { + return pbCallbackMap[id]; }; - -exports.getBidResponseByAdUnit = function(adUnitCode) { - return pbBidResponseByPlacement; +exports.getBidResponseByAdUnit = function () { + return pbBidResponseByPlacement; }; +exports.clearAllBidResponses = function () { + _allBidsAvailable = false; + _callbackExecuted = false; + + //init bid response received count + initbidResponseReceivedCount(); -exports.clearAllBidResponses = function(adUnitCode) { - _allBidsAvailable = false; - _callbackExecuted = false; + //init expected bids count + initExpectedBidsCount(); - //init bid response received count - initbidResponseReceivedCount(); - //init expected bids count - initExpectedBidsCount(); - //clear the callback handler flag - externalCallbackArr.called = false; + //clear the callback handler flag + externalCallbackArr.called = false; - for (var prop in this.pbBidResponseByPlacement) { - delete this.pbBidResponseByPlacement[prop]; - } + for (var prop in this.pbBidResponseByPlacement) { + delete this.pbBidResponseByPlacement[prop]; + } }; /** * Returns a list of bidders that we haven't received a response yet * @return {array} [description] */ -exports.getTimedOutBidders = function(){ - var bidderArr = []; - utils._each(bidResponseReceivedCount,function(count,bidderCode){ - if(count === 0){ - bidderArr.push(bidderCode); - } - }); - - return bidderArr; +exports.getTimedOutBidders = function () { + return utils._map(bidResponseReceivedCount, function (count, bidderCode) { + if (count === 0) { + return bidderCode; + } + }); }; -function initbidResponseReceivedCount(){ - - bidResponseReceivedCount = {}; - - for(var i=0; i https://developers.google.com/analytics/devguides/collection/ios/v3/limits-quotas?hl=en - _eventCount = 0, - //limit data sent by leaving this false - _enableDistribution = false, - _timedOutBidders = []; - +var _disibleInteraction = { nonInteraction: true }; +var _analyticsQueue = []; +var _gaGlobal = null; +var _enableCheck = true; +var _category = 'Prebid.js Bids'; +var _eventCount = 0; +var _enableDistribution = false; +var _timedOutBidders = []; /** * This will enable sending data to google analytics. Only call once, or duplicate data will be sent! * @param {object} gaOptions to set distribution and GA global (if renamed); * @return {[type]} [description] */ -exports.enableAnalytics = function(gaOptions) { - if(typeof gaOptions.global !== 'undefined'){ - _gaGlobal = gaOptions.global; - } - else{ - //default global is window.ga - _gaGlobal = 'ga'; - } - if(typeof gaOptions.enableDistribution !== 'undefined'){ - _enableDistribution = gaOptions.enableDistribution; - } - - var bid = null; - - //first send all events fired before enableAnalytics called - - var existingEvents = events.getEvents(); - utils._each(existingEvents, function(eventObj) { - var args = eventObj.args; - if (!eventObj) { - return; - } - if (eventObj.eventType === BID_REQUESTED) { - //bid is 1st args - bid = args[0]; - sendBidRequestToGa(bid); - } else if (eventObj.eventType === BID_RESPONSE) { - //bid is 2nd args - bid = args[1]; - sendBidResponseToGa(bid); - - } else if (eventObj.eventType === BID_TIMEOUT) { - var bidderArray = args[0]; - _timedOutBidders = bidderArray; - //todo disable event listeners - - } else if (eventObj.eventType === BID_WON) { - bid = args[0]; - sendBidWonToGa(bid); - } - }); - - //Next register event listeners to send data immediately - - //bidRequests - events.on(BID_REQUESTED, function(bidRequestObj) { - sendBidRequestToGa(bidRequestObj); - }); - - //bidResponses - events.on(BID_RESPONSE, function(adunit, bid) { - sendBidResponseToGa(bid); - sendBidTimeouts(bid); - }); - - //bidTimeouts - events.on(BID_TIMEOUT, function(bidderArray) { - _timedOutBidders = bidderArray; - }); - - //wins - events.on(BID_WON, function(bid) { - sendBidWonToGa(bid); - }); +exports.enableAnalytics = function (gaOptions) { + if (typeof gaOptions.global !== 'undefined') { + _gaGlobal = gaOptions.global; + } else { + //default global is window.ga + _gaGlobal = 'ga'; + } + + if (typeof gaOptions.enableDistribution !== 'undefined') { + _enableDistribution = gaOptions.enableDistribution; + } + + var bid = null; + + //first send all events fired before enableAnalytics called + + var existingEvents = events.getEvents(); + utils._each(existingEvents, function (eventObj) { + var args = eventObj.args; + if (!eventObj) { + return; + } + + if (eventObj.eventType === BID_REQUESTED) { + bid = args; + sendBidRequestToGa(bid); + } else if (eventObj.eventType === BID_RESPONSE) { + //bid is 2nd args + bid = args; + sendBidResponseToGa(bid); + + } else if (eventObj.eventType === BID_TIMEOUT) { + _timedOutBidders = args.bidderCode; + } else if (eventObj.eventType === BID_WON) { + bid = args; + sendBidWonToGa(bid); + } + }); + + //Next register event listeners to send data immediately + + //bidRequests + events.on(BID_REQUESTED, function (bidRequestObj) { + sendBidRequestToGa(bidRequestObj); + }); + + //bidResponses + events.on(BID_RESPONSE, function (adunit, bid) { + sendBidResponseToGa(bid); + sendBidTimeouts(bid); + }); + + //bidTimeouts + events.on(BID_TIMEOUT, function (bidderArray) { + _timedOutBidders = bidderArray; + }); + + //wins + events.on(BID_WON, function (bid) { + sendBidWonToGa(bid); + }); }; /** * Check if gaGlobal or window.ga is defined on page. If defined execute all the GA commands */ function checkAnalytics() { - if (_enableCheck && typeof window[_gaGlobal] === 'function' ) { - - for (var i = 0; i < _analyticsQueue.length; i++) { - _analyticsQueue[i].call(); - } - //override push to execute the command immediately from now on - _analyticsQueue.push = function(fn) { - fn.call(); - }; - //turn check into NOOP - _enableCheck = false; - } - utils.logMessage('event count sent to GA: ' + _eventCount); -} + if (_enableCheck && typeof window[_gaGlobal] === 'function') { + + for (var i = 0; i < _analyticsQueue.length; i++) { + _analyticsQueue[i].call(); + } + + //override push to execute the command immediately from now on + _analyticsQueue.push = function (fn) { + fn.call(); + }; + //turn check into NOOP + _enableCheck = false; + } + + utils.logMessage('event count sent to GA: ' + _eventCount); +} function convertToCents(dollars) { - if (dollars) { - return Math.floor(dollars * 100); - } - return 0; + if (dollars) { + return Math.floor(dollars * 100); + } + + return 0; } function getLoadTimeDistribution(time) { - var distribution; - if (time >= 0 && time < 200) { - distribution = '0-200ms'; - } else if (time >= 200 && time < 300) { - distribution = '200-300ms'; - } else if (time >= 300 && time < 400) { - distribution = '300-400ms'; - } else if (time >= 400 && time < 500) { - distribution = '400-500ms'; - } else if (time >= 500 && time < 600) { - distribution = '500-600ms'; - } else if (time >= 600 && time < 800) { - distribution = '600-800ms'; - } else if (time >= 800 && time < 1000) { - distribution = '800-1000ms'; - } else if (time >= 1000 && time < 1200) { - distribution = '1000-1200ms'; - } else if (time >= 1200 && time < 1500) { - distribution = '1200-1500ms'; - } else if (time >= 1500 && time < 2000) { - distribution = '1500-2000ms'; - } else if (time >= 2000) { - distribution = '2000ms above'; - } - - return distribution; + var distribution; + if (time >= 0 && time < 200) { + distribution = '0-200ms'; + } else if (time >= 200 && time < 300) { + distribution = '200-300ms'; + } else if (time >= 300 && time < 400) { + distribution = '300-400ms'; + } else if (time >= 400 && time < 500) { + distribution = '400-500ms'; + } else if (time >= 500 && time < 600) { + distribution = '500-600ms'; + } else if (time >= 600 && time < 800) { + distribution = '600-800ms'; + } else if (time >= 800 && time < 1000) { + distribution = '800-1000ms'; + } else if (time >= 1000 && time < 1200) { + distribution = '1000-1200ms'; + } else if (time >= 1200 && time < 1500) { + distribution = '1200-1500ms'; + } else if (time >= 1500 && time < 2000) { + distribution = '1500-2000ms'; + } else if (time >= 2000) { + distribution = '2000ms above'; + } + + return distribution; } - function getCpmDistribution(cpm) { - var distribution; - if (cpm >= 0 && cpm < 0.5) { - distribution = '$0-0.5'; - } else if (cpm >= 0.5 && cpm < 1) { - distribution = '$0.5-1'; - } else if (cpm >= 1 && cpm < 1.5) { - distribution = '$1-1.5'; - } else if (cpm >= 1.5 && cpm < 2) { - distribution = '$1.5-2'; - } else if (cpm >= 2 && cpm < 2.5) { - distribution = '$2-2.5'; - } else if (cpm >= 2.5 && cpm < 3) { - distribution = '$2.5-3'; - } else if (cpm >= 3 && cpm < 4) { - distribution = '$3-4'; - } else if (cpm >= 4 && cpm < 6) { - distribution = '$4-6'; - } else if (cpm >= 6 && cpm < 8) { - distribution = '$6-8'; - } else if (cpm >= 8) { - distribution = '$8 above'; - } - return distribution; + var distribution; + if (cpm >= 0 && cpm < 0.5) { + distribution = '$0-0.5'; + } else if (cpm >= 0.5 && cpm < 1) { + distribution = '$0.5-1'; + } else if (cpm >= 1 && cpm < 1.5) { + distribution = '$1-1.5'; + } else if (cpm >= 1.5 && cpm < 2) { + distribution = '$1.5-2'; + } else if (cpm >= 2 && cpm < 2.5) { + distribution = '$2-2.5'; + } else if (cpm >= 2.5 && cpm < 3) { + distribution = '$2.5-3'; + } else if (cpm >= 3 && cpm < 4) { + distribution = '$3-4'; + } else if (cpm >= 4 && cpm < 6) { + distribution = '$4-6'; + } else if (cpm >= 6 && cpm < 8) { + distribution = '$6-8'; + } else if (cpm >= 8) { + distribution = '$8 above'; + } + + return distribution; } - - function sendBidRequestToGa(bid) { - if (bid && bid.bidderCode) { - _analyticsQueue.push(function() { - _eventCount++; - window[_gaGlobal]('send', 'event', _category, 'Requests', bid.bidderCode, 1, _disibleInteraction); - }); - } - //check the queue - checkAnalytics(); + if (bid && bid.bidderCode) { + _analyticsQueue.push(function () { + _eventCount++; + window[_gaGlobal]('send', 'event', _category, 'Requests', bid.bidderCode, 1, _disibleInteraction); + }); + } + + //check the queue + checkAnalytics(); } - function sendBidResponseToGa(bid) { - if (bid && bid.bidderCode) { - _analyticsQueue.push(function() { - var cpmCents = convertToCents(bid.cpm), - bidder = bid.bidderCode; - if (typeof bid.timeToRespond !== 'undefined' && _enableDistribution) { - _eventCount++; - var dis = getLoadTimeDistribution(bid.timeToRespond); - window[_gaGlobal]('send', 'event', 'Prebid.js Load Time Distribution', dis, bidder, 1, _disibleInteraction); - } - if (bid.cpm > 0) { - _eventCount = _eventCount + 2; - var cpmDis = getCpmDistribution(bid.cpm); - if(_enableDistribution){ - _eventCount++; - window[_gaGlobal]('send', 'event', 'Prebid.js CPM Distribution', cpmDis, bidder, 1, _disibleInteraction); - } - window[_gaGlobal]('send', 'event', _category, 'Bids', bidder, cpmCents, _disibleInteraction); - window[_gaGlobal]('send', 'event', _category, 'Bid Load Time', bidder, bid.timeToRespond, _disibleInteraction); - } - }); - } - //check the queue - checkAnalytics(); + if (bid && bid.bidderCode) { + _analyticsQueue.push(function () { + var cpmCents = convertToCents(bid.cpm); + var bidder = bid.bidderCode; + if (typeof bid.timeToRespond !== 'undefined' && _enableDistribution) { + _eventCount++; + var dis = getLoadTimeDistribution(bid.timeToRespond); + window[_gaGlobal]('send', 'event', 'Prebid.js Load Time Distribution', dis, bidder, 1, _disibleInteraction); + } + + if (bid.cpm > 0) { + _eventCount = _eventCount + 2; + var cpmDis = getCpmDistribution(bid.cpm); + if (_enableDistribution) { + _eventCount++; + window[_gaGlobal]('send', 'event', 'Prebid.js CPM Distribution', cpmDis, bidder, 1, _disibleInteraction); + } + + window[_gaGlobal]('send', 'event', _category, 'Bids', bidder, cpmCents, _disibleInteraction); + window[_gaGlobal]('send', 'event', _category, 'Bid Load Time', bidder, bid.timeToRespond, _disibleInteraction); + } + }); + } + + //check the queue + checkAnalytics(); } -function sendBidTimeouts(bid){ - - if(bid && bid.bidder){ - _analyticsQueue.push(function(){ - utils._each(_timedOutBidders, function(bidderCode){ - if(bid.bidder === bidderCode){ - _eventCount++; - window[_gaGlobal]('send', 'event', _category, 'Timeouts', bidderCode, bid.timeToRespond, _disibleInteraction); - } - }); - }); - } - checkAnalytics(); +function sendBidTimeouts(bid) { + + if (bid && bid.bidder) { + _analyticsQueue.push(function () { + utils._each(_timedOutBidders, function (bidderCode) { + if (bid.bidder === bidderCode) { + _eventCount++; + window[_gaGlobal]('send', 'event', _category, 'Timeouts', bidderCode, bid.timeToRespond, _disibleInteraction); + } + }); + }); + } + + checkAnalytics(); } function sendBidWonToGa(bid) { - var cpmCents = convertToCents(bid.cpm); - _analyticsQueue.push(function() { - _eventCount++; - window[_gaGlobal]('send', 'event', _category, 'Wins', bid.bidderCode, cpmCents, _disibleInteraction); - }); - checkAnalytics(); + var cpmCents = convertToCents(bid.cpm); + _analyticsQueue.push(function () { + _eventCount++; + window[_gaGlobal]('send', 'event', _category, 'Wins', bid.bidderCode, cpmCents, _disibleInteraction); + }); + + checkAnalytics(); } diff --git a/src/polyfills.js b/src/polyfills.js new file mode 100644 index 00000000000..56288172038 --- /dev/null +++ b/src/polyfills.js @@ -0,0 +1,74 @@ +/** + * returns index of search element in array + * used by `utils` when Array.prototype.indexOf is unavailable (e.g. < IE9) + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill + * + * @param searchElement + * @param fromIndex + * @returns {*} + */ +exports.indexOf = function (searchElement, fromIndex) { + + var len; + var k; + + // 1. Let o be the result of calling ToObject passing + // the this value as the argument. + if (this === null) { + throw new TypeError('"this" is null or not defined'); + } + + var o = Object(this); + + // 2. Let lenValue be the result of calling the Get + // internal method of o with the argument "length". + // 3. Let len be ToUint32(lenValue). + + len = o.length >>> 0; + + // 4. If len is 0, return -1. + if (len === 0) { + return -1; + } + + // 5. If argument fromIndex was passed let n be + // ToInteger(fromIndex); else let n be 0. + var n = +fromIndex || 0; + + if (Math.abs(n) === Infinity) { + n = 0; + } + + // 6. If n >= len, return -1. + if (n >= len) { + return -1; + } + + // 7. If n >= 0, then Let k be n. + // 8. Else, n<0, Let k be len - abs(n). + // If k is less than 0, then let k be 0. + k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); + + // 9. Repeat, while k < len + while (k < len) { + // a. Let Pk be ToString(k). + // This is implicit for LHS operands of the in operator + // b. Let kPresent be the result of calling the + // HasProperty internal method of o with argument Pk. + // This step can be combined with c + // c. If kPresent is true, then + // i. Let elementK be the result of calling the Get + // internal method of o with the argument ToString(k). + // ii. Let same be the result of applying the + // Strict Equality Comparison Algorithm to + // searchElement and elementK. + // iii. If same is true, return k. + if (k in o && o[k] === searchElement) { + return k; + } + + k++; + } + + return -1; +}; diff --git a/src/prebid.js b/src/prebid.js index 62ead6e4d63..b9b2ff28d9d 100644 --- a/src/prebid.js +++ b/src/prebid.js @@ -1,4 +1,5 @@ /** @module pbjs */ + // if pbjs already exists in global dodcument scope, use it, if not, create the object window.pbjs = (window.pbjs || {}); window.pbjs.que = window.pbjs.que || []; @@ -18,19 +19,22 @@ var objectType_function = 'function'; var objectType_undefined = 'undefined'; var objectType_object = 'object'; var objectType_string = 'string'; -var objectType_number = 'number'; var BID_WON = CONSTANTS.EVENTS.BID_WON; var BID_TIMEOUT = CONSTANTS.EVENTS.BID_TIMEOUT; -var pb_preBidders = [], - pb_placements = [], - pb_bidderMap = {}, - pb_targetingMap = {}, - pb_keyHistoryMap = {}, - pb_bidsTimedOut = false; +var pb_preBidders = []; +var pb_placements = []; +var pb_bidderMap = {}; +var pb_targetingMap = {}; +var pb_keyHistoryMap = {}; +var pb_bidsTimedOut = false; +var eventValidators = { + bidWon: checkDefinedPlacement +}; /* Public vars */ + //default timeout for all bids pbjs.bidderTimeout = pbjs.bidderTimeout || 3000; pbjs.logging = pbjs.logging || false; @@ -38,6 +42,9 @@ pbjs.logging = pbjs.logging || false; //let the world know we are loaded pbjs.libLoaded = true; +//TODO: this should be auto generated from build +utils.logInfo('Prebid.js v0.7.0 loaded'); + //create adUnit array pbjs.adUnits = pbjs.adUnits || []; @@ -46,332 +53,343 @@ pbjs.adUnits = pbjs.adUnits || []; * @param {function} cmd Annoymous function to execute * @alias module:pbjs.que.push */ -pbjs.que.push = function(cmd) { - if (typeof cmd === objectType_function) { - try { - cmd.call(); - } catch (e) { - utils.logError('Error processing command :' + e.message); - } - } else { - utils.logError('Commands written into pbjs.que.push must wrapped in a function'); - } +pbjs.que.push = function (cmd) { + if (typeof cmd === objectType_function) { + try { + cmd.call(); + } catch (e) { + utils.logError('Error processing command :' + e.message); + } + } else { + utils.logError('Commands written into pbjs.que.push must wrapped in a function'); + } }; function processQue() { - for (var i = 0; i < pbjs.que.length; i++) { - if (typeof pbjs.que[i].called === objectType_undefined) { - try{ - pbjs.que[i].call(); - pbjs.que[i].called = true; - } - catch(e){ - utils.logError('Error processing command :', 'prebid.js', e); - } - - } - } + for (var i = 0; i < pbjs.que.length; i++) { + if (typeof pbjs.que[i].called === objectType_undefined) { + try { + pbjs.que[i].call(); + pbjs.que[i].called = true; + } + catch (e) { + utils.logError('Error processing command :', 'prebid.js', e); + } + } + } } /* * Main method entry point method */ function init(timeout, adUnitCodeArr) { - var cbTimeout = 0; - if(typeof timeout === objectType_undefined || timeout === null){ - cbTimeout = pbjs.bidderTimeout; - } - else{ - cbTimeout = timeout; - } - - if (!isValidAdUnitSetting()) { - utils.logMessage('No adUnits configured. No bids requested.'); - return; - } - //set timeout for all bids - setTimeout(bidmanager.executeCallback, cbTimeout); - //parse settings into internal vars - if (adUnitCodeArr && utils.isArray(adUnitCodeArr)) { - for (var k = 0; k < adUnitCodeArr.length; k++) { - for (var i = 0; i < pbjs.adUnits.length; i++) { - if (pbjs.adUnits[i].code === adUnitCodeArr[k]) { - pb_placements.push(pbjs.adUnits[i]); - } - } - } - loadPreBidders(); - sortAndCallBids(); - } else { - pb_placements = pbjs.adUnits; - //Aggregrate prebidders by their codes - loadPreBidders(); - //sort and call // default no sort - sortAndCallBids(); - } + var cbTimeout = 0; + if (typeof timeout === objectType_undefined || timeout === null) { + cbTimeout = pbjs.bidderTimeout; + } else { + cbTimeout = timeout; + } + + if (!isValidAdUnitSetting()) { + utils.logMessage('No adUnits configured. No bids requested.'); + return; + } + + //set timeout for all bids + setTimeout(bidmanager.executeCallback, cbTimeout); + + //parse settings into internal vars + if (adUnitCodeArr && utils.isArray(adUnitCodeArr)) { + for (var k = 0; k < adUnitCodeArr.length; k++) { + for (var i = 0; i < pbjs.adUnits.length; i++) { + if (pbjs.adUnits[i].code === adUnitCodeArr[k]) { + pb_placements.push(pbjs.adUnits[i]); + } + } + } + + loadPreBidders(); + sortAndCallBids(); + } else { + pb_placements = pbjs.adUnits; + + //Aggregrate prebidders by their codes + loadPreBidders(); + + //sort and call // default no sort + sortAndCallBids(); + } } function isValidAdUnitSetting() { - if (pbjs.adUnits && pbjs.adUnits.length !== 0) { - return true; - } - return false; + return !!(pbjs.adUnits && pbjs.adUnits.length !== 0); + } -function timeOutBidders(){ - if(!pb_bidsTimedOut){ - pb_bidsTimedOut = true; - var timedOutBidders = bidmanager.getTimedOutBidders(); - events.emit(BID_TIMEOUT, timedOutBidders); - } +function timeOutBidders() { + if (!pb_bidsTimedOut) { + pb_bidsTimedOut = true; + var timedOutBidders = bidmanager.getTimedOutBidders(); + events.emit(BID_TIMEOUT, timedOutBidders); + } } function sortAndCallBids(sortFunc) { - //Translate the bidder map into array so we can sort later if wanted - var pbArr = Object.keys(pb_bidderMap).map(function(key) { - return pb_bidderMap[key]; - }); - if (typeof sortFunc === objectType_function) { - pbArr.sort(sortFunc); - } - adaptermanager.callBids(pbArr); + //Translate the bidder map into array so we can sort later if wanted + var pbArr = utils._map(pb_bidderMap, function (v) { + return v; + }); + + if (typeof sortFunc === objectType_function) { + pbArr.sort(sortFunc); + } + + adaptermanager.callBids(pbArr); } function loadPreBidders() { - for (var i = 0; i < pb_placements.length; i++) { - var bids = pb_placements[i][CONSTANTS.JSON_MAPPING.PL_BIDS]; - var placementCode = pb_placements[i][CONSTANTS.JSON_MAPPING.PL_CODE]; - storeBidRequestByBidder(pb_placements[i][CONSTANTS.JSON_MAPPING.PL_CODE], pb_placements[i][CONSTANTS.JSON_MAPPING.PL_SIZE], bids); - //store pending response by placement - bidmanager.addBidResponse(placementCode); - } + for (var i = 0; i < pb_placements.length; i++) { + var bids = pb_placements[i][CONSTANTS.JSON_MAPPING.PL_BIDS]; + var placementCode = pb_placements[i][CONSTANTS.JSON_MAPPING.PL_CODE]; + storeBidRequestByBidder(pb_placements[i][CONSTANTS.JSON_MAPPING.PL_CODE], pb_placements[i][CONSTANTS.JSON_MAPPING.PL_SIZE], bids); - for (i = 0; i < pb_preBidders.length; i++) { - pb_preBidders[i].loadPreBid(); - } - //send a reference to bidmanager - bidmanager.setBidderMap(pb_bidderMap); -} + //store pending response by placement + bidmanager.addBidResponse(placementCode); + } -function storeBidRequestByBidder(placementCode, sizes, bids) { - for (var i = 0; i < bids.length; i++) { - - var currentBid = bids[i]; - currentBid.placementCode = placementCode; - currentBid.sizes = sizes; - - //look up bidder on map - var bidderName = bids[i][CONSTANTS.JSON_MAPPING.BD_BIDDER]; - var bidderObj = pb_bidderMap[bidderName]; - if (typeof bidderObj === objectType_undefined) { - //bid not found - var partnerBids = { - bidderCode: bidderName, - bids: [] - }; - partnerBids.bids.push(currentBid); - //put bidder on map with bids - pb_bidderMap[bidderName] = partnerBids; - } else { - bidderObj.bids.push(currentBid); - } + for (i = 0; i < pb_preBidders.length; i++) { + pb_preBidders[i].loadPreBid(); + } - } -} - -//use in place of hasOwnPropery - as opposed to a polyfill -function hasOwn(o, p) { - if (o.hasOwnProperty) { - return o.hasOwnProperty(p); - } else { - return (typeof o[p] !== objectType_undefined) && (o.constructor.prototype[p] !== o[p]); - } + //send a reference to bidmanager + bidmanager.setBidderMap(pb_bidderMap); } -function isEmptyObject(obj) { - var name; - for (name in obj) { - return false; - } - return true; +function storeBidRequestByBidder(placementCode, sizes, bids) { + for (var i = 0; i < bids.length; i++) { + + var currentBid = bids[i]; + currentBid.placementCode = placementCode; + currentBid.sizes = sizes; + + //look up bidder on map + var bidderName = bids[i][CONSTANTS.JSON_MAPPING.BD_BIDDER]; + var bidderObj = pb_bidderMap[bidderName]; + if (typeof bidderObj === objectType_undefined) { + //bid not found + var partnerBids = { + bidderCode: bidderName, + bids: [] + }; + partnerBids.bids.push(currentBid); + + //put bidder on map with bids + pb_bidderMap[bidderName] = partnerBids; + } else { + bidderObj.bids.push(currentBid); + } + + } } function getWinningBid(bidArray) { - var winningBid = {}; - if (bidArray && bidArray.length !== 0) { - bidArray.sort(function(a, b) { - //put the highest CPM first - return b.cpm - a.cpm; - }); - //the first item has the highest cpm - winningBid = bidArray[0]; - //TODO : if winning bid CPM === 0 - we need to indicate no targeting should be set - } - return winningBid.bid; + var winningBid = {}; + if (bidArray && bidArray.length !== 0) { + bidArray.sort(function (a, b) { + //put the highest CPM first + return b.cpm - a.cpm; + }); -} + //the first item has the highest cpm + winningBid = bidArray[0]; + //TODO : if winning bid CPM === 0 - we need to indicate no targeting should be set + } + + return winningBid.bid; +} function setGPTAsyncTargeting(code, slot) { - //get the targeting that is already configured - var keyStrings = getTargetingfromGPTIdentifier(slot); - //copy keyStrings into pb_keyHistoryMap by code - if(!pb_keyHistoryMap[code]){ - pb_keyHistoryMap[code] = keyStrings; - } - else{ - utils.extend(pb_keyHistoryMap[code], keyStrings); - } - utils._each(pb_keyHistoryMap[code], function(value, key){ - //since DFP doesn't support deleting a single key, we will set all to empty string - //This is "clear" for that key - slot.setTargeting(key, ''); - //utils.logMessage('Attempting to clear the key : ' + key + ' to empty string for code: ' + code); - }); - for (var key in keyStrings) { - if (keyStrings.hasOwnProperty(key)) { - try { - utils.logMessage('Attempting to set key value for slot: ' + slot.getSlotElementId() + ' key: ' + key + ' value: ' + encodeURIComponent(keyStrings[key])); - slot.setTargeting(key, keyStrings[key]); - } catch (e) { - utils.logMessage('Problem setting key value pairs in slot: ' + e.message); - } - } - } + //get the targeting that is already configured + var keyStrings = getTargetingfromGPTIdentifier(slot); + + //copy keyStrings into pb_keyHistoryMap by code + if (!pb_keyHistoryMap[code]) { + pb_keyHistoryMap[code] = keyStrings; + } else { + utils.extend(pb_keyHistoryMap[code], keyStrings); + } + + utils._each(pb_keyHistoryMap[code], function (value, key) { + //since DFP doesn't support deleting a single key, we will set all to empty string + //This is "clear" for that key + slot.setTargeting(key, ''); + + //utils.logMessage('Attempting to clear the key : ' + key + ' to empty string for code: ' + code); + }); + + for (var key in keyStrings) { + if (keyStrings.hasOwnProperty(key)) { + try { + utils.logMessage('Attempting to set key value for slot: ' + slot.getSlotElementId() + ' key: ' + key + ' value: ' + encodeURIComponent(keyStrings[key])); + slot.setTargeting(key, keyStrings[key]); + } catch (e) { + utils.logMessage('Problem setting key value pairs in slot: ' + e.message); + } + } + } } /* * This function returns the object map of placements or * if placement code is specified return just 1 placement bids */ function getBidResponsesByAdUnit(adunitCode) { - var returnObj = {}; - if (adunitCode) { - returnObj = bidmanager.pbBidResponseByPlacement[adunitCode]; - return returnObj; - } - else { - return bidmanager.pbBidResponseByPlacement; - } + var returnObj = {}; + if (adunitCode) { + returnObj = bidmanager.pbBidResponseByPlacement[adunitCode]; + return returnObj; + } else { + return bidmanager.pbBidResponseByPlacement; + } } - /* * Copies bids into a bidArray response */ function buildBidResponse(bidArray) { - var bidResponseArray = []; - var adUnitCode = ''; - //temp array to hold auction for bids - var bidArrayTargeting = []; - var bidClone = {}; - if (bidArray && bidArray[0] && bidArray[0].adUnitCode) { - // init the pb_targetingMap for the adUnitCode - adUnitCode = bidArray[0] && bidArray[0].adUnitCode; - pb_targetingMap[adUnitCode] = {}; - for (var i = 0; i < bidArray.length; i++) { - var bid = bidArray[i]; - //clone by json parse. This also gets rid of unwanted function properties - bidClone = getCloneBid(bid); - - if (bid.alwaysUseBid && bidClone.adserverTargeting) { // add the bid if alwaysUse and bid has returned - // push key into targeting - pb_targetingMap[bidClone.adUnitCode] = utils.extend(pb_targetingMap[bidClone.adUnitCode], bidClone.adserverTargeting); - } else if (bid.cpm && bid.cpm > 0){ - //else put into auction array if cpm > 0 - bidArrayTargeting.push({ - cpm: bid.cpm, - bid: bid - }); - } - //put all bids into bidArray by default - bidResponseArray.push(bidClone); - } - } - - // push the winning bid into targeting map - if (adUnitCode && bidArrayTargeting.length !== 0) { - var winningBid = getWinningBid(bidArrayTargeting); - var keyValues = winningBid.adserverTargeting; - pb_targetingMap[adUnitCode] = utils.extend(pb_targetingMap[adUnitCode], keyValues); - } - - return bidResponseArray; + var bidResponseArray = []; + var adUnitCode = ''; + + //temp array to hold auction for bids + var bidArrayTargeting = []; + var bidClone = {}; + if (bidArray && bidArray[0] && bidArray[0].adUnitCode) { + // init the pb_targetingMap for the adUnitCode + adUnitCode = bidArray[0] && bidArray[0].adUnitCode; + pb_targetingMap[adUnitCode] = {}; + for (var i = 0; i < bidArray.length; i++) { + var bid = bidArray[i]; + + //clone by json parse. This also gets rid of unwanted function properties + bidClone = getCloneBid(bid); + + if (bid.alwaysUseBid && bidClone.adserverTargeting) { // add the bid if alwaysUse and bid has returned + // push key into targeting + pb_targetingMap[bidClone.adUnitCode] = utils.extend(pb_targetingMap[bidClone.adUnitCode], bidClone.adserverTargeting); + } else if (bid.cpm && bid.cpm > 0) { + //else put into auction array if cpm > 0 + bidArrayTargeting.push({ + cpm: bid.cpm, + bid: bid + }); + } + + //put all bids into bidArray by default + bidResponseArray.push(bidClone); + } + } + + // push the winning bid into targeting map + if (adUnitCode && bidArrayTargeting.length !== 0) { + var winningBid = getWinningBid(bidArrayTargeting); + var keyValues = winningBid.adserverTargeting; + pb_targetingMap[adUnitCode] = utils.extend(pb_targetingMap[adUnitCode], keyValues); + } + + return bidResponseArray; } function getCloneBid(bid) { - var bidClone = {}; - //clone by json parse. This also gets rid of unwanted function properties - if (bid) { - var jsonBid = JSON.stringify(bid); - bidClone = JSON.parse(jsonBid); - - //clean up bid clone - delete bidClone.pbLg; - delete bidClone.pbMg; - delete bidClone.pbHg; - } - return bidClone; + var bidClone = {}; + + //clone by json parse. This also gets rid of unwanted function properties + if (bid) { + var jsonBid = JSON.stringify(bid); + bidClone = JSON.parse(jsonBid); + + //clean up bid clone + delete bidClone.pbLg; + delete bidClone.pbMg; + delete bidClone.pbHg; + } + + return bidClone; } function resetBids() { - bidmanager.clearAllBidResponses(); - pb_bidderMap = {}; - pb_placements = []; - pb_targetingMap = {}; - pb_bidsTimedOut = false; + bidmanager.clearAllBidResponses(); + pb_bidderMap = {}; + pb_placements = []; + pb_targetingMap = {}; + pb_bidsTimedOut = false; } -function requestAllBids(tmout){ - var timeout = tmout; - resetBids(); - init(timeout); +function requestAllBids(tmout) { + var timeout = tmout; + resetBids(); + init(timeout); } +function checkDefinedPlacement(id) { + var placementCodes = utils._map(pb_placements, function (placement) { + return placement.code; + }); + + if (!utils.contains(placementCodes, id)) { + utils.logError('The "' + id + '" placement is not defined.'); + return; + } + + return true; +} ////////////////////////////////// -// // -// Start Public APIs // -// // +// // +// Start Public APIs // +// // ////////////////////////////////// /** * This function returns the query string targeting parameters available at this moment for a given ad unit. Note that some bidder's response may not have been received if you call this function too quickly after the requests are sent. * @param {string} [adunitCode] adUnitCode to get the bid responses for * @alias module:pbjs.getAdserverTargetingForAdUnitCodeStr - * @return {array} returnObj return bids array + * @return {array} returnObj return bids array */ -pbjs.getAdserverTargetingForAdUnitCodeStr = function(adunitCode) { - // call to retrieve bids array - if(adunitCode){ - var res = pbjs.getAdserverTargetingForAdUnitCode(adunitCode); - return utils.transformAdServerTargetingObj(res); - } - else{ - utils.logMessage('Need to call getAdserverTargetingForAdUnitCodeStr with adunitCode'); - } +pbjs.getAdserverTargetingForAdUnitCodeStr = function (adunitCode) { + utils.logInfo('Invoking pbjs.getAdserverTargetingForAdUnitCodeStr', arguments); + // call to retrieve bids array + if (adunitCode) { + var res = pbjs.getAdserverTargetingForAdUnitCode(adunitCode); + return utils.transformAdServerTargetingObj(res); + } else { + utils.logMessage('Need to call getAdserverTargetingForAdUnitCodeStr with adunitCode'); + } }; /** * This function returns the query string targeting parameters available at this moment for a given ad unit. Note that some bidder's response may not have been received if you call this function too quickly after the requests are sent. * @param {string} [adunitCode] adUnitCode to get the bid responses for * @alias module:pbjs.getAdserverTargetingForAdUnitCode - * @return {object} returnObj return bids + * @return {object} returnObj return bids */ -pbjs.getAdserverTargetingForAdUnitCode = function(adunitCode) { - // call to populate pb_targetingMap - pbjs.getBidResponses(adunitCode); +pbjs.getAdserverTargetingForAdUnitCode = function (adunitCode) { + utils.logInfo('Invoking pbjs.getAdserverTargetingForAdUnitCode', arguments); + // call to populate pb_targetingMap + pbjs.getBidResponses(adunitCode); - if (adunitCode) { - return pb_targetingMap[adunitCode]; - } - return pb_targetingMap; + if (adunitCode) { + return pb_targetingMap[adunitCode]; + } + + return pb_targetingMap; }; /** * returns all ad server targeting for all ad units * @return {object} Map of adUnitCodes and targeting values [] * @alias module:pbjs.getAdserverTargeting */ -pbjs.getAdserverTargeting = function() { - return pbjs.getAdserverTargetingForAdUnitCode(); +pbjs.getAdserverTargeting = function () { + utils.logInfo('Invoking pbjs.getAdserverTargeting', arguments); + return pbjs.getAdserverTargetingForAdUnitCode(); }; /** @@ -380,40 +398,40 @@ pbjs.getAdserverTargeting = function() { * @alias module:pbjs.getBidResponses * @return {object} map | object that contains the bidResponses */ -pbjs.getBidResponses = function(adunitCode) { - var bidArrayTargeting = []; - var response = {}; - var bidArray = []; - var returnObj = {}; - - if (adunitCode) { - response = getBidResponsesByAdUnit(adunitCode); - bidArray = []; - if (response && response.bids) { - bidArray = buildBidResponse(response.bids); - } - - returnObj = { - bids: bidArray - }; - - } else { - response = getBidResponsesByAdUnit(); - for (var adUnit in response) { - if (response.hasOwnProperty(adUnit)) { - if (response && response[adUnit] && response[adUnit].bids) { - bidArray = buildBidResponse(response[adUnit].bids); - } - - returnObj[adUnit] = { - bids: bidArray - }; - - } - } - } - - return returnObj; +pbjs.getBidResponses = function (adunitCode) { + utils.logInfo('Invoking pbjs.getBidResponses', arguments); + var response = {}; + var bidArray = []; + var returnObj = {}; + + if (adunitCode) { + response = getBidResponsesByAdUnit(adunitCode); + bidArray = []; + if (response && response.bids) { + bidArray = buildBidResponse(response.bids); + } + + returnObj = { + bids: bidArray + }; + + } else { + response = getBidResponsesByAdUnit(); + for (var adUnit in response) { + if (response.hasOwnProperty(adUnit)) { + if (response && response[adUnit] && response[adUnit].bids) { + bidArray = buildBidResponse(response[adUnit].bids); + } + + returnObj[adUnit] = { + bids: bidArray + }; + + } + } + } + + return returnObj; }; /** @@ -422,57 +440,62 @@ pbjs.getBidResponses = function(adunitCode) { * @alias module:pbjs.getBidResponsesForAdUnitCode * @return {Object} bidResponse object */ -pbjs.getBidResponsesForAdUnitCode = function(adUnitCode) { - return pbjs.getBidResponses(adUnitCode); +pbjs.getBidResponsesForAdUnitCode = function (adUnitCode) { + utils.logInfo('Invoking pbjs.getBidResponsesForAdUnitCode', arguments); + return pbjs.getBidResponses(adUnitCode); }; /** * Set query string targeting on adUnits specified. The logic for deciding query strings is described in the section Configure AdServer Targeting. Note that this function has to be called after all ad units on page are defined. * @param {array} [codeArr] an array of adUnitodes to set targeting for. * @alias module:pbjs.setTargetingForAdUnitsGPTAsync */ -pbjs.setTargetingForAdUnitsGPTAsync = function(codeArr) { - if (!window.googletag || !utils.isFn(window.googletag.pubads) || !utils.isFn(window.googletag.pubads().getSlots)) { - utils.logError('window.googletag is not defined on the page'); - return; - } - - //emit bid timeout event here - timeOutBidders(); - - var adUnitCodesArr = codeArr; - - if (typeof codeArr === objectType_string) { - adUnitCodesArr = [codeArr]; - } else if (typeof codeArr === objectType_object) { - adUnitCodesArr = codeArr; - } - - var placementBids = {}, - i = 0; - if (adUnitCodesArr) { - for (i = 0; i < adUnitCodesArr.length; i++) { - var code = adUnitCodesArr[i]; - //get all the slots from google tag - var slots = window.googletag.pubads().getSlots(); - for (var k = 0; k < slots.length; k++) { - - if (slots[k].getSlotElementId() === code || slots[k].getAdUnitPath() === code) { - placementBids = getBidResponsesByAdUnit(code); - setGPTAsyncTargeting(code, slots[k]); - } - } - } - } else { - //get all the slots from google tag - var slots = window.googletag.pubads().getSlots(); - for (i = 0; i < slots.length; i++) { - var adUnitCode = slots[i].getSlotElementId(); - if (adUnitCode) { - //placementBids = getBidsFromGTPIdentifier(slots[i]); - setGPTAsyncTargeting(adUnitCode, slots[i]); - } - } - } +pbjs.setTargetingForAdUnitsGPTAsync = function (codeArr) { + utils.logInfo('Invoking pbjs.setTargetingForAdUnitsGPTAsync', arguments); + if (!window.googletag || !utils.isFn(window.googletag.pubads) || !utils.isFn(window.googletag.pubads().getSlots)) { + utils.logError('window.googletag is not defined on the page'); + return; + } + + //emit bid timeout event here + timeOutBidders(); + + var adUnitCodesArr = codeArr; + + if (typeof codeArr === objectType_string) { + adUnitCodesArr = [codeArr]; + } else if (typeof codeArr === objectType_object) { + adUnitCodesArr = codeArr; + } + + var placementBids = {}; + var i = 0; + var slots; + + if (adUnitCodesArr) { + for (i = 0; i < adUnitCodesArr.length; i++) { + var code = adUnitCodesArr[i]; + + //get all the slots from google tag + slots = window.googletag.pubads().getSlots(); + for (var k = 0; k < slots.length; k++) { + + if (slots[k].getSlotElementId() === code || slots[k].getAdUnitPath() === code) { + placementBids = getBidResponsesByAdUnit(code); + setGPTAsyncTargeting(code, slots[k]); + } + } + } + } else { + //get all the slots from google tag + slots = window.googletag.pubads().getSlots(); + for (i = 0; i < slots.length; i++) { + var adUnitCode = slots[i].getSlotElementId(); + if (adUnitCode) { + //placementBids = getBidsFromGTPIdentifier(slots[i]); + setGPTAsyncTargeting(adUnitCode, slots[i]); + } + } + } }; /** @@ -480,28 +503,31 @@ pbjs.setTargetingForAdUnitsGPTAsync = function(codeArr) { * @param {[type]} slot [description] * @return {[type]} [description] */ -function getTargetingfromGPTIdentifier(slot){ - var targeting = null; - if(slot){ - //first get by elementId - targeting = pbjs.getAdserverTargetingForAdUnitCode(slot.getSlotElementId()); - //if not available, try by adUnitPath - if(!targeting){ - targeting = pbjs.getAdserverTargetingForAdUnitCode(slot.getAdUnitPath()); - } - } - return targeting; +function getTargetingfromGPTIdentifier(slot) { + var targeting = null; + if (slot) { + //first get by elementId + targeting = pbjs.getAdserverTargetingForAdUnitCode(slot.getSlotElementId()); + + //if not available, try by adUnitPath + if (!targeting) { + targeting = pbjs.getAdserverTargetingForAdUnitCode(slot.getAdUnitPath()); + } + } + + return targeting; } /** -/** + /** * Set query string targeting on all GPT ad units. * @alias module:pbjs.setTargetingForGPTAsync */ -pbjs.setTargetingForGPTAsync = function(codeArr) { - pbjs.setTargetingForAdUnitsGPTAsync(codeArr); +pbjs.setTargetingForGPTAsync = function (codeArr) { + utils.logInfo('Invoking pbjs.setTargetingForGPTAsync', arguments); + pbjs.setTargetingForAdUnitsGPTAsync(codeArr); }; /** @@ -509,8 +535,9 @@ pbjs.setTargetingForGPTAsync = function(codeArr) { * @alias module:pbjs.allBidsAvailable * @return {bool} all bids available */ -pbjs.allBidsAvailable = function() { - return bidmanager.allBidsBack(); +pbjs.allBidsAvailable = function () { + utils.logInfo('Invoking pbjs.allBidsAvailable', arguments); + return bidmanager.allBidsBack(); }; /** @@ -519,79 +546,81 @@ pbjs.allBidsAvailable = function() { * @param {string} id bid id to locate the ad * @alias module:pbjs.renderAd */ -pbjs.renderAd = function(doc, id) { - utils.logMessage('Calling renderAd with adId :' + id); - if (doc && id) { - try { - //lookup ad by ad Id - var adObject = bidmanager._adResponsesByBidderId[id]; - if (adObject) { - //emit 'bid won' event here - events.emit(BID_WON, adObject); - var height = adObject.height; - var width = adObject.width; - var url = adObject.adUrl; - var ad = adObject.ad; - - if (ad) { - doc.write(ad); - doc.close(); - if (doc.defaultView && doc.defaultView.frameElement) { - doc.defaultView.frameElement.width = width; - doc.defaultView.frameElement.height = height; - } - } - //doc.body.style.width = width; - //doc.body.style.height = height; - else if (url) { - doc.write(''); - doc.close(); - - if (doc.defaultView && doc.defaultView.frameElement) { - doc.defaultView.frameElement.width = width; - doc.defaultView.frameElement.height = height; - } - - } else { - utils.logError('Error trying to write ad. No ad for bid response id: ' + id); - } - - } else { - utils.logError('Error trying to write ad. Cannot find ad by given id : ' + id); - } - - } catch (e) { - utils.logError('Error trying to write ad Id :' + id + ' to the page:' + e.message); - } - } else { - utils.logError('Error trying to write ad Id :' + id + ' to the page. Missing document or adId'); - } +pbjs.renderAd = function (doc, id) { + utils.logInfo('Invoking pbjs.renderAd', arguments); + utils.logMessage('Calling renderAd with adId :' + id); + if (doc && id) { + try { + //lookup ad by ad Id + var adObject = bidmanager._adResponsesByBidderId[id]; + if (adObject) { + //emit 'bid won' event here + events.emit(BID_WON, adObject); + var height = adObject.height; + var width = adObject.width; + var url = adObject.adUrl; + var ad = adObject.ad; + + if (ad) { + doc.write(ad); + doc.close(); + if (doc.defaultView && doc.defaultView.frameElement) { + doc.defaultView.frameElement.width = width; + doc.defaultView.frameElement.height = height; + } + } + + //doc.body.style.width = width; + //doc.body.style.height = height; + else if (url) { + doc.write(''); + doc.close(); + + if (doc.defaultView && doc.defaultView.frameElement) { + doc.defaultView.frameElement.width = width; + doc.defaultView.frameElement.height = height; + } + + } else { + utils.logError('Error trying to write ad. No ad for bid response id: ' + id); + } + + } else { + utils.logError('Error trying to write ad. Cannot find ad by given id : ' + id); + } + + } catch (e) { + utils.logError('Error trying to write ad Id :' + id + ' to the page:' + e.message); + } + } else { + utils.logError('Error trying to write ad Id :' + id + ' to the page. Missing document or adId'); + } }; - -/* +/** * @deprecated - will be removed next release. Use pbjs.requestBids */ -pbjs.requestBidsForAdUnit = function(adUnitCode) { - resetBids(); - init(adUnitCode); +pbjs.requestBidsForAdUnit = function (adUnitCode) { + resetBids(); + init(adUnitCode); }; /** * @deprecated - will be removed next release. Use pbjs.requestBids */ -pbjs.requestBidsForAdUnits = function(adUnitsObj) { - if (!adUnitsObj || adUnitsObj.constructor !== Array) { - utils.logError('requestBidsForAdUnits must pass an array of adUnits'); - return; - } - resetBids(); - var adUnitBackup = pbjs.adUnits.slice(0); - pbjs.adUnits = adUnitsObj; - init(); - pbjs.adUnits = adUnitBackup; +pbjs.requestBidsForAdUnits = function (adUnitsObj) { + if (!adUnitsObj || adUnitsObj.constructor !== Array) { + utils.logError('requestBidsForAdUnits must pass an array of adUnits'); + return; + } + + resetBids(); + var adUnitBackup = pbjs.adUnits.slice(0); + pbjs.adUnits = adUnitsObj; + init(); + pbjs.adUnits = adUnitBackup; }; @@ -600,17 +629,17 @@ pbjs.requestBidsForAdUnits = function(adUnitsObj) { * @param {String} adUnitCode the adUnitCode to remove * @alias module:pbjs.removeAdUnit */ -pbjs.removeAdUnit = function(adUnitCode) { - if (adUnitCode) { - for (var i = 0; i < pbjs.adUnits.length; i++) { - if (pbjs.adUnits[i].code === adUnitCode) { - pbjs.adUnits.splice(i, 1); - } - } - } +pbjs.removeAdUnit = function (adUnitCode) { + utils.logInfo('Invoking pbjs.removeAdUnit', arguments); + if (adUnitCode) { + for (var i = 0; i < pbjs.adUnits.length; i++) { + if (pbjs.adUnits[i].code === adUnitCode) { + pbjs.adUnits.splice(i, 1); + } + } + } }; - /** * Request bids ad-hoc. This function does not add or remove adUnits already configured. * @param {Object} requestObj @@ -620,85 +649,128 @@ pbjs.removeAdUnit = function(adUnitCode) { * @param {function} [requestObj.bidsBackHandler] Callback to execute when all the bid responses are back or the timeout hits. * @alias module:pbjs.requestBids */ -pbjs.requestBids = function(requestObj) { - if (!requestObj) { - //utils.logMessage('requesting all bids'); - - requestAllBids(); - - } - else{ - var adUnitCodes = requestObj.adUnitCodes; - var adUnits = requestObj.adUnits; - var timeout = requestObj.timeout; - var bidsBackHandler = requestObj.bidsBackHandler; - var adUnitBackup = pbjs.adUnits.slice(0); - - if (typeof bidsBackHandler === objectType_function) { - bidmanager.addOneTimeCallback(bidsBackHandler); - } - - if (adUnitCodes && utils.isArray(adUnitCodes)) { - resetBids(); - init(timeout, adUnitCodes); - - } else if (adUnits && utils.isArray(adUnits)) { - resetBids(); - pbjs.adUnits = adUnits; - init(timeout); - } else { - //request all ads - requestAllBids(timeout); - } - - pbjs.adUnits = adUnitBackup; - } +pbjs.requestBids = function (requestObj) { + utils.logInfo('Invoking pbjs.requestBids', arguments); + if (!requestObj) { + requestAllBids(); + } else { + var adUnitCodes = requestObj.adUnitCodes; + var adUnits = requestObj.adUnits; + var timeout = requestObj.timeout; + var bidsBackHandler = requestObj.bidsBackHandler; + var adUnitBackup = pbjs.adUnits.slice(0); + + if (typeof bidsBackHandler === objectType_function) { + bidmanager.addOneTimeCallback(bidsBackHandler); + } + + if (adUnitCodes && utils.isArray(adUnitCodes)) { + resetBids(); + init(timeout, adUnitCodes); + + } else if (adUnits && utils.isArray(adUnits)) { + resetBids(); + pbjs.adUnits = adUnits; + init(timeout); + } else { + //request all ads + requestAllBids(timeout); + } + + pbjs.adUnits = adUnitBackup; + } }; /** * * Add adunit(s) - * @param {(string|string[])} Array of adUnits or single adUnit Object. + * @param {Array|String} adUnitArr Array of adUnits or single adUnit Object. * @alias module:pbjs.addAdUnits */ -pbjs.addAdUnits = function(adUnitArr) { - if (utils.isArray(adUnitArr)) { - //append array to existing - pbjs.adUnits.push.apply(pbjs.adUnits, adUnitArr); - } else if (typeof adUnitArr === objectType_object) { - pbjs.adUnits.push(adUnitArr); - } +pbjs.addAdUnits = function (adUnitArr) { + utils.logInfo('Invoking pbjs.addAdUnits', arguments); + if (utils.isArray(adUnitArr)) { + //append array to existing + pbjs.adUnits.push.apply(pbjs.adUnits, adUnitArr); + } else if (typeof adUnitArr === objectType_object) { + pbjs.adUnits.push(adUnitArr); + } }; +/** + * @param {String} event the name of the event + * @param {Function} handler a callback to set on event + * @param {String} id an identifier in the context of the event + * + * This API call allows you to register a callback to handle a Prebid.js event. + * An optional `id` parameter provides more finely-grained event callback registration. + * This makes it possible to register callback events for a specific item in the + * event context. For example, `bidWon` events will accept an `id` for ad unit code. + * `bidWon` callbacks registered with an ad unit code id will be called when a bid + * for that ad unit code wins the auction. Without an `id` this method registers the + * callback for every `bidWon` event. + * + * Currently `bidWon` is the only event that accepts an `id` parameter. + */ +pbjs.onEvent = function (event, handler, id) { + utils.logInfo('Invoking pbjs.onEvent', arguments); + if (!utils.isFn(handler)) { + utils.logError('The event handler provided is not a function and was not set on event "' + event + '".'); + return; + } + + if (id && !eventValidators[event].call(null, id)) { + utils.logError('The id provided is not valid for event "' + event + '" and no handler was set.'); + return; + } + + events.on(event, handler, id); +}; + +/** + * @param {String} event the name of the event + * @param {Function} handler a callback to remove from the event + * @param {String} id an identifier in the context of the event (see `pbjs.onEvent`) + */ +pbjs.offEvent = function (event, handler, id) { + utils.logInfo('Invoking pbjs.offEvent', arguments); + if (id && !eventValidators[event].call(null, id)) { + return; + } + + events.off(event, handler, id); +}; /** * Add a callback event - * @param {String} event event to attach callback to Options: "allRequestedBidsBack" | "adUnitBidsBack" + * @param {String} eventStr event to attach callback to Options: "allRequestedBidsBack" | "adUnitBidsBack" * @param {Function} func function to execute. Paramaters passed into the function: (bidResObj), [adUnitCode]); * @alias module:pbjs.addCallback * @returns {String} id for callback */ -pbjs.addCallback = function(eventStr, func) { - var id = null; - if (!eventStr || !func || typeof func !== objectType_function) { - utils.logError('error registering callback. Check method signature'); - return id; - } - - id = utils.getUniqueIdentifierStr; - bidmanager.addCallback(id, func, eventStr); - return id; +pbjs.addCallback = function (eventStr, func) { + utils.logInfo('Invoking pbjs.addCallback', arguments); + var id = null; + if (!eventStr || !func || typeof func !== objectType_function) { + utils.logError('error registering callback. Check method signature'); + return id; + } + + id = utils.getUniqueIdentifierStr; + bidmanager.addCallback(id, func, eventStr); + return id; }; /** * Remove a callback event - * @param {string} cbId id of the callback to remove + * //@param {string} cbId id of the callback to remove * @alias module:pbjs.removeCallback * @returns {String} id for callback */ -pbjs.removeCallback = function(cbId) { - //todo +pbjs.removeCallback = function (/* cbId */) { + //todo + return null; }; /** @@ -707,53 +779,57 @@ pbjs.removeCallback = function(cbId) { * @param {[type]} bidderCode [description] * @return {[type]} [description] */ -pbjs.registerBidAdapter = function(bidderAdaptor, bidderCode){ - try{ - adaptermanager.registerBidAdapter(bidderAdaptor(), bidderCode); - } - catch(e){ - utils.logError('Error registering bidder adapter : ' + e.message); - } +pbjs.registerBidAdapter = function (bidderAdaptor, bidderCode) { + utils.logInfo('Invoking pbjs.registerBidAdapter', arguments); + try { + adaptermanager.registerBidAdapter(bidderAdaptor(), bidderCode); + } + catch (e) { + utils.logError('Error registering bidder adapter : ' + e.message); + } }; /** * */ - pbjs.bidsAvailableForAdapter = function(bidderCode){ - - //TODO getAd - var bids = pb_bidderMap[bidderCode].bids; - - for (var i = 0; i < bids.length; i++) { - var adunitCode = bids[i].placementCode; - var responseObj = bidmanager.pbBidResponseByPlacement[adunitCode]; - - var bid = bidfactory.createBid(1); - // bid.creative_id = adId; - bid.bidderCode = bidderCode; - bid.adUnitCode = adunitCode; - bid.bidder = bidderCode; - // bid.cpm = responseCPM; - // bid.adUrl = jptResponseObj.result.ad; - // bid.width = jptResponseObj.result.width; - // bid.height = jptResponseObj.result.height; - // bid.dealId = jptResponseObj.result.deal_id; - - responseObj.bids.push(bid); - responseObj.bidsReceivedCount++; - bidmanager.pbBidResponseByPlacement[adunitCode] = responseObj; - }; - - bidmanager.increaseBidResponseReceivedCount(bidderCode); -} +pbjs.bidsAvailableForAdapter = function (bidderCode) { + utils.logInfo('Invoking pbjs.bidsAvailableForAdapter', arguments); + //TODO getAd + var bids = pb_bidderMap[bidderCode].bids; + + for (var i = 0; i < bids.length; i++) { + var adunitCode = bids[i].placementCode; + var responseObj = bidmanager.pbBidResponseByPlacement[adunitCode]; + + var bid = bidfactory.createBid(1); + + // bid.creative_id = adId; + bid.bidderCode = bidderCode; + bid.adUnitCode = adunitCode; + bid.bidder = bidderCode; + + // bid.cpm = responseCPM; + // bid.adUrl = jptResponseObj.result.ad; + // bid.width = jptResponseObj.result.width; + // bid.height = jptResponseObj.result.height; + // bid.dealId = jptResponseObj.result.deal_id; + + responseObj.bids.push(bid); + responseObj.bidsReceivedCount++; + bidmanager.pbBidResponseByPlacement[adunitCode] = responseObj; + } + + bidmanager.increaseBidResponseReceivedCount(bidderCode); +}; /** * Wrapper to bidfactory.createBid() * @param {[type]} statusCode [description] * @return {[type]} [description] */ -pbjs.createBid = function(statusCode){ - return bidfactory.createBid(statusCode); +pbjs.createBid = function (statusCode) { + utils.logInfo('Invoking pbjs.createBid', arguments); + return bidfactory.createBid(statusCode); }; /** @@ -761,8 +837,9 @@ pbjs.createBid = function(statusCode){ * @param {[type]} adUnitCode [description] * @param {[type]} bid [description] */ -pbjs.addBidResponse = function(adUnitCode, bid){ - bidmanager.addBidResponse(adUnitCode, bid); +pbjs.addBidResponse = function (adUnitCode, bid) { + utils.logInfo('Invoking pbjs.addBidResponse', arguments); + bidmanager.addBidResponse(adUnitCode, bid); }; /** @@ -771,8 +848,9 @@ pbjs.addBidResponse = function(adUnitCode, bid){ * @param {Function} callback [description] * @return {[type]} [description] */ -pbjs.loadScript = function(tagSrc, callback){ - adloader.loadScript(tagSrc, callback); +pbjs.loadScript = function (tagSrc, callback, useCache) { + utils.logInfo('Invoking pbjs.loadScript', arguments); + adloader.loadScript(tagSrc, callback, useCache); }; /** @@ -780,8 +858,8 @@ pbjs.loadScript = function(tagSrc, callback){ * return data for analytics * @param {Function} [description] * @return {[type]} [description] - -pbjs.getAnalyticsData = function(){ + + pbjs.getAnalyticsData = function(){ var returnObj = {}; var bidResponses = pbjs.getBidResponses(); @@ -811,7 +889,7 @@ pbjs.getAnalyticsData = function(){ if(bid.bidderCode!==''){ var returnBids = returnObj[bid.bidderCode].bids; var returnIdx = 0; - + for(var j=0;j "text it was subbed this text with something else" */ -exports.replaceTokenInString = function(str, map, token) { +exports.replaceTokenInString = function (str, map, token) { this._each(map, function (value, key) { value = (value === undefined) ? '' : value; - var keyString = token + key.toUpperCase() + token, - re = new RegExp(keyString, 'g'); + var keyString = token + key.toUpperCase() + token; + var re = new RegExp(keyString, 'g'); str = str.replace(re, value); }); + return str; }; /* utility method to get incremental integer starting from 1 */ -var getIncrementalInteger = (function() { - var count = 0; - return function() { - count++; - return count; - }; +var getIncrementalInteger = (function () { + var count = 0; + return function () { + count++; + return count; + }; })(); function _getUniqueIdentifierStr() { - return getIncrementalInteger() + Math.random().toString(16).substr(2); + return getIncrementalInteger() + Math.random().toString(16).substr(2); } //generate a random string (to be used as a dynamic JSONP callback) exports.getUniqueIdentifierStr = _getUniqueIdentifierStr; -exports.getBidIdParamater = function(key, paramsObj) { - if (paramsObj && paramsObj[key]) { - return paramsObj[key]; - } - return ''; -}; +exports.getBidIdParamater = function (key, paramsObj) { + if (paramsObj && paramsObj[key]) { + return paramsObj[key]; + } -exports.tryAppendQueryString = function(existingUrl, key, value) { - if (value) { - return existingUrl += key + '=' + encodeURIComponent(value) + '&'; - } - return existingUrl; + return ''; }; +exports.tryAppendQueryString = function (existingUrl, key, value) { + if (value) { + return existingUrl += key + '=' + encodeURIComponent(value) + '&'; + } + + return existingUrl; +}; //parse a query string object passed in bid params //bid params should be an object such as {key: "value", key1 : "value1"} -exports.parseQueryStringParameters = function(queryObj) { - var result = ""; - for (var k in queryObj){ - if (queryObj.hasOwnProperty(k)) - result += k + "=" + encodeURIComponent(queryObj[k]) + "&"; - } - return result; +exports.parseQueryStringParameters = function (queryObj) { + var result = ''; + for (var k in queryObj) { + if (queryObj.hasOwnProperty(k)) + result += k + '=' + encodeURIComponent(queryObj[k]) + '&'; + } + + return result; }; - //transform an AdServer targeting bids into a query string to send to the adserver //bid params should be an object such as {key: "value", key1 : "value1"} -exports.transformAdServerTargetingObj = function(adServerTargeting) { - var result = ""; - if (!adServerTargeting) - return ""; - for (var k in adServerTargeting) - if (adServerTargeting.hasOwnProperty(k)) - result += k + "=" + encodeURIComponent(adServerTargeting[k]) + "&"; - return result; +exports.transformAdServerTargetingObj = function (adServerTargeting) { + var result = ''; + if (!adServerTargeting) + return ''; + for (var k in adServerTargeting) + if (adServerTargeting.hasOwnProperty(k)) + result += k + '=' + encodeURIComponent(adServerTargeting[k]) + '&'; + return result; }; //Copy all of the properties in the source objects over to the target object //return the target object. -exports.extend = function(target, source){ - target = target || {}; - - this._each(source,function(value,prop){ - if (typeof source[prop] === objectType_object) { - target[prop] = this.extend(target[prop], source[prop]); - } else { - target[prop] = source[prop]; - } - }); - return target; +exports.extend = function (target, source) { + target = target || {}; + + this._each(source, function (value, prop) { + if (typeof source[prop] === objectType_object) { + target[prop] = this.extend(target[prop], source[prop]); + } else { + target[prop] = source[prop]; + } + }); + + return target; }; -//parse a GPT-Style General Size Array or a string like "300x250" into a format -//suitable for passing to a GPT tag, may include size and/or promo sizes -exports.parseSizesInput = function(sizeObj) { - var sizeQueryString; - var parsedSizes = []; - - //if a string for now we can assume it is a single size, like "300x250" - if (typeof sizeObj === objectType_string) { - //multiple sizes will be comma-separated - var sizes = sizeObj.split(','); - //regular expression to match strigns like 300x250 - //start of line, at least 1 number, an "x" , then at least 1 number, and the then end of the line - var sizeRegex = /^(\d)+x(\d)+$/i; - if (sizes) { - for (var curSizePos in sizes) { - if (hasOwn(sizes, curSizePos) && sizes[curSizePos].match(sizeRegex)) { - parsedSizes.push(sizes[curSizePos]); - } - } - } - } else if (typeof sizeObj === objectType_object) { - var sizeArrayLength = sizeObj.length; - //don't process empty array - if (sizeArrayLength > 0) { - //if we are a 2 item array of 2 numbers, we must be a SingleSize array - if (sizeArrayLength === 2 && typeof sizeObj[0] === objectType_number && typeof sizeObj[1] === objectType_number) { - parsedSizes.push(this.parseGPTSingleSizeArray(sizeObj)); - } else { - //otherwise, we must be a MultiSize array - for (var i = 0; i < sizeArrayLength; i++) { - parsedSizes.push(this.parseGPTSingleSizeArray(sizeObj[i])); - } - - } - } - } - - - //combine string into proper querystring for impbus - var parsedSizesLength = parsedSizes.length; - if (parsedSizesLength > 0) { - //first value should be "size" - sizeQueryString = 'size=' + parsedSizes[0]; - if (parsedSizesLength > 1) { - //any subsequent values should be "promo_sizes" - sizeQueryString += '&promo_sizes='; - for (var j = 1; j < parsedSizesLength; j++) { - sizeQueryString += parsedSizes[j] += ','; - } - //remove trailing comma - if (sizeQueryString && sizeQueryString.charAt(sizeQueryString.length - 1) === ',') { - sizeQueryString = sizeQueryString.slice(0, sizeQueryString.length - 1); - } - } - } - - return sizeQueryString; +/** + * Parse a GPT-Style general size Array like `[[300, 250]]` or `"300x250,970x90"` into an array of sizes `["300x250"]` or '['300x250', '970x90']' + * @param {array[array|number]} sizeObj Input array or double array [300,250] or [[300,250], [728,90]] + * @return {array[string]} Array of strings like `["300x250"]` or `["300x250", "728x90"]` + */ +exports.parseSizesInput = function (sizeObj) { + var parsedSizes = []; + + //if a string for now we can assume it is a single size, like "300x250" + if (typeof sizeObj === objectType_string) { + //multiple sizes will be comma-separated + var sizes = sizeObj.split(','); + + //regular expression to match strigns like 300x250 + //start of line, at least 1 number, an "x" , then at least 1 number, and the then end of the line + var sizeRegex = /^(\d)+x(\d)+$/i; + if (sizes) { + for (var curSizePos in sizes) { + if (hasOwn(sizes, curSizePos) && sizes[curSizePos].match(sizeRegex)) { + parsedSizes.push(sizes[curSizePos]); + } + } + } + } else if (typeof sizeObj === objectType_object) { + var sizeArrayLength = sizeObj.length; + + //don't process empty array + if (sizeArrayLength > 0) { + //if we are a 2 item array of 2 numbers, we must be a SingleSize array + if (sizeArrayLength === 2 && typeof sizeObj[0] === objectType_number && typeof sizeObj[1] === objectType_number) { + parsedSizes.push(this.parseGPTSingleSizeArray(sizeObj)); + } else { + //otherwise, we must be a MultiSize array + for (var i = 0; i < sizeArrayLength; i++) { + parsedSizes.push(this.parseGPTSingleSizeArray(sizeObj[i])); + } + + } + } + } + + return parsedSizes; }; //parse a GPT style sigle size array, (i.e [300,250]) //into an AppNexus style string, (i.e. 300x250) -exports.parseGPTSingleSizeArray = function(singleSize) { - //if we aren't exactly 2 items in this array, it is invalid +exports.parseGPTSingleSizeArray = function (singleSize) { + //if we aren't exactly 2 items in this array, it is invalid if (this.isArray(singleSize) && singleSize.length === 2 && (!isNaN(singleSize[0]) && !isNaN(singleSize[1]))) { - return singleSize[0] + 'x' + singleSize[1]; - } + return singleSize[0] + 'x' + singleSize[1]; + } }; -exports.getTopWindowUrl = function() { - try { - return window.top.location.href; - } catch (e) { - return window.location.href; - } +exports.getTopWindowUrl = function () { + try { + return window.top.location.href; + } catch (e) { + return window.location.href; + } }; -exports.logMessage = function(msg) { - if (debugTurnedOn() && hasConsoleLogger()) { - console.log('MESSAGE: ' + msg); - } +exports.logInfo = function(msg, args) { + if (debugTurnedOn() && hasConsoleLogger()) { + if (infoLogger) { + if (!args || args.length === 0) { + args = ''; + } + + infoLogger('INFO: ' + msg + ((args === '') ? '' : ' : params : '), args); + } + } +}; + +exports.logMessage = function (msg) { + if (debugTurnedOn() && hasConsoleLogger()) { + console.log('MESSAGE: ' + msg); + } }; function hasConsoleLogger() { - return (window.console && window.console.log); + return (window.console && window.console.log); } + exports.hasConsoleLogger = hasConsoleLogger; var errLogFn = (function (hasLogger) { @@ -202,97 +206,96 @@ var errLogFn = (function (hasLogger) { return window.console.error ? 'error' : 'log'; }(hasConsoleLogger())); -var debugTurnedOn = function() { - if (pbjs.logging === false && _loggingChecked === false) { - pbjs.logging = getParameterByName(CONSTANTS.DEBUG_MODE).toUpperCase() === 'TRUE'; - _loggingChecked = true; - } - - if (pbjs.logging) { - return true; - } - return false; +var debugTurnedOn = function () { + if (pbjs.logging === false && _loggingChecked === false) { + pbjs.logging = getParameterByName(CONSTANTS.DEBUG_MODE).toUpperCase() === 'TRUE'; + _loggingChecked = true; + } + return !!pbjs.logging; }; + exports.debugTurnedOn = debugTurnedOn; -exports.logError = function(msg, code, exception) { - var errCode = code || 'ERROR'; - if (debugTurnedOn() && hasConsoleLogger()) { +exports.logError = function (msg, code, exception) { + var errCode = code || 'ERROR'; + if (debugTurnedOn() && hasConsoleLogger()) { console[errLogFn].call(console, errCode + ': ' + msg, exception || ''); - } + } }; exports.createInvisibleIframe = function _createInvisibleIframe() { - var f = document.createElement('iframe'); - f.id = _getUniqueIdentifierStr(); - f.height = 0; - f.width = 0; - f.border = '0px'; - f.hspace = '0'; - f.vspace = '0'; - f.marginWidth = '0'; - f.marginHeight = '0'; - f.style.border = '0'; - f.scrolling = 'no'; - f.frameBorder = '0'; - f.src = 'about:self'; - f.style = 'display:none'; - return f; + var f = document.createElement('iframe'); + f.id = _getUniqueIdentifierStr(); + f.height = 0; + f.width = 0; + f.border = '0px'; + f.hspace = '0'; + f.vspace = '0'; + f.marginWidth = '0'; + f.marginHeight = '0'; + f.style.border = '0'; + f.scrolling = 'no'; + f.frameBorder = '0'; + f.src = 'about:blank'; + f.style.display = 'none'; + return f; }; /* - * Check if a given paramater name exists in query string + * Check if a given parameter name exists in query string * and if it does return the value */ -var getParameterByName = function(name) { - var regexS = '[\\?&]' + name + '=([^&#]*)', - regex = new RegExp(regexS), - results = regex.exec(window.location.search); - if (results === null) { - return ''; - } - return decodeURIComponent(results[1].replace(/\+/g, ' ')); +var getParameterByName = function (name) { + var regexS = '[\\?&]' + name + '=([^&#]*)'; + var regex = new RegExp(regexS); + var results = regex.exec(window.location.search); + if (results === null) { + return ''; + } + + return decodeURIComponent(results[1].replace(/\+/g, ' ')); }; -exports.getPriceBucketString = function(cpm) { - var low = '', - med = '', - high = '', - cpmFloat = 0, - returnObj = { - low: low, - med: med, - high: high - }; - try { - cpmFloat = parseFloat(cpm); - if (cpmFloat) { - //round to closet .5 - if (cpmFloat > _lgPriceCap) { - returnObj.low = _lgPriceCap.toFixed(2); - } else { - returnObj.low = (Math.floor(cpm * 2) / 2).toFixed(2); - } - - //round to closet .1 - if (cpmFloat > _mgPriceCap) { - returnObj.med = _mgPriceCap.toFixed(2); - } else { - returnObj.med = (Math.floor(cpm * 10) / 10).toFixed(2); - } - - //round to closet .01 - if (cpmFloat > _hgPriceCap) { - returnObj.high = _hgPriceCap.toFixed(2); - } else { - returnObj.high = (Math.floor(cpm * 100) / 100).toFixed(2); - } - } - } catch (e) { - this.logError('Exception parsing CPM :' + e.message); - } - return returnObj; +exports.getPriceBucketString = function (cpm) { + var low = ''; + var med = ''; + var high = ''; + var cpmFloat = 0; + var returnObj = { + low: low, + med: med, + high: high + }; + try { + cpmFloat = parseFloat(cpm); + if (cpmFloat) { + //round to closet .5 + if (cpmFloat > _lgPriceCap) { + returnObj.low = _lgPriceCap.toFixed(2); + } else { + returnObj.low = (Math.floor(cpm * 2) / 2).toFixed(2); + } + + //round to closet .1 + if (cpmFloat > _mgPriceCap) { + returnObj.med = _mgPriceCap.toFixed(2); + } else { + returnObj.med = (Math.floor(cpm * 10) / 10).toFixed(2); + } + + //round to closet .01 + if (cpmFloat > _hgPriceCap) { + returnObj.high = _hgPriceCap.toFixed(2); + } else { + returnObj.high = (Math.floor(cpm * 100) / 100).toFixed(2); + } + } + } catch (e) { + this.logError('Exception parsing CPM :' + e.message); + } + + return returnObj; }; @@ -302,43 +305,46 @@ exports.getPriceBucketString = function(cpm) { * @param {string[]} requiredParamsArr [description] * @return {bool} Bool if paramaters are valid */ -exports.hasValidBidRequest = function(paramObj, requiredParamsArr, adapter){ +exports.hasValidBidRequest = function (paramObj, requiredParamsArr, adapter) { + var found = false; - for(var i = 0; i < requiredParamsArr.length; i++){ - var found = false; + function findParam(value, key) { + if (key === requiredParamsArr[i]) { + found = true; + } + } - this._each(paramObj, function (value, key) { - if (key === requiredParamsArr[i]) { - found = true; - } - }); + for (var i = 0; i < requiredParamsArr.length; i++) { + found = false; - if(!found){ - this.logError('Params are missing for bid request. One of these required paramaters are missing: ' + requiredParamsArr, adapter); - return false; - } - } + this._each(paramObj, findParam); - return true; + if (!found) { + this.logError('Params are missing for bid request. One of these required paramaters are missing: ' + requiredParamsArr, adapter); + return false; + } + } + + return true; }; // Handle addEventListener gracefully in older browsers -exports.addEventHandler = function(element, event, func) { - if (element.addEventListener) { - element.addEventListener(event, func, true); - } else if (element.attachEvent) { - element.attachEvent('on' + event, func); - } - }; - /** - * Return if the object is of the - * given type. - * @param {*} object to test - * @param {String} _t type string (e.g., Array) - * @return {Boolean} if object is of type _t - */ -exports.isA = function(object, _t) { - return toString.call(object) === '[object ' + _t + ']'; +exports.addEventHandler = function (element, event, func) { + if (element.addEventListener) { + element.addEventListener(event, func, true); + } else if (element.attachEvent) { + element.attachEvent('on' + event, func); + } +}; +/** + * Return if the object is of the + * given type. + * @param {*} object to test + * @param {String} _t type string (e.g., Array) + * @return {Boolean} if object is of type _t + */ +exports.isA = function (object, _t) { + return toString.call(object) === '[object ' + _t + ']'; }; exports.isFn = function (object) { @@ -359,53 +365,68 @@ exports.isArray = function (object) { * @param {*} object object to test * @return {Boolean} if object is empty */ -exports.isEmpty = function(object) { - if (!object) return true; - if (this.isArray(object) || this.isStr(object)) return !(object.length > 0); - for (var k in object) { - if (hasOwnProperty.call(object, k)) return false; - } - return true; - }; +exports.isEmpty = function (object) { + if (!object) return true; + if (this.isArray(object) || this.isStr(object)) { + return !(object.length > 0); // jshint ignore:line + } - /** - * Iterate object with the function - * falls back to es5 `forEach` - * @param {Array|Object} object - * @param {Function(value, key, object)} fn - */ -exports._each = function(object, fn) { - if (this.isEmpty(object)) return; - if (this.isFn(object.forEach)) return object.forEach(fn, this); - - var k = 0, - l = object.length; - - if (l > 0) { - for (; k < l; k++) fn(object[k], k, object); - } else { - for (k in object) { - if (hasOwnProperty.call(object, k)) fn.call(this, object[k], k); - } - } - }; + for (var k in object) { + if (hasOwnProperty.call(object, k)) return false; + } -exports.contains = function(a, obj) { - if(this.isEmpty(a)){ - return false; - } - if (this.isFn(a.indexOf)) { - return a.indexOf(obj) !== -1; - } - var i = a.length; - while (i--) { - if (a[i] === obj) { - return true; - } + return true; +}; + +/** + * Iterate object with the function + * falls back to es5 `forEach` + * @param {Array|Object} object + * @param {Function(value, key, object)} fn + */ +exports._each = function (object, fn) { + if (this.isEmpty(object)) return; + if (this.isFn(object.forEach)) return object.forEach(fn, this); + + var k = 0; + var l = object.length; + + if (l > 0) { + for (; k < l; k++) fn(object[k], k, object); + } else { + for (k in object) { + if (hasOwnProperty.call(object, k)) fn.call(this, object[k], k); } + } +}; + +exports.contains = function (a, obj) { + if (this.isEmpty(a)) { return false; + } + + if (this.isFn(a.indexOf)) { + return a.indexOf(obj) !== -1; + } + + var i = a.length; + while (i--) { + if (a[i] === obj) { + return true; + } + } + + return false; }; +exports.indexOf = (function () { + if (Array.prototype.indexOf) { + return Array.prototype.indexOf; + } + + return polyfills.indexOf; +}()); + /** * Map an array or object into another array * given a function @@ -420,13 +441,56 @@ exports._map = function (object, callback) { this._each(object, function (value, key) { output.push(callback(value, key, object)); }); + return output; }; -var hasOwn = function(objectToCheck, propertyToCheckFor) { - if (objectToCheck.hasOwnProperty) { - return objectToCheck.hasOwnProperty(propertyToCheckFor); +var hasOwn = function (objectToCheck, propertyToCheckFor) { + if (objectToCheck.hasOwnProperty) { + return objectToCheck.hasOwnProperty(propertyToCheckFor); + } else { + return (typeof objectToCheck[propertyToCheckFor] !== 'undefined') && (objectToCheck.constructor.prototype[propertyToCheckFor] !== objectToCheck[propertyToCheckFor]); + } +}; +/** + * Creates a snippet of HTML that retrieves the specified `url` + * @param {string} url URL to be requested + * @return {string} HTML snippet that contains the img src = set to `url` + */ +exports.createTrackPixelHtml = function (url) { + if (!url) { + return ''; + } + + let escapedUrl = encodeURI(url); + let img = '
'; + img += '
'; + return img; +}; + +/** + * Returns iframe document in a browser agnostic way + * @param {object} iframe reference + * @return {object} iframe `document` reference + */ +exports.getIframeDocument = function (iframe) { + if (!iframe) { + return; + } + + let doc; + try { + if (iframe.contentWindow) { + doc = iframe.contentWindow.document; + } else if (iframe.contentDocument.document) { + doc = iframe.contentDocument.document; } else { - return (typeof objectToCheck[propertyToCheckFor] !== UNDEFINED) && (objectToCheck.constructor.prototype[propertyToCheckFor] !== objectToCheck[propertyToCheckFor]); + doc = iframe.contentDocument; } -}; \ No newline at end of file + } + catch (e) { + this.logError('Cannot get iframe document', e); + } + + return doc; +}; diff --git a/styleRules.jscs.json b/styleRules.jscs.json deleted file mode 100644 index 46978d54cac..00000000000 --- a/styleRules.jscs.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "excludeFiles": [""], - "validateQuoteMarks": "'", - "disallowNewlineBeforeBlockStatements": true -} \ No newline at end of file diff --git a/test/automatedRunnner.html b/test/automatedRunnner.html deleted file mode 100644 index 2f82a9a36da..00000000000 --- a/test/automatedRunnner.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - Unit Tests for Prebid.js - - - -
- - - - - - - - - - - \ No newline at end of file diff --git a/test/fixtures/ad-server-targeting.json b/test/fixtures/ad-server-targeting.json new file mode 100644 index 00000000000..9c6774c1ed5 --- /dev/null +++ b/test/fixtures/ad-server-targeting.json @@ -0,0 +1,12 @@ +{ + "/9968336/header-bid-tag-0": { + "hb_bidder": "rubicon", + "hb_adid": "13f44b0d3c", + "hb_pb": "1.50" + }, + "/9968336/header-bid-tag1": { + "hb_bidder": "openx", + "hb_adid": "147ac541a", + "hb_pb": "1.00" + } +} diff --git a/test/fixtures/bid-responses-cloned.json b/test/fixtures/bid-responses-cloned.json new file mode 100644 index 00000000000..188518b4565 --- /dev/null +++ b/test/fixtures/bid-responses-cloned.json @@ -0,0 +1,130 @@ +{ + "/123456/header-bid-tag-1": { + "bids": [] + }, + "/123456/header-bid-tag-0": { + "bids": [ + { + "bidderCode": "criteo", + "width": 0, + "height": 0, + "statusMessage": "Bid returned empty or error response", + "adId": "83fb6a073", + "requestTimestamp": 1454535718619, + "responseTimestamp": 1454535720575, + "timeToRespond": 1956, + "cpm": 0, + "adUnitCode": "/123456/header-bid-tag-0", + "bidder": "criteo" + }, + { + "bidderCode": "sovrn", + "width": 0, + "height": 0, + "statusMessage": "Bid returned empty or error response", + "adId": "9d64dde8c", + "requestTimestamp": 1454535718628, + "responseTimestamp": 1454535721135, + "timeToRespond": 2507, + "cpm": 0, + "adUnitCode": "/123456/header-bid-tag-0", + "bidder": "sovrn" + }, + { + "bidderCode": "pulsepoint", + "width": 0, + "height": 0, + "statusMessage": "Bid returned empty or error response", + "adId": "102e25872d", + "requestTimestamp": 1454535718629, + "responseTimestamp": 1454535721687, + "timeToRespond": 3058, + "cpm": 0, + "adUnitCode": "/123456/header-bid-tag-0", + "bidder": "pulsepoint" + }, + { + "bidderCode": "amazon", + "width": 0, + "height": 0, + "statusMessage": "Bid available", + "adId": "112cdb3eff", + "adUnitCode": "/123456/header-bid-tag-0", + "bidder": "amazon" + }, + { + "bidderCode": "yieldbot", + "width": 0, + "height": 0, + "statusMessage": "Bid returned empty or error response", + "adId": "1234cc92d8", + "requestTimestamp": 1454535718624, + "responseTimestamp": 1454535722273, + "timeToRespond": 3649, + "cpm": 0, + "adUnitCode": "/123456/header-bid-tag-0", + "bidder": "yieldbot" + }, + { + "bidderCode": "openx", + "width": 0, + "height": 0, + "statusMessage": "Bid returned empty or error response", + "adId": "1383ffde21", + "requestTimestamp": 1454535718611, + "responseTimestamp": 1454535724228, + "timeToRespond": 5617, + "cpm": 0, + "adUnitCode": "/123456/header-bid-tag-0", + "bidder": "openx" + }, + { + "bidderCode": "rubicon", + "width": "300", + "height": "250", + "statusMessage": "Bid available", + "adId": "148018fe5e", + "cpm": 0.537234, + "ad": "", + "ad_id": "3163950", + "sizeId": "15", + "requestTimestamp": 1454535718610, + "responseTimestamp": 1454535724863, + "timeToRespond": 6253, + "adUnitCode": "/123456/header-bid-tag-0", + "bidder": "rubicon", + "size": "300x250", + "adserverTargeting": { + "hb_bidder": "rubicon", + "hb_adid": "148018fe5e", + "hb_pb": "10.00", + "foobar": "300x250" + } + }, + { + "bidderCode": "pubmatic", + "width": "300", + "height": "250", + "statusMessage": "Bid available", + "adId": "15bea0b1db", + "adSlot": "39620189@300x250", + "cpm": 0.01, + "ad": "\n ", + "adUrl": "http://aktrack.pubmatic.com/AdServer/AdDisplayTrackerServlet?operId=1&pubId=39741&siteId=66156&adId=148827&adServerId=243&kefact=0.010000&kaxefact=0.010000&kadNetFrequecy=1&kadwidth=300&kadheight=250&kadsizeid=9&kltstamp=1454535719&indirectAdId=0&adServerOptimizerId=2&ranreq=0.052495126612484455&kpbmtpfact=0.011000&dcId=1&tldId=13890466&passback=0&imprId=529C7210-AB7A-4217-A9BD-A3190CA2382A&oid=529C7210-AB7A-4217-A9BD-A3190CA2382A&ias=272&fbs=1&campaignId=5400&creativeId=0&pctr=0.000000&wDSPByrId=1&pageURL=http%3A%2F%2Flocalhost%3A9999%2FintegrationExamples%2Fgpt%2Fgpt.html&lpu=www.xfinity.com", + "dealId": "", + "requestTimestamp": 1454535718617, + "responseTimestamp": 1454535725437, + "timeToRespond": 6820, + "adUnitCode": "/123456/header-bid-tag-0", + "bidder": "pubmatic", + "size": "300x250", + "adserverTargeting": { + "hb_bidder": "pubmatic", + "hb_adid": "15bea0b1db", + "hb_pb": "10.00", + "foobar": "300x250" + } + } + ] + } +} \ No newline at end of file diff --git a/test/fixtures/bid-responses.json b/test/fixtures/bid-responses.json new file mode 100644 index 00000000000..35e55664cdc --- /dev/null +++ b/test/fixtures/bid-responses.json @@ -0,0 +1,155 @@ +{ + "/123456/header-bid-tag-1": { + "bids": [], + "allBidsAvailable": false, + "bidsReceivedCount": 0 + }, + "/123456/header-bid-tag-0": { + "bids": [ + { + "bidderCode": "criteo", + "width": 0, + "height": 0, + "statusMessage": "Bid returned empty or error response", + "adId": "83fb6a073", + "requestTimestamp": 1454535718619, + "responseTimestamp": 1454535720575, + "timeToRespond": 1956, + "cpm": 0, + "pbLg": "", + "pbMg": "", + "pbHg": "", + "adUnitCode": "/123456/header-bid-tag-0", + "bidder": "criteo" + }, + { + "bidderCode": "sovrn", + "width": 0, + "height": 0, + "statusMessage": "Bid returned empty or error response", + "adId": "9d64dde8c", + "requestTimestamp": 1454535718628, + "responseTimestamp": 1454535721135, + "timeToRespond": 2507, + "cpm": 0, + "pbLg": "", + "pbMg": "", + "pbHg": "", + "adUnitCode": "/123456/header-bid-tag-0", + "bidder": "sovrn" + }, + { + "bidderCode": "pulsepoint", + "width": 0, + "height": 0, + "statusMessage": "Bid returned empty or error response", + "adId": "102e25872d", + "requestTimestamp": 1454535718629, + "responseTimestamp": 1454535721687, + "timeToRespond": 3058, + "cpm": 0, + "pbLg": "", + "pbMg": "", + "pbHg": "", + "adUnitCode": "/123456/header-bid-tag-0", + "bidder": "pulsepoint" + }, + { + "bidderCode": "amazon", + "width": 0, + "height": 0, + "statusMessage": "Bid available", + "adId": "112cdb3eff", + "adUnitCode": "/123456/header-bid-tag-0", + "bidder": "amazon" + }, + { + "bidderCode": "yieldbot", + "width": 0, + "height": 0, + "statusMessage": "Bid returned empty or error response", + "adId": "1234cc92d8", + "requestTimestamp": 1454535718624, + "responseTimestamp": 1454535722273, + "timeToRespond": 3649, + "cpm": 0, + "pbLg": "", + "pbMg": "", + "pbHg": "", + "adUnitCode": "/123456/header-bid-tag-0", + "bidder": "yieldbot" + }, + { + "bidderCode": "openx", + "width": 0, + "height": 0, + "statusMessage": "Bid returned empty or error response", + "adId": "1383ffde21", + "requestTimestamp": 1454535718611, + "responseTimestamp": 1454535724228, + "timeToRespond": 5617, + "cpm": 0, + "pbLg": "", + "pbMg": "", + "pbHg": "", + "adUnitCode": "/123456/header-bid-tag-0", + "bidder": "openx" + }, + { + "bidderCode": "rubicon", + "width": "300", + "height": "250", + "statusMessage": "Bid available", + "adId": "148018fe5e", + "cpm": 0.537234, + "ad": "", + "ad_id": "3163950", + "sizeId": "15", + "requestTimestamp": 1454535718610, + "responseTimestamp": 1454535724863, + "timeToRespond": 6253, + "pbLg": "0.50", + "pbMg": "0.50", + "pbHg": "0.53", + "adUnitCode": "/123456/header-bid-tag-0", + "bidder": "rubicon", + "size": "300x250", + "adserverTargeting": { + "hb_bidder": "rubicon", + "hb_adid": "148018fe5e", + "hb_pb": "10.00", + "foobar": "300x250" + } + }, + { + "bidderCode": "pubmatic", + "width": "300", + "height": "250", + "statusMessage": "Bid available", + "adId": "15bea0b1db", + "adSlot": "39620189@300x250", + "cpm": 0.01, + "ad": "\n ", + "adUrl": "http://aktrack.pubmatic.com/AdServer/AdDisplayTrackerServlet?operId=1&pubId=39741&siteId=66156&adId=148827&adServerId=243&kefact=0.010000&kaxefact=0.010000&kadNetFrequecy=1&kadwidth=300&kadheight=250&kadsizeid=9&kltstamp=1454535719&indirectAdId=0&adServerOptimizerId=2&ranreq=0.052495126612484455&kpbmtpfact=0.011000&dcId=1&tldId=13890466&passback=0&imprId=529C7210-AB7A-4217-A9BD-A3190CA2382A&oid=529C7210-AB7A-4217-A9BD-A3190CA2382A&ias=272&fbs=1&campaignId=5400&creativeId=0&pctr=0.000000&wDSPByrId=1&pageURL=http%3A%2F%2Flocalhost%3A9999%2FintegrationExamples%2Fgpt%2Fgpt.html&lpu=www.xfinity.com", + "dealId": "", + "requestTimestamp": 1454535718617, + "responseTimestamp": 1454535725437, + "timeToRespond": 6820, + "pbLg": "0.00", + "pbMg": "0.00", + "pbHg": "0.01", + "adUnitCode": "/123456/header-bid-tag-0", + "bidder": "pubmatic", + "size": "300x250", + "adserverTargeting": { + "hb_bidder": "pubmatic", + "hb_adid": "15bea0b1db", + "hb_pb": "10.00", + "foobar": "300x250" + } + } + ], + "allBidsAvailable": false, + "bidsReceivedCount": 8 + } +} \ No newline at end of file diff --git a/test/fixtures/config.json b/test/fixtures/config.json new file mode 100644 index 00000000000..5b842813923 --- /dev/null +++ b/test/fixtures/config.json @@ -0,0 +1,12 @@ +{ + "adUnitElementIDs": [ + "div-test-ad-0", + "div-test-ad-1", + "div-test-ad-2" + ], + "adUnitCodes": [ + "/123456/header-bid-tag-0", + "/123456/header-bid-tag-1", + "/123456/header-bid-tag-2" + ] +} \ No newline at end of file diff --git a/test/fixtures/googletag-slots.json b/test/fixtures/googletag-slots.json new file mode 100644 index 00000000000..4c1347ba5e5 --- /dev/null +++ b/test/fixtures/googletag-slots.json @@ -0,0 +1,11 @@ +[ + { + "getSlotElementId": "function(){ return \"div-gpt-ad-1438287399331-0\"; }" + }, + { + "getSlotElementId": "function(){ return \"div-gpt-ad-1438287399331-1\"; }" + }, + { + "getSlotElementId": "function(){ return \"div-gpt-ad-1438287399331-2\"; }" + } +] \ No newline at end of file diff --git a/test/fixtures/targeting-map.json b/test/fixtures/targeting-map.json new file mode 100644 index 00000000000..373e538a08e --- /dev/null +++ b/test/fixtures/targeting-map.json @@ -0,0 +1,8 @@ +{ + "/123456/header-bid-tag-0": { + "hb_bidder": "rubicon", + "hb_adid": "148018fe5e", + "hb_pb": "10.00", + "foobar": "300x250" + } +} \ No newline at end of file diff --git a/test/helpers/pbjs-test-only.js b/test/helpers/pbjs-test-only.js new file mode 100644 index 00000000000..c2c1e9ef406 --- /dev/null +++ b/test/helpers/pbjs-test-only.js @@ -0,0 +1,10 @@ +export const pbjsTestOnly = { + + getAdUnits() { + return pbjs.adUnits; + }, + + clearAllAdUnits() { + pbjs.adUnits = []; + } +}; diff --git a/test/lib/browser/jquery.js b/test/lib/browser/jquery.js deleted file mode 100755 index ee0233703da..00000000000 --- a/test/lib/browser/jquery.js +++ /dev/null @@ -1,4 +0,0 @@ -/*! jQuery v1.7.1 jquery.com | jquery.org/license */ -(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"":"")+""),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;g=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="
"+""+"
",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="
t
",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="
",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")}; -f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&i.push({elem:this,matches:d.slice(e)});for(j=0;j0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function() -{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file diff --git a/test/lib/browser/mocha.css b/test/lib/browser/mocha.css deleted file mode 100755 index 01bac1abc18..00000000000 --- a/test/lib/browser/mocha.css +++ /dev/null @@ -1,199 +0,0 @@ -@charset "UTF-8"; -body { - font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; - padding: 60px 50px; -} - -#mocha ul, #mocha li { - margin: 0; - padding: 0; -} - -#mocha ul { - list-style: none; -} - -#mocha h1, #mocha h2 { - margin: 0; -} - -#mocha h1 { - margin-top: 15px; - font-size: 1em; - font-weight: 200; -} - -#mocha h1 a { - text-decoration: none; - color: inherit; -} - -#mocha h1 a:hover { - text-decoration: underline; -} - -#mocha .suite .suite h1 { - margin-top: 0; - font-size: .8em; -} - -#mocha h2 { - font-size: 12px; - font-weight: normal; - cursor: pointer; -} - -#mocha .suite { - margin-left: 15px; -} - -#mocha .test { - margin-left: 15px; -} - -#mocha .test:hover h2::after { - position: relative; - top: 0; - right: -10px; - content: '(view source)'; - font-size: 12px; - font-family: arial; - color: #888; -} - -#mocha .test.pending:hover h2::after { - content: '(pending)'; - font-family: arial; -} - -#mocha .test.pass.medium .duration { - background: #C09853; -} - -#mocha .test.pass.slow .duration { - background: #B94A48; -} - -#mocha .test.pass::before { - content: '✓'; - font-size: 12px; - display: block; - float: left; - margin-right: 5px; - color: #00d6b2; -} - -#mocha .test.pass .duration { - font-size: 9px; - margin-left: 5px; - padding: 2px 5px; - color: white; - -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); - -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); - box-shadow: inset 0 1px 1px rgba(0,0,0,.2); - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - -ms-border-radius: 5px; - -o-border-radius: 5px; - border-radius: 5px; -} - -#mocha .test.pass.fast .duration { - display: none; -} - -#mocha .test.pending { - color: #0b97c4; -} - -#mocha .test.pending::before { - content: '◦'; - color: #0b97c4; -} - -#mocha .test.fail { - color: #c00; -} - -#mocha .test.fail pre { - color: black; -} - -#mocha .test.fail::before { - content: '✖'; - font-size: 12px; - display: block; - float: left; - margin-right: 5px; - color: #c00; -} - -#mocha .test pre.error { - color: #c00; -} - -#mocha .test pre { - display: inline-block; - font: 12px/1.5 monaco, monospace; - margin: 5px; - padding: 15px; - border: 1px solid #eee; - border-bottom-color: #ddd; - -webkit-border-radius: 3px; - -webkit-box-shadow: 0 1px 3px #eee; -} - -#report.pass .test.fail { - display: none; -} - -#report.fail .test.pass { - display: none; -} - -#error { - color: #c00; - font-size: 1.5 em; - font-weight: 100; - letter-spacing: 1px; -} - -#stats { - position: fixed; - top: 15px; - right: 10px; - font-size: 12px; - margin: 0; - color: #888; -} - -#stats .progress { - float: right; - padding-top: 0; -} - -#stats em { - color: black; -} - -#stats a { - text-decoration: none; - color: inherit; -} - -#stats a:hover { - border-bottom: 1px solid #eee; -} - -#stats li { - display: inline-block; - margin: 0 5px; - list-style: none; - padding-top: 11px; -} - -code .comment { color: #ddd } -code .init { color: #2F6FAD } -code .string { color: #5890AD } -code .keyword { color: #8A6343 } -code .number { color: #2F6FAD } diff --git a/test/lib/chai.js b/test/lib/chai.js deleted file mode 100644 index 7c9017149ee..00000000000 --- a/test/lib/chai.js +++ /dev/null @@ -1,5332 +0,0 @@ - -;(function(){ - -/** - * Require the module at `name`. - * - * @param {String} name - * @return {Object} exports - * @api public - */ - -function require(name) { - var module = require.modules[name]; - if (!module) throw new Error('failed to require "' + name + '"'); - - if (!('exports' in module) && typeof module.definition === 'function') { - module.client = module.component = true; - module.definition.call(this, module.exports = {}, module); - delete module.definition; - } - - return module.exports; -} - -/** - * Meta info, accessible in the global scope unless you use AMD option. - */ - -require.loader = 'component'; - -/** - * Internal helper object, contains a sorting function for semantiv versioning - */ -require.helper = {}; -require.helper.semVerSort = function(a, b) { - var aArray = a.version.split('.'); - var bArray = b.version.split('.'); - for (var i=0; i bLex ? 1 : -1; - continue; - } else if (aInt > bInt) { - return 1; - } else { - return -1; - } - } - return 0; -} - -/** - * Find and require a module which name starts with the provided name. - * If multiple modules exists, the highest semver is used. - * This function can only be used for remote dependencies. - - * @param {String} name - module name: `user~repo` - * @param {Boolean} returnPath - returns the canonical require path if true, - * otherwise it returns the epxorted module - */ -require.latest = function (name, returnPath) { - function showError(name) { - throw new Error('failed to find latest module of "' + name + '"'); - } - // only remotes with semvers, ignore local files conataining a '/' - var versionRegexp = /(.*)~(.*)@v?(\d+\.\d+\.\d+[^\/]*)$/; - var remoteRegexp = /(.*)~(.*)/; - if (!remoteRegexp.test(name)) showError(name); - var moduleNames = Object.keys(require.modules); - var semVerCandidates = []; - var otherCandidates = []; // for instance: name of the git branch - for (var i=0; i 0) { - var module = semVerCandidates.sort(require.helper.semVerSort).pop().name; - if (returnPath === true) { - return module; - } - return require(module); - } - // if the build contains more than one branch of the same module - // you should not use this funciton - var module = otherCandidates.sort(function(a, b) {return a.name > b.name})[0].name; - if (returnPath === true) { - return module; - } - return require(module); -} - -/** - * Registered modules. - */ - -require.modules = {}; - -/** - * Register module at `name` with callback `definition`. - * - * @param {String} name - * @param {Function} definition - * @api private - */ - -require.register = function (name, definition) { - require.modules[name] = { - definition: definition - }; -}; - -/** - * Define a module's exports immediately with `exports`. - * - * @param {String} name - * @param {Generic} exports - * @api private - */ - -require.define = function (name, exports) { - require.modules[name] = { - exports: exports - }; -}; -require.register("chaijs~assertion-error@1.0.0", function (exports, module) { -/*! - * assertion-error - * Copyright(c) 2013 Jake Luer - * MIT Licensed - */ - -/*! - * Return a function that will copy properties from - * one object to another excluding any originally - * listed. Returned function will create a new `{}`. - * - * @param {String} excluded properties ... - * @return {Function} - */ - -function exclude () { - var excludes = [].slice.call(arguments); - - function excludeProps (res, obj) { - Object.keys(obj).forEach(function (key) { - if (!~excludes.indexOf(key)) res[key] = obj[key]; - }); - } - - return function extendExclude () { - var args = [].slice.call(arguments) - , i = 0 - , res = {}; - - for (; i < args.length; i++) { - excludeProps(res, args[i]); - } - - return res; - }; -}; - -/*! - * Primary Exports - */ - -module.exports = AssertionError; - -/** - * ### AssertionError - * - * An extension of the JavaScript `Error` constructor for - * assertion and validation scenarios. - * - * @param {String} message - * @param {Object} properties to include (optional) - * @param {callee} start stack function (optional) - */ - -function AssertionError (message, _props, ssf) { - var extend = exclude('name', 'message', 'stack', 'constructor', 'toJSON') - , props = extend(_props || {}); - - // default values - this.message = message || 'Unspecified AssertionError'; - this.showDiff = false; - - // copy from properties - for (var key in props) { - this[key] = props[key]; - } - - // capture stack trace - ssf = ssf || arguments.callee; - if (ssf && Error.captureStackTrace) { - Error.captureStackTrace(this, ssf); - } -} - -/*! - * Inherit from Error.prototype - */ - -AssertionError.prototype = Object.create(Error.prototype); - -/*! - * Statically set name - */ - -AssertionError.prototype.name = 'AssertionError'; - -/*! - * Ensure correct constructor - */ - -AssertionError.prototype.constructor = AssertionError; - -/** - * Allow errors to be converted to JSON for static transfer. - * - * @param {Boolean} include stack (default: `true`) - * @return {Object} object that can be `JSON.stringify` - */ - -AssertionError.prototype.toJSON = function (stack) { - var extend = exclude('constructor', 'toJSON', 'stack') - , props = extend({ name: this.name }, this); - - // include stack if exists and not turned off - if (false !== stack && this.stack) { - props.stack = this.stack; - } - - return props; -}; - -}); - -require.register("chaijs~type-detect@0.1.1", function (exports, module) { -/*! - * type-detect - * Copyright(c) 2013 jake luer - * MIT Licensed - */ - -/*! - * Primary Exports - */ - -var exports = module.exports = getType; - -/*! - * Detectable javascript natives - */ - -var natives = { - '[object Array]': 'array' - , '[object RegExp]': 'regexp' - , '[object Function]': 'function' - , '[object Arguments]': 'arguments' - , '[object Date]': 'date' -}; - -/** - * ### typeOf (obj) - * - * Use several different techniques to determine - * the type of object being tested. - * - * - * @param {Mixed} object - * @return {String} object type - * @api public - */ - -function getType (obj) { - var str = Object.prototype.toString.call(obj); - if (natives[str]) return natives[str]; - if (obj === null) return 'null'; - if (obj === undefined) return 'undefined'; - if (obj === Object(obj)) return 'object'; - return typeof obj; -} - -exports.Library = Library; - -/** - * ### Library - * - * Create a repository for custom type detection. - * - * ```js - * var lib = new type.Library; - * ``` - * - */ - -function Library () { - this.tests = {}; -} - -/** - * #### .of (obj) - * - * Expose replacement `typeof` detection to the library. - * - * ```js - * if ('string' === lib.of('hello world')) { - * // ... - * } - * ``` - * - * @param {Mixed} object to test - * @return {String} type - */ - -Library.prototype.of = getType; - -/** - * #### .define (type, test) - * - * Add a test to for the `.test()` assertion. - * - * Can be defined as a regular expression: - * - * ```js - * lib.define('int', /^[0-9]+$/); - * ``` - * - * ... or as a function: - * - * ```js - * lib.define('bln', function (obj) { - * if ('boolean' === lib.of(obj)) return true; - * var blns = [ 'yes', 'no', 'true', 'false', 1, 0 ]; - * if ('string' === lib.of(obj)) obj = obj.toLowerCase(); - * return !! ~blns.indexOf(obj); - * }); - * ``` - * - * @param {String} type - * @param {RegExp|Function} test - * @api public - */ - -Library.prototype.define = function (type, test) { - if (arguments.length === 1) return this.tests[type]; - this.tests[type] = test; - return this; -}; - -/** - * #### .test (obj, test) - * - * Assert that an object is of type. Will first - * check natives, and if that does not pass it will - * use the user defined custom tests. - * - * ```js - * assert(lib.test('1', 'int')); - * assert(lib.test('yes', 'bln')); - * ``` - * - * @param {Mixed} object - * @param {String} type - * @return {Boolean} result - * @api public - */ - -Library.prototype.test = function (obj, type) { - if (type === getType(obj)) return true; - var test = this.tests[type]; - - if (test && 'regexp' === getType(test)) { - return test.test(obj); - } else if (test && 'function' === getType(test)) { - return test(obj); - } else { - throw new ReferenceError('Type test "' + type + '" not defined or invalid.'); - } -}; - -}); - -require.register("chaijs~deep-eql@0.1.3", function (exports, module) { -/*! - * deep-eql - * Copyright(c) 2013 Jake Luer - * MIT Licensed - */ - -/*! - * Module dependencies - */ - -var type = require('chaijs~type-detect@0.1.1'); - -/*! - * Buffer.isBuffer browser shim - */ - -var Buffer; -try { Buffer = require('buffer').Buffer; } -catch(ex) { - Buffer = {}; - Buffer.isBuffer = function() { return false; } -} - -/*! - * Primary Export - */ - -module.exports = deepEqual; - -/** - * Assert super-strict (egal) equality between - * two objects of any type. - * - * @param {Mixed} a - * @param {Mixed} b - * @param {Array} memoised (optional) - * @return {Boolean} equal match - */ - -function deepEqual(a, b, m) { - if (sameValue(a, b)) { - return true; - } else if ('date' === type(a)) { - return dateEqual(a, b); - } else if ('regexp' === type(a)) { - return regexpEqual(a, b); - } else if (Buffer.isBuffer(a)) { - return bufferEqual(a, b); - } else if ('arguments' === type(a)) { - return argumentsEqual(a, b, m); - } else if (!typeEqual(a, b)) { - return false; - } else if (('object' !== type(a) && 'object' !== type(b)) - && ('array' !== type(a) && 'array' !== type(b))) { - return sameValue(a, b); - } else { - return objectEqual(a, b, m); - } -} - -/*! - * Strict (egal) equality test. Ensures that NaN always - * equals NaN and `-0` does not equal `+0`. - * - * @param {Mixed} a - * @param {Mixed} b - * @return {Boolean} equal match - */ - -function sameValue(a, b) { - if (a === b) return a !== 0 || 1 / a === 1 / b; - return a !== a && b !== b; -} - -/*! - * Compare the types of two given objects and - * return if they are equal. Note that an Array - * has a type of `array` (not `object`) and arguments - * have a type of `arguments` (not `array`/`object`). - * - * @param {Mixed} a - * @param {Mixed} b - * @return {Boolean} result - */ - -function typeEqual(a, b) { - return type(a) === type(b); -} - -/*! - * Compare two Date objects by asserting that - * the time values are equal using `saveValue`. - * - * @param {Date} a - * @param {Date} b - * @return {Boolean} result - */ - -function dateEqual(a, b) { - if ('date' !== type(b)) return false; - return sameValue(a.getTime(), b.getTime()); -} - -/*! - * Compare two regular expressions by converting them - * to string and checking for `sameValue`. - * - * @param {RegExp} a - * @param {RegExp} b - * @return {Boolean} result - */ - -function regexpEqual(a, b) { - if ('regexp' !== type(b)) return false; - return sameValue(a.toString(), b.toString()); -} - -/*! - * Assert deep equality of two `arguments` objects. - * Unfortunately, these must be sliced to arrays - * prior to test to ensure no bad behavior. - * - * @param {Arguments} a - * @param {Arguments} b - * @param {Array} memoize (optional) - * @return {Boolean} result - */ - -function argumentsEqual(a, b, m) { - if ('arguments' !== type(b)) return false; - a = [].slice.call(a); - b = [].slice.call(b); - return deepEqual(a, b, m); -} - -/*! - * Get enumerable properties of a given object. - * - * @param {Object} a - * @return {Array} property names - */ - -function enumerable(a) { - var res = []; - for (var key in a) res.push(key); - return res; -} - -/*! - * Simple equality for flat iterable objects - * such as Arrays or Node.js buffers. - * - * @param {Iterable} a - * @param {Iterable} b - * @return {Boolean} result - */ - -function iterableEqual(a, b) { - if (a.length !== b.length) return false; - - var i = 0; - var match = true; - - for (; i < a.length; i++) { - if (a[i] !== b[i]) { - match = false; - break; - } - } - - return match; -} - -/*! - * Extension to `iterableEqual` specifically - * for Node.js Buffers. - * - * @param {Buffer} a - * @param {Mixed} b - * @return {Boolean} result - */ - -function bufferEqual(a, b) { - if (!Buffer.isBuffer(b)) return false; - return iterableEqual(a, b); -} - -/*! - * Block for `objectEqual` ensuring non-existing - * values don't get in. - * - * @param {Mixed} object - * @return {Boolean} result - */ - -function isValue(a) { - return a !== null && a !== undefined; -} - -/*! - * Recursively check the equality of two objects. - * Once basic sameness has been established it will - * defer to `deepEqual` for each enumerable key - * in the object. - * - * @param {Mixed} a - * @param {Mixed} b - * @return {Boolean} result - */ - -function objectEqual(a, b, m) { - if (!isValue(a) || !isValue(b)) { - return false; - } - - if (a.prototype !== b.prototype) { - return false; - } - - var i; - if (m) { - for (i = 0; i < m.length; i++) { - if ((m[i][0] === a && m[i][1] === b) - || (m[i][0] === b && m[i][1] === a)) { - return true; - } - } - } else { - m = []; - } - - try { - var ka = enumerable(a); - var kb = enumerable(b); - } catch (ex) { - return false; - } - - ka.sort(); - kb.sort(); - - if (!iterableEqual(ka, kb)) { - return false; - } - - m.push([ a, b ]); - - var key; - for (i = ka.length - 1; i >= 0; i--) { - key = ka[i]; - if (!deepEqual(a[key], b[key], m)) { - return false; - } - } - - return true; -} - -}); - -require.register("chai", function (exports, module) { -module.exports = require('chai/lib/chai.js'); - -}); - -require.register("chai/lib/chai.js", function (exports, module) { -/*! - * chai - * Copyright(c) 2011-2014 Jake Luer - * MIT Licensed - */ - -var used = [] - , exports = module.exports = {}; - -/*! - * Chai version - */ - -exports.version = '2.1.0'; - -/*! - * Assertion Error - */ - -exports.AssertionError = require('chaijs~assertion-error@1.0.0'); - -/*! - * Utils for plugins (not exported) - */ - -var util = require('chai/lib/chai/utils/index.js'); - -/** - * # .use(function) - * - * Provides a way to extend the internals of Chai - * - * @param {Function} - * @returns {this} for chaining - * @api public - */ - -exports.use = function (fn) { - if (!~used.indexOf(fn)) { - fn(this, util); - used.push(fn); - } - - return this; -}; - -/*! - * Utility Functions - */ - -exports.util = util; - -/*! - * Configuration - */ - -var config = require('chai/lib/chai/config.js'); -exports.config = config; - -/*! - * Primary `Assertion` prototype - */ - -var assertion = require('chai/lib/chai/assertion.js'); -exports.use(assertion); - -/*! - * Core Assertions - */ - -var core = require('chai/lib/chai/core/assertions.js'); -exports.use(core); - -/*! - * Expect interface - */ - -var expect = require('chai/lib/chai/interface/expect.js'); -exports.use(expect); - -/*! - * Should interface - */ - -var should = require('chai/lib/chai/interface/should.js'); -exports.use(should); - -/*! - * Assert interface - */ - -var assert = require('chai/lib/chai/interface/assert.js'); -exports.use(assert); - -}); - -require.register("chai/lib/chai/assertion.js", function (exports, module) { -/*! - * chai - * http://chaijs.com - * Copyright(c) 2011-2014 Jake Luer - * MIT Licensed - */ - -var config = require('chai/lib/chai/config.js'); - -module.exports = function (_chai, util) { - /*! - * Module dependencies. - */ - - var AssertionError = _chai.AssertionError - , flag = util.flag; - - /*! - * Module export. - */ - - _chai.Assertion = Assertion; - - /*! - * Assertion Constructor - * - * Creates object for chaining. - * - * @api private - */ - - function Assertion (obj, msg, stack) { - flag(this, 'ssfi', stack || arguments.callee); - flag(this, 'object', obj); - flag(this, 'message', msg); - } - - Object.defineProperty(Assertion, 'includeStack', { - get: function() { - console.warn('Assertion.includeStack is deprecated, use chai.config.includeStack instead.'); - return config.includeStack; - }, - set: function(value) { - console.warn('Assertion.includeStack is deprecated, use chai.config.includeStack instead.'); - config.includeStack = value; - } - }); - - Object.defineProperty(Assertion, 'showDiff', { - get: function() { - console.warn('Assertion.showDiff is deprecated, use chai.config.showDiff instead.'); - return config.showDiff; - }, - set: function(value) { - console.warn('Assertion.showDiff is deprecated, use chai.config.showDiff instead.'); - config.showDiff = value; - } - }); - - Assertion.addProperty = function (name, fn) { - util.addProperty(this.prototype, name, fn); - }; - - Assertion.addMethod = function (name, fn) { - util.addMethod(this.prototype, name, fn); - }; - - Assertion.addChainableMethod = function (name, fn, chainingBehavior) { - util.addChainableMethod(this.prototype, name, fn, chainingBehavior); - }; - - Assertion.overwriteProperty = function (name, fn) { - util.overwriteProperty(this.prototype, name, fn); - }; - - Assertion.overwriteMethod = function (name, fn) { - util.overwriteMethod(this.prototype, name, fn); - }; - - Assertion.overwriteChainableMethod = function (name, fn, chainingBehavior) { - util.overwriteChainableMethod(this.prototype, name, fn, chainingBehavior); - }; - - /*! - * ### .assert(expression, message, negateMessage, expected, actual) - * - * Executes an expression and check expectations. Throws AssertionError for reporting if test doesn't pass. - * - * @name assert - * @param {Philosophical} expression to be tested - * @param {String or Function} message or function that returns message to display if fails - * @param {String or Function} negatedMessage or function that returns negatedMessage to display if negated expression fails - * @param {Mixed} expected value (remember to check for negation) - * @param {Mixed} actual (optional) will default to `this.obj` - * @api private - */ - - Assertion.prototype.assert = function (expr, msg, negateMsg, expected, _actual, showDiff) { - var ok = util.test(this, arguments); - if (true !== showDiff) showDiff = false; - if (true !== config.showDiff) showDiff = false; - - if (!ok) { - var msg = util.getMessage(this, arguments) - , actual = util.getActual(this, arguments); - throw new AssertionError(msg, { - actual: actual - , expected: expected - , showDiff: showDiff - }, (config.includeStack) ? this.assert : flag(this, 'ssfi')); - } - }; - - /*! - * ### ._obj - * - * Quick reference to stored `actual` value for plugin developers. - * - * @api private - */ - - Object.defineProperty(Assertion.prototype, '_obj', - { get: function () { - return flag(this, 'object'); - } - , set: function (val) { - flag(this, 'object', val); - } - }); -}; - -}); - -require.register("chai/lib/chai/config.js", function (exports, module) { -module.exports = { - - /** - * ### config.includeStack - * - * User configurable property, influences whether stack trace - * is included in Assertion error message. Default of false - * suppresses stack trace in the error message. - * - * chai.config.includeStack = true; // enable stack on error - * - * @param {Boolean} - * @api public - */ - - includeStack: false, - - /** - * ### config.showDiff - * - * User configurable property, influences whether or not - * the `showDiff` flag should be included in the thrown - * AssertionErrors. `false` will always be `false`; `true` - * will be true when the assertion has requested a diff - * be shown. - * - * @param {Boolean} - * @api public - */ - - showDiff: true, - - /** - * ### config.truncateThreshold - * - * User configurable property, sets length threshold for actual and - * expected values in assertion errors. If this threshold is exceeded, - * the value is truncated. - * - * Set it to zero if you want to disable truncating altogether. - * - * chai.config.truncateThreshold = 0; // disable truncating - * - * @param {Number} - * @api public - */ - - truncateThreshold: 40 - -}; - -}); - -require.register("chai/lib/chai/core/assertions.js", function (exports, module) { -/*! - * chai - * http://chaijs.com - * Copyright(c) 2011-2014 Jake Luer - * MIT Licensed - */ - -module.exports = function (chai, _) { - var Assertion = chai.Assertion - , toString = Object.prototype.toString - , flag = _.flag; - - /** - * ### Language Chains - * - * The following are provided as chainable getters to - * improve the readability of your assertions. They - * do not provide testing capabilities unless they - * have been overwritten by a plugin. - * - * **Chains** - * - * - to - * - be - * - been - * - is - * - that - * - which - * - and - * - has - * - have - * - with - * - at - * - of - * - same - * - * @name language chains - * @api public - */ - - [ 'to', 'be', 'been' - , 'is', 'and', 'has', 'have' - , 'with', 'that', 'which', 'at' - , 'of', 'same' ].forEach(function (chain) { - Assertion.addProperty(chain, function () { - return this; - }); - }); - - /** - * ### .not - * - * Negates any of assertions following in the chain. - * - * expect(foo).to.not.equal('bar'); - * expect(goodFn).to.not.throw(Error); - * expect({ foo: 'baz' }).to.have.property('foo') - * .and.not.equal('bar'); - * - * @name not - * @api public - */ - - Assertion.addProperty('not', function () { - flag(this, 'negate', true); - }); - - /** - * ### .deep - * - * Sets the `deep` flag, later used by the `equal` and - * `property` assertions. - * - * expect(foo).to.deep.equal({ bar: 'baz' }); - * expect({ foo: { bar: { baz: 'quux' } } }) - * .to.have.deep.property('foo.bar.baz', 'quux'); - * - * @name deep - * @api public - */ - - Assertion.addProperty('deep', function () { - flag(this, 'deep', true); - }); - - /** - * ### .any - * - * Sets the `any` flag, (opposite of the `all` flag) - * later used in the `keys` assertion. - * - * expect(foo).to.have.any.keys('bar', 'baz'); - * - * @name any - * @api public - */ - - Assertion.addProperty('any', function () { - flag(this, 'any', true); - flag(this, 'all', false) - }); - - - /** - * ### .all - * - * Sets the `all` flag (opposite of the `any` flag) - * later used by the `keys` assertion. - * - * expect(foo).to.have.all.keys('bar', 'baz'); - * - * @name all - * @api public - */ - - Assertion.addProperty('all', function () { - flag(this, 'all', true); - flag(this, 'any', false); - }); - - /** - * ### .a(type) - * - * The `a` and `an` assertions are aliases that can be - * used either as language chains or to assert a value's - * type. - * - * // typeof - * expect('test').to.be.a('string'); - * expect({ foo: 'bar' }).to.be.an('object'); - * expect(null).to.be.a('null'); - * expect(undefined).to.be.an('undefined'); - * - * // language chain - * expect(foo).to.be.an.instanceof(Foo); - * - * @name a - * @alias an - * @param {String} type - * @param {String} message _optional_ - * @api public - */ - - function an (type, msg) { - if (msg) flag(this, 'message', msg); - type = type.toLowerCase(); - var obj = flag(this, 'object') - , article = ~[ 'a', 'e', 'i', 'o', 'u' ].indexOf(type.charAt(0)) ? 'an ' : 'a '; - - this.assert( - type === _.type(obj) - , 'expected #{this} to be ' + article + type - , 'expected #{this} not to be ' + article + type - ); - } - - Assertion.addChainableMethod('an', an); - Assertion.addChainableMethod('a', an); - - /** - * ### .include(value) - * - * The `include` and `contain` assertions can be used as either property - * based language chains or as methods to assert the inclusion of an object - * in an array or a substring in a string. When used as language chains, - * they toggle the `contains` flag for the `keys` assertion. - * - * expect([1,2,3]).to.include(2); - * expect('foobar').to.contain('foo'); - * expect({ foo: 'bar', hello: 'universe' }).to.include.keys('foo'); - * - * @name include - * @alias contain - * @alias includes - * @alias contains - * @param {Object|String|Number} obj - * @param {String} message _optional_ - * @api public - */ - - function includeChainingBehavior () { - flag(this, 'contains', true); - } - - function include (val, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - var expected = false; - if (_.type(obj) === 'array' && _.type(val) === 'object') { - for (var i in obj) { - if (_.eql(obj[i], val)) { - expected = true; - break; - } - } - } else if (_.type(val) === 'object') { - if (!flag(this, 'negate')) { - for (var k in val) new Assertion(obj).property(k, val[k]); - return; - } - var subset = {}; - for (var k in val) subset[k] = obj[k]; - expected = _.eql(subset, val); - } else { - expected = obj && ~obj.indexOf(val); - } - this.assert( - expected - , 'expected #{this} to include ' + _.inspect(val) - , 'expected #{this} to not include ' + _.inspect(val)); - } - - Assertion.addChainableMethod('include', include, includeChainingBehavior); - Assertion.addChainableMethod('contain', include, includeChainingBehavior); - Assertion.addChainableMethod('contains', include, includeChainingBehavior); - Assertion.addChainableMethod('includes', include, includeChainingBehavior); - - /** - * ### .ok - * - * Asserts that the target is truthy. - * - * expect('everthing').to.be.ok; - * expect(1).to.be.ok; - * expect(false).to.not.be.ok; - * expect(undefined).to.not.be.ok; - * expect(null).to.not.be.ok; - * - * @name ok - * @api public - */ - - Assertion.addProperty('ok', function () { - this.assert( - flag(this, 'object') - , 'expected #{this} to be truthy' - , 'expected #{this} to be falsy'); - }); - - /** - * ### .true - * - * Asserts that the target is `true`. - * - * expect(true).to.be.true; - * expect(1).to.not.be.true; - * - * @name true - * @api public - */ - - Assertion.addProperty('true', function () { - this.assert( - true === flag(this, 'object') - , 'expected #{this} to be true' - , 'expected #{this} to be false' - , this.negate ? false : true - ); - }); - - /** - * ### .false - * - * Asserts that the target is `false`. - * - * expect(false).to.be.false; - * expect(0).to.not.be.false; - * - * @name false - * @api public - */ - - Assertion.addProperty('false', function () { - this.assert( - false === flag(this, 'object') - , 'expected #{this} to be false' - , 'expected #{this} to be true' - , this.negate ? true : false - ); - }); - - /** - * ### .null - * - * Asserts that the target is `null`. - * - * expect(null).to.be.null; - * expect(undefined).not.to.be.null; - * - * @name null - * @api public - */ - - Assertion.addProperty('null', function () { - this.assert( - null === flag(this, 'object') - , 'expected #{this} to be null' - , 'expected #{this} not to be null' - ); - }); - - /** - * ### .undefined - * - * Asserts that the target is `undefined`. - * - * expect(undefined).to.be.undefined; - * expect(null).to.not.be.undefined; - * - * @name undefined - * @api public - */ - - Assertion.addProperty('undefined', function () { - this.assert( - undefined === flag(this, 'object') - , 'expected #{this} to be undefined' - , 'expected #{this} not to be undefined' - ); - }); - - /** - * ### .exist - * - * Asserts that the target is neither `null` nor `undefined`. - * - * var foo = 'hi' - * , bar = null - * , baz; - * - * expect(foo).to.exist; - * expect(bar).to.not.exist; - * expect(baz).to.not.exist; - * - * @name exist - * @api public - */ - - Assertion.addProperty('exist', function () { - this.assert( - null != flag(this, 'object') - , 'expected #{this} to exist' - , 'expected #{this} to not exist' - ); - }); - - - /** - * ### .empty - * - * Asserts that the target's length is `0`. For arrays, it checks - * the `length` property. For objects, it gets the count of - * enumerable keys. - * - * expect([]).to.be.empty; - * expect('').to.be.empty; - * expect({}).to.be.empty; - * - * @name empty - * @api public - */ - - Assertion.addProperty('empty', function () { - var obj = flag(this, 'object') - , expected = obj; - - if (Array.isArray(obj) || 'string' === typeof object) { - expected = obj.length; - } else if (typeof obj === 'object') { - expected = Object.keys(obj).length; - } - - this.assert( - !expected - , 'expected #{this} to be empty' - , 'expected #{this} not to be empty' - ); - }); - - /** - * ### .arguments - * - * Asserts that the target is an arguments object. - * - * function test () { - * expect(arguments).to.be.arguments; - * } - * - * @name arguments - * @alias Arguments - * @api public - */ - - function checkArguments () { - var obj = flag(this, 'object') - , type = Object.prototype.toString.call(obj); - this.assert( - '[object Arguments]' === type - , 'expected #{this} to be arguments but got ' + type - , 'expected #{this} to not be arguments' - ); - } - - Assertion.addProperty('arguments', checkArguments); - Assertion.addProperty('Arguments', checkArguments); - - /** - * ### .equal(value) - * - * Asserts that the target is strictly equal (`===`) to `value`. - * Alternately, if the `deep` flag is set, asserts that - * the target is deeply equal to `value`. - * - * expect('hello').to.equal('hello'); - * expect(42).to.equal(42); - * expect(1).to.not.equal(true); - * expect({ foo: 'bar' }).to.not.equal({ foo: 'bar' }); - * expect({ foo: 'bar' }).to.deep.equal({ foo: 'bar' }); - * - * @name equal - * @alias equals - * @alias eq - * @alias deep.equal - * @param {Mixed} value - * @param {String} message _optional_ - * @api public - */ - - function assertEqual (val, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - if (flag(this, 'deep')) { - return this.eql(val); - } else { - this.assert( - val === obj - , 'expected #{this} to equal #{exp}' - , 'expected #{this} to not equal #{exp}' - , val - , this._obj - , true - ); - } - } - - Assertion.addMethod('equal', assertEqual); - Assertion.addMethod('equals', assertEqual); - Assertion.addMethod('eq', assertEqual); - - /** - * ### .eql(value) - * - * Asserts that the target is deeply equal to `value`. - * - * expect({ foo: 'bar' }).to.eql({ foo: 'bar' }); - * expect([ 1, 2, 3 ]).to.eql([ 1, 2, 3 ]); - * - * @name eql - * @alias eqls - * @param {Mixed} value - * @param {String} message _optional_ - * @api public - */ - - function assertEql(obj, msg) { - if (msg) flag(this, 'message', msg); - this.assert( - _.eql(obj, flag(this, 'object')) - , 'expected #{this} to deeply equal #{exp}' - , 'expected #{this} to not deeply equal #{exp}' - , obj - , this._obj - , true - ); - } - - Assertion.addMethod('eql', assertEql); - Assertion.addMethod('eqls', assertEql); - - /** - * ### .above(value) - * - * Asserts that the target is greater than `value`. - * - * expect(10).to.be.above(5); - * - * Can also be used in conjunction with `length` to - * assert a minimum length. The benefit being a - * more informative error message than if the length - * was supplied directly. - * - * expect('foo').to.have.length.above(2); - * expect([ 1, 2, 3 ]).to.have.length.above(2); - * - * @name above - * @alias gt - * @alias greaterThan - * @param {Number} value - * @param {String} message _optional_ - * @api public - */ - - function assertAbove (n, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - if (flag(this, 'doLength')) { - new Assertion(obj, msg).to.have.property('length'); - var len = obj.length; - this.assert( - len > n - , 'expected #{this} to have a length above #{exp} but got #{act}' - , 'expected #{this} to not have a length above #{exp}' - , n - , len - ); - } else { - this.assert( - obj > n - , 'expected #{this} to be above ' + n - , 'expected #{this} to be at most ' + n - ); - } - } - - Assertion.addMethod('above', assertAbove); - Assertion.addMethod('gt', assertAbove); - Assertion.addMethod('greaterThan', assertAbove); - - /** - * ### .least(value) - * - * Asserts that the target is greater than or equal to `value`. - * - * expect(10).to.be.at.least(10); - * - * Can also be used in conjunction with `length` to - * assert a minimum length. The benefit being a - * more informative error message than if the length - * was supplied directly. - * - * expect('foo').to.have.length.of.at.least(2); - * expect([ 1, 2, 3 ]).to.have.length.of.at.least(3); - * - * @name least - * @alias gte - * @param {Number} value - * @param {String} message _optional_ - * @api public - */ - - function assertLeast (n, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - if (flag(this, 'doLength')) { - new Assertion(obj, msg).to.have.property('length'); - var len = obj.length; - this.assert( - len >= n - , 'expected #{this} to have a length at least #{exp} but got #{act}' - , 'expected #{this} to have a length below #{exp}' - , n - , len - ); - } else { - this.assert( - obj >= n - , 'expected #{this} to be at least ' + n - , 'expected #{this} to be below ' + n - ); - } - } - - Assertion.addMethod('least', assertLeast); - Assertion.addMethod('gte', assertLeast); - - /** - * ### .below(value) - * - * Asserts that the target is less than `value`. - * - * expect(5).to.be.below(10); - * - * Can also be used in conjunction with `length` to - * assert a maximum length. The benefit being a - * more informative error message than if the length - * was supplied directly. - * - * expect('foo').to.have.length.below(4); - * expect([ 1, 2, 3 ]).to.have.length.below(4); - * - * @name below - * @alias lt - * @alias lessThan - * @param {Number} value - * @param {String} message _optional_ - * @api public - */ - - function assertBelow (n, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - if (flag(this, 'doLength')) { - new Assertion(obj, msg).to.have.property('length'); - var len = obj.length; - this.assert( - len < n - , 'expected #{this} to have a length below #{exp} but got #{act}' - , 'expected #{this} to not have a length below #{exp}' - , n - , len - ); - } else { - this.assert( - obj < n - , 'expected #{this} to be below ' + n - , 'expected #{this} to be at least ' + n - ); - } - } - - Assertion.addMethod('below', assertBelow); - Assertion.addMethod('lt', assertBelow); - Assertion.addMethod('lessThan', assertBelow); - - /** - * ### .most(value) - * - * Asserts that the target is less than or equal to `value`. - * - * expect(5).to.be.at.most(5); - * - * Can also be used in conjunction with `length` to - * assert a maximum length. The benefit being a - * more informative error message than if the length - * was supplied directly. - * - * expect('foo').to.have.length.of.at.most(4); - * expect([ 1, 2, 3 ]).to.have.length.of.at.most(3); - * - * @name most - * @alias lte - * @param {Number} value - * @param {String} message _optional_ - * @api public - */ - - function assertMost (n, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - if (flag(this, 'doLength')) { - new Assertion(obj, msg).to.have.property('length'); - var len = obj.length; - this.assert( - len <= n - , 'expected #{this} to have a length at most #{exp} but got #{act}' - , 'expected #{this} to have a length above #{exp}' - , n - , len - ); - } else { - this.assert( - obj <= n - , 'expected #{this} to be at most ' + n - , 'expected #{this} to be above ' + n - ); - } - } - - Assertion.addMethod('most', assertMost); - Assertion.addMethod('lte', assertMost); - - /** - * ### .within(start, finish) - * - * Asserts that the target is within a range. - * - * expect(7).to.be.within(5,10); - * - * Can also be used in conjunction with `length` to - * assert a length range. The benefit being a - * more informative error message than if the length - * was supplied directly. - * - * expect('foo').to.have.length.within(2,4); - * expect([ 1, 2, 3 ]).to.have.length.within(2,4); - * - * @name within - * @param {Number} start lowerbound inclusive - * @param {Number} finish upperbound inclusive - * @param {String} message _optional_ - * @api public - */ - - Assertion.addMethod('within', function (start, finish, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object') - , range = start + '..' + finish; - if (flag(this, 'doLength')) { - new Assertion(obj, msg).to.have.property('length'); - var len = obj.length; - this.assert( - len >= start && len <= finish - , 'expected #{this} to have a length within ' + range - , 'expected #{this} to not have a length within ' + range - ); - } else { - this.assert( - obj >= start && obj <= finish - , 'expected #{this} to be within ' + range - , 'expected #{this} to not be within ' + range - ); - } - }); - - /** - * ### .instanceof(constructor) - * - * Asserts that the target is an instance of `constructor`. - * - * var Tea = function (name) { this.name = name; } - * , Chai = new Tea('chai'); - * - * expect(Chai).to.be.an.instanceof(Tea); - * expect([ 1, 2, 3 ]).to.be.instanceof(Array); - * - * @name instanceof - * @param {Constructor} constructor - * @param {String} message _optional_ - * @alias instanceOf - * @api public - */ - - function assertInstanceOf (constructor, msg) { - if (msg) flag(this, 'message', msg); - var name = _.getName(constructor); - this.assert( - flag(this, 'object') instanceof constructor - , 'expected #{this} to be an instance of ' + name - , 'expected #{this} to not be an instance of ' + name - ); - }; - - Assertion.addMethod('instanceof', assertInstanceOf); - Assertion.addMethod('instanceOf', assertInstanceOf); - - /** - * ### .property(name, [value]) - * - * Asserts that the target has a property `name`, optionally asserting that - * the value of that property is strictly equal to `value`. - * If the `deep` flag is set, you can use dot- and bracket-notation for deep - * references into objects and arrays. - * - * // simple referencing - * var obj = { foo: 'bar' }; - * expect(obj).to.have.property('foo'); - * expect(obj).to.have.property('foo', 'bar'); - * - * // deep referencing - * var deepObj = { - * green: { tea: 'matcha' } - * , teas: [ 'chai', 'matcha', { tea: 'konacha' } ] - * }; - - * expect(deepObj).to.have.deep.property('green.tea', 'matcha'); - * expect(deepObj).to.have.deep.property('teas[1]', 'matcha'); - * expect(deepObj).to.have.deep.property('teas[2].tea', 'konacha'); - * - * You can also use an array as the starting point of a `deep.property` - * assertion, or traverse nested arrays. - * - * var arr = [ - * [ 'chai', 'matcha', 'konacha' ] - * , [ { tea: 'chai' } - * , { tea: 'matcha' } - * , { tea: 'konacha' } ] - * ]; - * - * expect(arr).to.have.deep.property('[0][1]', 'matcha'); - * expect(arr).to.have.deep.property('[1][2].tea', 'konacha'); - * - * Furthermore, `property` changes the subject of the assertion - * to be the value of that property from the original object. This - * permits for further chainable assertions on that property. - * - * expect(obj).to.have.property('foo') - * .that.is.a('string'); - * expect(deepObj).to.have.property('green') - * .that.is.an('object') - * .that.deep.equals({ tea: 'matcha' }); - * expect(deepObj).to.have.property('teas') - * .that.is.an('array') - * .with.deep.property('[2]') - * .that.deep.equals({ tea: 'konacha' }); - * - * @name property - * @alias deep.property - * @param {String} name - * @param {Mixed} value (optional) - * @param {String} message _optional_ - * @returns value of property for chaining - * @api public - */ - - Assertion.addMethod('property', function (name, val, msg) { - if (msg) flag(this, 'message', msg); - - var isDeep = !!flag(this, 'deep') - , descriptor = isDeep ? 'deep property ' : 'property ' - , negate = flag(this, 'negate') - , obj = flag(this, 'object') - , pathInfo = isDeep ? _.getPathInfo(name, obj) : null - , hasProperty = isDeep - ? pathInfo.exists - : _.hasProperty(name, obj) - , value = isDeep - ? pathInfo.value - : obj[name]; - - if (negate && undefined !== val) { - if (undefined === value) { - msg = (msg != null) ? msg + ': ' : ''; - throw new Error(msg + _.inspect(obj) + ' has no ' + descriptor + _.inspect(name)); - } - } else { - this.assert( - hasProperty - , 'expected #{this} to have a ' + descriptor + _.inspect(name) - , 'expected #{this} to not have ' + descriptor + _.inspect(name)); - } - - if (undefined !== val) { - this.assert( - val === value - , 'expected #{this} to have a ' + descriptor + _.inspect(name) + ' of #{exp}, but got #{act}' - , 'expected #{this} to not have a ' + descriptor + _.inspect(name) + ' of #{act}' - , val - , value - ); - } - - flag(this, 'object', value); - }); - - - /** - * ### .ownProperty(name) - * - * Asserts that the target has an own property `name`. - * - * expect('test').to.have.ownProperty('length'); - * - * @name ownProperty - * @alias haveOwnProperty - * @param {String} name - * @param {String} message _optional_ - * @api public - */ - - function assertOwnProperty (name, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - this.assert( - obj.hasOwnProperty(name) - , 'expected #{this} to have own property ' + _.inspect(name) - , 'expected #{this} to not have own property ' + _.inspect(name) - ); - } - - Assertion.addMethod('ownProperty', assertOwnProperty); - Assertion.addMethod('haveOwnProperty', assertOwnProperty); - - /** - * ### .length(value) - * - * Asserts that the target's `length` property has - * the expected value. - * - * expect([ 1, 2, 3]).to.have.length(3); - * expect('foobar').to.have.length(6); - * - * Can also be used as a chain precursor to a value - * comparison for the length property. - * - * expect('foo').to.have.length.above(2); - * expect([ 1, 2, 3 ]).to.have.length.above(2); - * expect('foo').to.have.length.below(4); - * expect([ 1, 2, 3 ]).to.have.length.below(4); - * expect('foo').to.have.length.within(2,4); - * expect([ 1, 2, 3 ]).to.have.length.within(2,4); - * - * @name length - * @alias lengthOf - * @param {Number} length - * @param {String} message _optional_ - * @api public - */ - - function assertLengthChain () { - flag(this, 'doLength', true); - } - - function assertLength (n, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - new Assertion(obj, msg).to.have.property('length'); - var len = obj.length; - - this.assert( - len == n - , 'expected #{this} to have a length of #{exp} but got #{act}' - , 'expected #{this} to not have a length of #{act}' - , n - , len - ); - } - - Assertion.addChainableMethod('length', assertLength, assertLengthChain); - Assertion.addMethod('lengthOf', assertLength); - - /** - * ### .match(regexp) - * - * Asserts that the target matches a regular expression. - * - * expect('foobar').to.match(/^foo/); - * - * @name match - * @param {RegExp} RegularExpression - * @param {String} message _optional_ - * @api public - */ - - Assertion.addMethod('match', function (re, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - this.assert( - re.exec(obj) - , 'expected #{this} to match ' + re - , 'expected #{this} not to match ' + re - ); - }); - - /** - * ### .string(string) - * - * Asserts that the string target contains another string. - * - * expect('foobar').to.have.string('bar'); - * - * @name string - * @param {String} string - * @param {String} message _optional_ - * @api public - */ - - Assertion.addMethod('string', function (str, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - new Assertion(obj, msg).is.a('string'); - - this.assert( - ~obj.indexOf(str) - , 'expected #{this} to contain ' + _.inspect(str) - , 'expected #{this} to not contain ' + _.inspect(str) - ); - }); - - - /** - * ### .keys(key1, [key2], [...]) - * - * Asserts that the target contains any or all of the passed-in keys. - * Use in combination with `any`, `all`, `contains`, or `have` will affect - * what will pass. - * - * When used in conjunction with `any`, at least one key that is passed - * in must exist in the target object. This is regardless whether or not - * the `have` or `contain` qualifiers are used. Note, either `any` or `all` - * should be used in the assertion. If neither are used, the assertion is - * defaulted to `all`. - * - * When both `all` and `contain` are used, the target object must have at - * least all of the passed-in keys but may have more keys not listed. - * - * When both `all` and `have` are used, the target object must both contain - * all of the passed-in keys AND the number of keys in the target object must - * match the number of keys passed in (in other words, a target object must - * have all and only all of the passed-in keys). - * - * expect({ foo: 1, bar: 2 }).to.have.any.keys('foo', 'baz'); - * expect({ foo: 1, bar: 2 }).to.have.any.keys('foo'); - * expect({ foo: 1, bar: 2 }).to.contain.any.keys('bar', 'baz'); - * expect({ foo: 1, bar: 2 }).to.contain.any.keys(['foo']); - * expect({ foo: 1, bar: 2 }).to.contain.any.keys({'foo': 6}); - * expect({ foo: 1, bar: 2 }).to.have.all.keys(['bar', 'foo']); - * expect({ foo: 1, bar: 2 }).to.have.all.keys({'bar': 6, 'foo', 7}); - * expect({ foo: 1, bar: 2, baz: 3 }).to.contain.all.keys(['bar', 'foo']); - * expect({ foo: 1, bar: 2, baz: 3 }).to.contain.all.keys([{'bar': 6}}]); - * - * - * @name keys - * @alias key - * @param {String...|Array|Object} keys - * @api public - */ - - function assertKeys (keys) { - var obj = flag(this, 'object') - , str - , ok = true - , mixedArgsMsg = 'keys must be given single argument of Array|Object|String, or multiple String arguments'; - - switch (_.type(keys)) { - case "array": - if (arguments.length > 1) throw (new Error(mixedArgsMsg)); - break; - case "object": - if (arguments.length > 1) throw (new Error(mixedArgsMsg)); - keys = Object.keys(keys); - break; - default: - keys = Array.prototype.slice.call(arguments); - } - - if (!keys.length) throw new Error('keys required'); - - var actual = Object.keys(obj) - , expected = keys - , len = keys.length - , any = flag(this, 'any') - , all = flag(this, 'all'); - - if (!any && !all) { - all = true; - } - - // Has any - if (any) { - var intersection = expected.filter(function(key) { - return ~actual.indexOf(key); - }); - ok = intersection.length > 0; - } - - // Has all - if (all) { - ok = keys.every(function(key){ - return ~actual.indexOf(key); - }); - if (!flag(this, 'negate') && !flag(this, 'contains')) { - ok = ok && keys.length == actual.length; - } - } - - // Key string - if (len > 1) { - keys = keys.map(function(key){ - return _.inspect(key); - }); - var last = keys.pop(); - if (all) { - str = keys.join(', ') + ', and ' + last; - } - if (any) { - str = keys.join(', ') + ', or ' + last; - } - } else { - str = _.inspect(keys[0]); - } - - // Form - str = (len > 1 ? 'keys ' : 'key ') + str; - - // Have / include - str = (flag(this, 'contains') ? 'contain ' : 'have ') + str; - - // Assertion - this.assert( - ok - , 'expected #{this} to ' + str - , 'expected #{this} to not ' + str - , expected.slice(0).sort() - , actual.sort() - , true - ); - } - - Assertion.addMethod('keys', assertKeys); - Assertion.addMethod('key', assertKeys); - - /** - * ### .throw(constructor) - * - * Asserts that the function target will throw a specific error, or specific type of error - * (as determined using `instanceof`), optionally with a RegExp or string inclusion test - * for the error's message. - * - * var err = new ReferenceError('This is a bad function.'); - * var fn = function () { throw err; } - * expect(fn).to.throw(ReferenceError); - * expect(fn).to.throw(Error); - * expect(fn).to.throw(/bad function/); - * expect(fn).to.not.throw('good function'); - * expect(fn).to.throw(ReferenceError, /bad function/); - * expect(fn).to.throw(err); - * expect(fn).to.not.throw(new RangeError('Out of range.')); - * - * Please note that when a throw expectation is negated, it will check each - * parameter independently, starting with error constructor type. The appropriate way - * to check for the existence of a type of error but for a message that does not match - * is to use `and`. - * - * expect(fn).to.throw(ReferenceError) - * .and.not.throw(/good function/); - * - * @name throw - * @alias throws - * @alias Throw - * @param {ErrorConstructor} constructor - * @param {String|RegExp} expected error message - * @param {String} message _optional_ - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types - * @returns error for chaining (null if no error) - * @api public - */ - - function assertThrows (constructor, errMsg, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - new Assertion(obj, msg).is.a('function'); - - var thrown = false - , desiredError = null - , name = null - , thrownError = null; - - if (arguments.length === 0) { - errMsg = null; - constructor = null; - } else if (constructor && (constructor instanceof RegExp || 'string' === typeof constructor)) { - errMsg = constructor; - constructor = null; - } else if (constructor && constructor instanceof Error) { - desiredError = constructor; - constructor = null; - errMsg = null; - } else if (typeof constructor === 'function') { - name = constructor.prototype.name || constructor.name; - if (name === 'Error' && constructor !== Error) { - name = (new constructor()).name; - } - } else { - constructor = null; - } - - try { - obj(); - } catch (err) { - // first, check desired error - if (desiredError) { - this.assert( - err === desiredError - , 'expected #{this} to throw #{exp} but #{act} was thrown' - , 'expected #{this} to not throw #{exp}' - , (desiredError instanceof Error ? desiredError.toString() : desiredError) - , (err instanceof Error ? err.toString() : err) - ); - - flag(this, 'object', err); - return this; - } - - // next, check constructor - if (constructor) { - this.assert( - err instanceof constructor - , 'expected #{this} to throw #{exp} but #{act} was thrown' - , 'expected #{this} to not throw #{exp} but #{act} was thrown' - , name - , (err instanceof Error ? err.toString() : err) - ); - - if (!errMsg) { - flag(this, 'object', err); - return this; - } - } - - // next, check message - var message = 'object' === _.type(err) && "message" in err - ? err.message - : '' + err; - - if ((message != null) && errMsg && errMsg instanceof RegExp) { - this.assert( - errMsg.exec(message) - , 'expected #{this} to throw error matching #{exp} but got #{act}' - , 'expected #{this} to throw error not matching #{exp}' - , errMsg - , message - ); - - flag(this, 'object', err); - return this; - } else if ((message != null) && errMsg && 'string' === typeof errMsg) { - this.assert( - ~message.indexOf(errMsg) - , 'expected #{this} to throw error including #{exp} but got #{act}' - , 'expected #{this} to throw error not including #{act}' - , errMsg - , message - ); - - flag(this, 'object', err); - return this; - } else { - thrown = true; - thrownError = err; - } - } - - var actuallyGot = '' - , expectedThrown = name !== null - ? name - : desiredError - ? '#{exp}' //_.inspect(desiredError) - : 'an error'; - - if (thrown) { - actuallyGot = ' but #{act} was thrown' - } - - this.assert( - thrown === true - , 'expected #{this} to throw ' + expectedThrown + actuallyGot - , 'expected #{this} to not throw ' + expectedThrown + actuallyGot - , (desiredError instanceof Error ? desiredError.toString() : desiredError) - , (thrownError instanceof Error ? thrownError.toString() : thrownError) - ); - - flag(this, 'object', thrownError); - }; - - Assertion.addMethod('throw', assertThrows); - Assertion.addMethod('throws', assertThrows); - Assertion.addMethod('Throw', assertThrows); - - /** - * ### .respondTo(method) - * - * Asserts that the object or class target will respond to a method. - * - * Klass.prototype.bar = function(){}; - * expect(Klass).to.respondTo('bar'); - * expect(obj).to.respondTo('bar'); - * - * To check if a constructor will respond to a static function, - * set the `itself` flag. - * - * Klass.baz = function(){}; - * expect(Klass).itself.to.respondTo('baz'); - * - * @name respondTo - * @param {String} method - * @param {String} message _optional_ - * @api public - */ - - Assertion.addMethod('respondTo', function (method, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object') - , itself = flag(this, 'itself') - , context = ('function' === _.type(obj) && !itself) - ? obj.prototype[method] - : obj[method]; - - this.assert( - 'function' === typeof context - , 'expected #{this} to respond to ' + _.inspect(method) - , 'expected #{this} to not respond to ' + _.inspect(method) - ); - }); - - /** - * ### .itself - * - * Sets the `itself` flag, later used by the `respondTo` assertion. - * - * function Foo() {} - * Foo.bar = function() {} - * Foo.prototype.baz = function() {} - * - * expect(Foo).itself.to.respondTo('bar'); - * expect(Foo).itself.not.to.respondTo('baz'); - * - * @name itself - * @api public - */ - - Assertion.addProperty('itself', function () { - flag(this, 'itself', true); - }); - - /** - * ### .satisfy(method) - * - * Asserts that the target passes a given truth test. - * - * expect(1).to.satisfy(function(num) { return num > 0; }); - * - * @name satisfy - * @param {Function} matcher - * @param {String} message _optional_ - * @api public - */ - - Assertion.addMethod('satisfy', function (matcher, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - var result = matcher(obj); - this.assert( - result - , 'expected #{this} to satisfy ' + _.objDisplay(matcher) - , 'expected #{this} to not satisfy' + _.objDisplay(matcher) - , this.negate ? false : true - , result - ); - }); - - /** - * ### .closeTo(expected, delta) - * - * Asserts that the target is equal `expected`, to within a +/- `delta` range. - * - * expect(1.5).to.be.closeTo(1, 0.5); - * - * @name closeTo - * @param {Number} expected - * @param {Number} delta - * @param {String} message _optional_ - * @api public - */ - - Assertion.addMethod('closeTo', function (expected, delta, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - - new Assertion(obj, msg).is.a('number'); - if (_.type(expected) !== 'number' || _.type(delta) !== 'number') { - throw new Error('the arguments to closeTo must be numbers'); - } - - this.assert( - Math.abs(obj - expected) <= delta - , 'expected #{this} to be close to ' + expected + ' +/- ' + delta - , 'expected #{this} not to be close to ' + expected + ' +/- ' + delta - ); - }); - - function isSubsetOf(subset, superset, cmp) { - return subset.every(function(elem) { - if (!cmp) return superset.indexOf(elem) !== -1; - - return superset.some(function(elem2) { - return cmp(elem, elem2); - }); - }) - } - - /** - * ### .members(set) - * - * Asserts that the target is a superset of `set`, - * or that the target and `set` have the same strictly-equal (===) members. - * Alternately, if the `deep` flag is set, set members are compared for deep - * equality. - * - * expect([1, 2, 3]).to.include.members([3, 2]); - * expect([1, 2, 3]).to.not.include.members([3, 2, 8]); - * - * expect([4, 2]).to.have.members([2, 4]); - * expect([5, 2]).to.not.have.members([5, 2, 1]); - * - * expect([{ id: 1 }]).to.deep.include.members([{ id: 1 }]); - * - * @name members - * @param {Array} set - * @param {String} message _optional_ - * @api public - */ - - Assertion.addMethod('members', function (subset, msg) { - if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object'); - - new Assertion(obj).to.be.an('array'); - new Assertion(subset).to.be.an('array'); - - var cmp = flag(this, 'deep') ? _.eql : undefined; - - if (flag(this, 'contains')) { - return this.assert( - isSubsetOf(subset, obj, cmp) - , 'expected #{this} to be a superset of #{act}' - , 'expected #{this} to not be a superset of #{act}' - , obj - , subset - ); - } - - this.assert( - isSubsetOf(obj, subset, cmp) && isSubsetOf(subset, obj, cmp) - , 'expected #{this} to have the same members as #{act}' - , 'expected #{this} to not have the same members as #{act}' - , obj - , subset - ); - }); - - /** - * ### .change(function) - * - * Asserts that a function changes an object property - * - * var obj = { val: 10 }; - * var fn = function() { obj.val += 3 }; - * var noChangeFn = function() { return 'foo' + 'bar'; } - * expect(fn).to.change(obj, 'val'); - * expect(noChangFn).to.not.change(obj, 'val') - * - * @name change - * @alias changes - * @alias Change - * @param {String} object - * @param {String} property name - * @param {String} message _optional_ - * @api public - */ - - function assertChanges (object, prop, msg) { - if (msg) flag(this, 'message', msg); - var fn = flag(this, 'object'); - new Assertion(object, msg).to.have.property(prop); - new Assertion(fn).is.a('function'); - - var initial = object[prop]; - fn(); - - this.assert( - initial !== object[prop] - , 'expected .' + prop + ' to change' - , 'expected .' + prop + ' to not change' - ); - } - - Assertion.addChainableMethod('change', assertChanges); - Assertion.addChainableMethod('changes', assertChanges); - - /** - * ### .increase(function) - * - * Asserts that a function increases an object property - * - * var obj = { val: 10 }; - * var fn = function() { obj.val = 15 }; - * expect(fn).to.increase(obj, 'val'); - * - * @name increase - * @alias increases - * @alias Increase - * @param {String} object - * @param {String} property name - * @param {String} message _optional_ - * @api public - */ - - function assertIncreases (object, prop, msg) { - if (msg) flag(this, 'message', msg); - var fn = flag(this, 'object'); - new Assertion(object, msg).to.have.property(prop); - new Assertion(fn).is.a('function'); - - var initial = object[prop]; - fn(); - - this.assert( - object[prop] - initial > 0 - , 'expected .' + prop + ' to increase' - , 'expected .' + prop + ' to not increase' - ); - } - - Assertion.addChainableMethod('increase', assertIncreases); - Assertion.addChainableMethod('increases', assertIncreases); - - /** - * ### .decrease(function) - * - * Asserts that a function decreases an object property - * - * var obj = { val: 10 }; - * var fn = function() { obj.val = 5 }; - * expect(fn).to.decrease(obj, 'val'); - * - * @name decrease - * @alias decreases - * @alias Decrease - * @param {String} object - * @param {String} property name - * @param {String} message _optional_ - * @api public - */ - - function assertDecreases (object, prop, msg) { - if (msg) flag(this, 'message', msg); - var fn = flag(this, 'object'); - new Assertion(object, msg).to.have.property(prop); - new Assertion(fn).is.a('function'); - - var initial = object[prop]; - fn(); - - this.assert( - object[prop] - initial < 0 - , 'expected .' + prop + ' to decrease' - , 'expected .' + prop + ' to not decrease' - ); - } - - Assertion.addChainableMethod('decrease', assertDecreases); - Assertion.addChainableMethod('decreases', assertDecreases); - -}; - -}); - -require.register("chai/lib/chai/interface/assert.js", function (exports, module) { -/*! - * chai - * Copyright(c) 2011-2014 Jake Luer - * MIT Licensed - */ - - -module.exports = function (chai, util) { - - /*! - * Chai dependencies. - */ - - var Assertion = chai.Assertion - , flag = util.flag; - - /*! - * Module export. - */ - - /** - * ### assert(expression, message) - * - * Write your own test expressions. - * - * assert('foo' !== 'bar', 'foo is not bar'); - * assert(Array.isArray([]), 'empty arrays are arrays'); - * - * @param {Mixed} expression to test for truthiness - * @param {String} message to display on error - * @name assert - * @api public - */ - - var assert = chai.assert = function (express, errmsg) { - var test = new Assertion(null, null, chai.assert); - test.assert( - express - , errmsg - , '[ negation message unavailable ]' - ); - }; - - /** - * ### .fail(actual, expected, [message], [operator]) - * - * Throw a failure. Node.js `assert` module-compatible. - * - * @name fail - * @param {Mixed} actual - * @param {Mixed} expected - * @param {String} message - * @param {String} operator - * @api public - */ - - assert.fail = function (actual, expected, message, operator) { - message = message || 'assert.fail()'; - throw new chai.AssertionError(message, { - actual: actual - , expected: expected - , operator: operator - }, assert.fail); - }; - - /** - * ### .ok(object, [message]) - * - * Asserts that `object` is truthy. - * - * assert.ok('everything', 'everything is ok'); - * assert.ok(false, 'this will fail'); - * - * @name ok - * @param {Mixed} object to test - * @param {String} message - * @api public - */ - - assert.ok = function (val, msg) { - new Assertion(val, msg).is.ok; - }; - - /** - * ### .notOk(object, [message]) - * - * Asserts that `object` is falsy. - * - * assert.notOk('everything', 'this will fail'); - * assert.notOk(false, 'this will pass'); - * - * @name notOk - * @param {Mixed} object to test - * @param {String} message - * @api public - */ - - assert.notOk = function (val, msg) { - new Assertion(val, msg).is.not.ok; - }; - - /** - * ### .equal(actual, expected, [message]) - * - * Asserts non-strict equality (`==`) of `actual` and `expected`. - * - * assert.equal(3, '3', '== coerces values to strings'); - * - * @name equal - * @param {Mixed} actual - * @param {Mixed} expected - * @param {String} message - * @api public - */ - - assert.equal = function (act, exp, msg) { - var test = new Assertion(act, msg, assert.equal); - - test.assert( - exp == flag(test, 'object') - , 'expected #{this} to equal #{exp}' - , 'expected #{this} to not equal #{act}' - , exp - , act - ); - }; - - /** - * ### .notEqual(actual, expected, [message]) - * - * Asserts non-strict inequality (`!=`) of `actual` and `expected`. - * - * assert.notEqual(3, 4, 'these numbers are not equal'); - * - * @name notEqual - * @param {Mixed} actual - * @param {Mixed} expected - * @param {String} message - * @api public - */ - - assert.notEqual = function (act, exp, msg) { - var test = new Assertion(act, msg, assert.notEqual); - - test.assert( - exp != flag(test, 'object') - , 'expected #{this} to not equal #{exp}' - , 'expected #{this} to equal #{act}' - , exp - , act - ); - }; - - /** - * ### .strictEqual(actual, expected, [message]) - * - * Asserts strict equality (`===`) of `actual` and `expected`. - * - * assert.strictEqual(true, true, 'these booleans are strictly equal'); - * - * @name strictEqual - * @param {Mixed} actual - * @param {Mixed} expected - * @param {String} message - * @api public - */ - - assert.strictEqual = function (act, exp, msg) { - new Assertion(act, msg).to.equal(exp); - }; - - /** - * ### .notStrictEqual(actual, expected, [message]) - * - * Asserts strict inequality (`!==`) of `actual` and `expected`. - * - * assert.notStrictEqual(3, '3', 'no coercion for strict equality'); - * - * @name notStrictEqual - * @param {Mixed} actual - * @param {Mixed} expected - * @param {String} message - * @api public - */ - - assert.notStrictEqual = function (act, exp, msg) { - new Assertion(act, msg).to.not.equal(exp); - }; - - /** - * ### .deepEqual(actual, expected, [message]) - * - * Asserts that `actual` is deeply equal to `expected`. - * - * assert.deepEqual({ tea: 'green' }, { tea: 'green' }); - * - * @name deepEqual - * @param {Mixed} actual - * @param {Mixed} expected - * @param {String} message - * @api public - */ - - assert.deepEqual = function (act, exp, msg) { - new Assertion(act, msg).to.eql(exp); - }; - - /** - * ### .notDeepEqual(actual, expected, [message]) - * - * Assert that `actual` is not deeply equal to `expected`. - * - * assert.notDeepEqual({ tea: 'green' }, { tea: 'jasmine' }); - * - * @name notDeepEqual - * @param {Mixed} actual - * @param {Mixed} expected - * @param {String} message - * @api public - */ - - assert.notDeepEqual = function (act, exp, msg) { - new Assertion(act, msg).to.not.eql(exp); - }; - - /** - * ### .isTrue(value, [message]) - * - * Asserts that `value` is true. - * - * var teaServed = true; - * assert.isTrue(teaServed, 'the tea has been served'); - * - * @name isTrue - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.isAbove = function (val, abv, msg) { - new Assertion(val, msg).to.be.above(abv); - }; - - /** - * ### .isAbove(valueToCheck, valueToBeAbove, [message]) - * - * Asserts `valueToCheck` is strictly greater than (>) `valueToBeAbove` - * - * assert.isAbove(5, 2, '5 is strictly greater than 2'); - * - * @name isAbove - * @param {Mixed} valueToCheck - * @param {Mixed} valueToBeAbove - * @param {String} message - * @api public - */ - - assert.isBelow = function (val, blw, msg) { - new Assertion(val, msg).to.be.below(blw); - }; - - /** - * ### .isBelow(valueToCheck, valueToBeBelow, [message]) - * - * Asserts `valueToCheck` is strictly less than (<) `valueToBeBelow` - * - * assert.isBelow(3, 6, '3 is strictly less than 6'); - * - * @name isBelow - * @param {Mixed} valueToCheck - * @param {Mixed} valueToBeBelow - * @param {String} message - * @api public - */ - - assert.isTrue = function (val, msg) { - new Assertion(val, msg).is['true']; - }; - - /** - * ### .isFalse(value, [message]) - * - * Asserts that `value` is false. - * - * var teaServed = false; - * assert.isFalse(teaServed, 'no tea yet? hmm...'); - * - * @name isFalse - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.isFalse = function (val, msg) { - new Assertion(val, msg).is['false']; - }; - - /** - * ### .isNull(value, [message]) - * - * Asserts that `value` is null. - * - * assert.isNull(err, 'there was no error'); - * - * @name isNull - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.isNull = function (val, msg) { - new Assertion(val, msg).to.equal(null); - }; - - /** - * ### .isNotNull(value, [message]) - * - * Asserts that `value` is not null. - * - * var tea = 'tasty chai'; - * assert.isNotNull(tea, 'great, time for tea!'); - * - * @name isNotNull - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.isNotNull = function (val, msg) { - new Assertion(val, msg).to.not.equal(null); - }; - - /** - * ### .isUndefined(value, [message]) - * - * Asserts that `value` is `undefined`. - * - * var tea; - * assert.isUndefined(tea, 'no tea defined'); - * - * @name isUndefined - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.isUndefined = function (val, msg) { - new Assertion(val, msg).to.equal(undefined); - }; - - /** - * ### .isDefined(value, [message]) - * - * Asserts that `value` is not `undefined`. - * - * var tea = 'cup of chai'; - * assert.isDefined(tea, 'tea has been defined'); - * - * @name isDefined - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.isDefined = function (val, msg) { - new Assertion(val, msg).to.not.equal(undefined); - }; - - /** - * ### .isFunction(value, [message]) - * - * Asserts that `value` is a function. - * - * function serveTea() { return 'cup of tea'; }; - * assert.isFunction(serveTea, 'great, we can have tea now'); - * - * @name isFunction - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.isFunction = function (val, msg) { - new Assertion(val, msg).to.be.a('function'); - }; - - /** - * ### .isNotFunction(value, [message]) - * - * Asserts that `value` is _not_ a function. - * - * var serveTea = [ 'heat', 'pour', 'sip' ]; - * assert.isNotFunction(serveTea, 'great, we have listed the steps'); - * - * @name isNotFunction - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.isNotFunction = function (val, msg) { - new Assertion(val, msg).to.not.be.a('function'); - }; - - /** - * ### .isObject(value, [message]) - * - * Asserts that `value` is an object (as revealed by - * `Object.prototype.toString`). - * - * var selection = { name: 'Chai', serve: 'with spices' }; - * assert.isObject(selection, 'tea selection is an object'); - * - * @name isObject - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.isObject = function (val, msg) { - new Assertion(val, msg).to.be.a('object'); - }; - - /** - * ### .isNotObject(value, [message]) - * - * Asserts that `value` is _not_ an object. - * - * var selection = 'chai' - * assert.isNotObject(selection, 'tea selection is not an object'); - * assert.isNotObject(null, 'null is not an object'); - * - * @name isNotObject - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.isNotObject = function (val, msg) { - new Assertion(val, msg).to.not.be.a('object'); - }; - - /** - * ### .isArray(value, [message]) - * - * Asserts that `value` is an array. - * - * var menu = [ 'green', 'chai', 'oolong' ]; - * assert.isArray(menu, 'what kind of tea do we want?'); - * - * @name isArray - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.isArray = function (val, msg) { - new Assertion(val, msg).to.be.an('array'); - }; - - /** - * ### .isNotArray(value, [message]) - * - * Asserts that `value` is _not_ an array. - * - * var menu = 'green|chai|oolong'; - * assert.isNotArray(menu, 'what kind of tea do we want?'); - * - * @name isNotArray - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.isNotArray = function (val, msg) { - new Assertion(val, msg).to.not.be.an('array'); - }; - - /** - * ### .isString(value, [message]) - * - * Asserts that `value` is a string. - * - * var teaOrder = 'chai'; - * assert.isString(teaOrder, 'order placed'); - * - * @name isString - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.isString = function (val, msg) { - new Assertion(val, msg).to.be.a('string'); - }; - - /** - * ### .isNotString(value, [message]) - * - * Asserts that `value` is _not_ a string. - * - * var teaOrder = 4; - * assert.isNotString(teaOrder, 'order placed'); - * - * @name isNotString - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.isNotString = function (val, msg) { - new Assertion(val, msg).to.not.be.a('string'); - }; - - /** - * ### .isNumber(value, [message]) - * - * Asserts that `value` is a number. - * - * var cups = 2; - * assert.isNumber(cups, 'how many cups'); - * - * @name isNumber - * @param {Number} value - * @param {String} message - * @api public - */ - - assert.isNumber = function (val, msg) { - new Assertion(val, msg).to.be.a('number'); - }; - - /** - * ### .isNotNumber(value, [message]) - * - * Asserts that `value` is _not_ a number. - * - * var cups = '2 cups please'; - * assert.isNotNumber(cups, 'how many cups'); - * - * @name isNotNumber - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.isNotNumber = function (val, msg) { - new Assertion(val, msg).to.not.be.a('number'); - }; - - /** - * ### .isBoolean(value, [message]) - * - * Asserts that `value` is a boolean. - * - * var teaReady = true - * , teaServed = false; - * - * assert.isBoolean(teaReady, 'is the tea ready'); - * assert.isBoolean(teaServed, 'has tea been served'); - * - * @name isBoolean - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.isBoolean = function (val, msg) { - new Assertion(val, msg).to.be.a('boolean'); - }; - - /** - * ### .isNotBoolean(value, [message]) - * - * Asserts that `value` is _not_ a boolean. - * - * var teaReady = 'yep' - * , teaServed = 'nope'; - * - * assert.isNotBoolean(teaReady, 'is the tea ready'); - * assert.isNotBoolean(teaServed, 'has tea been served'); - * - * @name isNotBoolean - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.isNotBoolean = function (val, msg) { - new Assertion(val, msg).to.not.be.a('boolean'); - }; - - /** - * ### .typeOf(value, name, [message]) - * - * Asserts that `value`'s type is `name`, as determined by - * `Object.prototype.toString`. - * - * assert.typeOf({ tea: 'chai' }, 'object', 'we have an object'); - * assert.typeOf(['chai', 'jasmine'], 'array', 'we have an array'); - * assert.typeOf('tea', 'string', 'we have a string'); - * assert.typeOf(/tea/, 'regexp', 'we have a regular expression'); - * assert.typeOf(null, 'null', 'we have a null'); - * assert.typeOf(undefined, 'undefined', 'we have an undefined'); - * - * @name typeOf - * @param {Mixed} value - * @param {String} name - * @param {String} message - * @api public - */ - - assert.typeOf = function (val, type, msg) { - new Assertion(val, msg).to.be.a(type); - }; - - /** - * ### .notTypeOf(value, name, [message]) - * - * Asserts that `value`'s type is _not_ `name`, as determined by - * `Object.prototype.toString`. - * - * assert.notTypeOf('tea', 'number', 'strings are not numbers'); - * - * @name notTypeOf - * @param {Mixed} value - * @param {String} typeof name - * @param {String} message - * @api public - */ - - assert.notTypeOf = function (val, type, msg) { - new Assertion(val, msg).to.not.be.a(type); - }; - - /** - * ### .instanceOf(object, constructor, [message]) - * - * Asserts that `value` is an instance of `constructor`. - * - * var Tea = function (name) { this.name = name; } - * , chai = new Tea('chai'); - * - * assert.instanceOf(chai, Tea, 'chai is an instance of tea'); - * - * @name instanceOf - * @param {Object} object - * @param {Constructor} constructor - * @param {String} message - * @api public - */ - - assert.instanceOf = function (val, type, msg) { - new Assertion(val, msg).to.be.instanceOf(type); - }; - - /** - * ### .notInstanceOf(object, constructor, [message]) - * - * Asserts `value` is not an instance of `constructor`. - * - * var Tea = function (name) { this.name = name; } - * , chai = new String('chai'); - * - * assert.notInstanceOf(chai, Tea, 'chai is not an instance of tea'); - * - * @name notInstanceOf - * @param {Object} object - * @param {Constructor} constructor - * @param {String} message - * @api public - */ - - assert.notInstanceOf = function (val, type, msg) { - new Assertion(val, msg).to.not.be.instanceOf(type); - }; - - /** - * ### .include(haystack, needle, [message]) - * - * Asserts that `haystack` includes `needle`. Works - * for strings and arrays. - * - * assert.include('foobar', 'bar', 'foobar contains string "bar"'); - * assert.include([ 1, 2, 3 ], 3, 'array contains value'); - * - * @name include - * @param {Array|String} haystack - * @param {Mixed} needle - * @param {String} message - * @api public - */ - - assert.include = function (exp, inc, msg) { - new Assertion(exp, msg, assert.include).include(inc); - }; - - /** - * ### .notInclude(haystack, needle, [message]) - * - * Asserts that `haystack` does not include `needle`. Works - * for strings and arrays. - *i - * assert.notInclude('foobar', 'baz', 'string not include substring'); - * assert.notInclude([ 1, 2, 3 ], 4, 'array not include contain value'); - * - * @name notInclude - * @param {Array|String} haystack - * @param {Mixed} needle - * @param {String} message - * @api public - */ - - assert.notInclude = function (exp, inc, msg) { - new Assertion(exp, msg, assert.notInclude).not.include(inc); - }; - - /** - * ### .match(value, regexp, [message]) - * - * Asserts that `value` matches the regular expression `regexp`. - * - * assert.match('foobar', /^foo/, 'regexp matches'); - * - * @name match - * @param {Mixed} value - * @param {RegExp} regexp - * @param {String} message - * @api public - */ - - assert.match = function (exp, re, msg) { - new Assertion(exp, msg).to.match(re); - }; - - /** - * ### .notMatch(value, regexp, [message]) - * - * Asserts that `value` does not match the regular expression `regexp`. - * - * assert.notMatch('foobar', /^foo/, 'regexp does not match'); - * - * @name notMatch - * @param {Mixed} value - * @param {RegExp} regexp - * @param {String} message - * @api public - */ - - assert.notMatch = function (exp, re, msg) { - new Assertion(exp, msg).to.not.match(re); - }; - - /** - * ### .property(object, property, [message]) - * - * Asserts that `object` has a property named by `property`. - * - * assert.property({ tea: { green: 'matcha' }}, 'tea'); - * - * @name property - * @param {Object} object - * @param {String} property - * @param {String} message - * @api public - */ - - assert.property = function (obj, prop, msg) { - new Assertion(obj, msg).to.have.property(prop); - }; - - /** - * ### .notProperty(object, property, [message]) - * - * Asserts that `object` does _not_ have a property named by `property`. - * - * assert.notProperty({ tea: { green: 'matcha' }}, 'coffee'); - * - * @name notProperty - * @param {Object} object - * @param {String} property - * @param {String} message - * @api public - */ - - assert.notProperty = function (obj, prop, msg) { - new Assertion(obj, msg).to.not.have.property(prop); - }; - - /** - * ### .deepProperty(object, property, [message]) - * - * Asserts that `object` has a property named by `property`, which can be a - * string using dot- and bracket-notation for deep reference. - * - * assert.deepProperty({ tea: { green: 'matcha' }}, 'tea.green'); - * - * @name deepProperty - * @param {Object} object - * @param {String} property - * @param {String} message - * @api public - */ - - assert.deepProperty = function (obj, prop, msg) { - new Assertion(obj, msg).to.have.deep.property(prop); - }; - - /** - * ### .notDeepProperty(object, property, [message]) - * - * Asserts that `object` does _not_ have a property named by `property`, which - * can be a string using dot- and bracket-notation for deep reference. - * - * assert.notDeepProperty({ tea: { green: 'matcha' }}, 'tea.oolong'); - * - * @name notDeepProperty - * @param {Object} object - * @param {String} property - * @param {String} message - * @api public - */ - - assert.notDeepProperty = function (obj, prop, msg) { - new Assertion(obj, msg).to.not.have.deep.property(prop); - }; - - /** - * ### .propertyVal(object, property, value, [message]) - * - * Asserts that `object` has a property named by `property` with value given - * by `value`. - * - * assert.propertyVal({ tea: 'is good' }, 'tea', 'is good'); - * - * @name propertyVal - * @param {Object} object - * @param {String} property - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.propertyVal = function (obj, prop, val, msg) { - new Assertion(obj, msg).to.have.property(prop, val); - }; - - /** - * ### .propertyNotVal(object, property, value, [message]) - * - * Asserts that `object` has a property named by `property`, but with a value - * different from that given by `value`. - * - * assert.propertyNotVal({ tea: 'is good' }, 'tea', 'is bad'); - * - * @name propertyNotVal - * @param {Object} object - * @param {String} property - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.propertyNotVal = function (obj, prop, val, msg) { - new Assertion(obj, msg).to.not.have.property(prop, val); - }; - - /** - * ### .deepPropertyVal(object, property, value, [message]) - * - * Asserts that `object` has a property named by `property` with value given - * by `value`. `property` can use dot- and bracket-notation for deep - * reference. - * - * assert.deepPropertyVal({ tea: { green: 'matcha' }}, 'tea.green', 'matcha'); - * - * @name deepPropertyVal - * @param {Object} object - * @param {String} property - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.deepPropertyVal = function (obj, prop, val, msg) { - new Assertion(obj, msg).to.have.deep.property(prop, val); - }; - - /** - * ### .deepPropertyNotVal(object, property, value, [message]) - * - * Asserts that `object` has a property named by `property`, but with a value - * different from that given by `value`. `property` can use dot- and - * bracket-notation for deep reference. - * - * assert.deepPropertyNotVal({ tea: { green: 'matcha' }}, 'tea.green', 'konacha'); - * - * @name deepPropertyNotVal - * @param {Object} object - * @param {String} property - * @param {Mixed} value - * @param {String} message - * @api public - */ - - assert.deepPropertyNotVal = function (obj, prop, val, msg) { - new Assertion(obj, msg).to.not.have.deep.property(prop, val); - }; - - /** - * ### .lengthOf(object, length, [message]) - * - * Asserts that `object` has a `length` property with the expected value. - * - * assert.lengthOf([1,2,3], 3, 'array has length of 3'); - * assert.lengthOf('foobar', 5, 'string has length of 6'); - * - * @name lengthOf - * @param {Mixed} object - * @param {Number} length - * @param {String} message - * @api public - */ - - assert.lengthOf = function (exp, len, msg) { - new Assertion(exp, msg).to.have.length(len); - }; - - /** - * ### .throws(function, [constructor/string/regexp], [string/regexp], [message]) - * - * Asserts that `function` will throw an error that is an instance of - * `constructor`, or alternately that it will throw an error with message - * matching `regexp`. - * - * assert.throw(fn, 'function throws a reference error'); - * assert.throw(fn, /function throws a reference error/); - * assert.throw(fn, ReferenceError); - * assert.throw(fn, ReferenceError, 'function throws a reference error'); - * assert.throw(fn, ReferenceError, /function throws a reference error/); - * - * @name throws - * @alias throw - * @alias Throw - * @param {Function} function - * @param {ErrorConstructor} constructor - * @param {RegExp} regexp - * @param {String} message - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types - * @api public - */ - - assert.Throw = function (fn, errt, errs, msg) { - if ('string' === typeof errt || errt instanceof RegExp) { - errs = errt; - errt = null; - } - - var assertErr = new Assertion(fn, msg).to.Throw(errt, errs); - return flag(assertErr, 'object'); - }; - - /** - * ### .doesNotThrow(function, [constructor/regexp], [message]) - * - * Asserts that `function` will _not_ throw an error that is an instance of - * `constructor`, or alternately that it will not throw an error with message - * matching `regexp`. - * - * assert.doesNotThrow(fn, Error, 'function does not throw'); - * - * @name doesNotThrow - * @param {Function} function - * @param {ErrorConstructor} constructor - * @param {RegExp} regexp - * @param {String} message - * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types - * @api public - */ - - assert.doesNotThrow = function (fn, type, msg) { - if ('string' === typeof type) { - msg = type; - type = null; - } - - new Assertion(fn, msg).to.not.Throw(type); - }; - - /** - * ### .operator(val1, operator, val2, [message]) - * - * Compares two values using `operator`. - * - * assert.operator(1, '<', 2, 'everything is ok'); - * assert.operator(1, '>', 2, 'this will fail'); - * - * @name operator - * @param {Mixed} val1 - * @param {String} operator - * @param {Mixed} val2 - * @param {String} message - * @api public - */ - - assert.operator = function (val, operator, val2, msg) { - if (!~['==', '===', '>', '>=', '<', '<=', '!=', '!=='].indexOf(operator)) { - throw new Error('Invalid operator "' + operator + '"'); - } - var test = new Assertion(eval(val + operator + val2), msg); - test.assert( - true === flag(test, 'object') - , 'expected ' + util.inspect(val) + ' to be ' + operator + ' ' + util.inspect(val2) - , 'expected ' + util.inspect(val) + ' to not be ' + operator + ' ' + util.inspect(val2) ); - }; - - /** - * ### .closeTo(actual, expected, delta, [message]) - * - * Asserts that the target is equal `expected`, to within a +/- `delta` range. - * - * assert.closeTo(1.5, 1, 0.5, 'numbers are close'); - * - * @name closeTo - * @param {Number} actual - * @param {Number} expected - * @param {Number} delta - * @param {String} message - * @api public - */ - - assert.closeTo = function (act, exp, delta, msg) { - new Assertion(act, msg).to.be.closeTo(exp, delta); - }; - - /** - * ### .sameMembers(set1, set2, [message]) - * - * Asserts that `set1` and `set2` have the same members. - * Order is not taken into account. - * - * assert.sameMembers([ 1, 2, 3 ], [ 2, 1, 3 ], 'same members'); - * - * @name sameMembers - * @param {Array} set1 - * @param {Array} set2 - * @param {String} message - * @api public - */ - - assert.sameMembers = function (set1, set2, msg) { - new Assertion(set1, msg).to.have.same.members(set2); - } - - /** - * ### .sameDeepMembers(set1, set2, [message]) - * - * Asserts that `set1` and `set2` have the same members - using a deep equality checking. - * Order is not taken into account. - * - * assert.sameDeepMembers([ {b: 3}, {a: 2}, {c: 5} ], [ {c: 5}, {b: 3}, {a: 2} ], 'same deep members'); - * - * @name sameDeepMembers - * @param {Array} set1 - * @param {Array} set2 - * @param {String} message - * @api public - */ - - assert.sameDeepMembers = function (set1, set2, msg) { - new Assertion(set1, msg).to.have.same.deep.members(set2); - } - - /** - * ### .includeMembers(superset, subset, [message]) - * - * Asserts that `subset` is included in `superset`. - * Order is not taken into account. - * - * assert.includeMembers([ 1, 2, 3 ], [ 2, 1 ], 'include members'); - * - * @name includeMembers - * @param {Array} superset - * @param {Array} subset - * @param {String} message - * @api public - */ - - assert.includeMembers = function (superset, subset, msg) { - new Assertion(superset, msg).to.include.members(subset); - } - - /** - * ### .changes(function, object, property) - * - * Asserts that a function changes the value of a property - * - * var obj = { val: 10 }; - * var fn = function() { obj.val = 22 }; - * assert.changes(fn, obj, 'val'); - * - * @name changes - * @param {Function} modifier function - * @param {Object} object - * @param {String} property name - * @param {String} message _optional_ - * @api public - */ - - assert.changes = function (fn, obj, prop) { - new Assertion(fn).to.change(obj, prop); - } - - /** - * ### .doesNotChange(function, object, property) - * - * Asserts that a function does not changes the value of a property - * - * var obj = { val: 10 }; - * var fn = function() { console.log('foo'); }; - * assert.doesNotChange(fn, obj, 'val'); - * - * @name doesNotChange - * @param {Function} modifier function - * @param {Object} object - * @param {String} property name - * @param {String} message _optional_ - * @api public - */ - - assert.doesNotChange = function (fn, obj, prop) { - new Assertion(fn).to.not.change(obj, prop); - } - - /** - * ### .increases(function, object, property) - * - * Asserts that a function increases an object property - * - * var obj = { val: 10 }; - * var fn = function() { obj.val = 13 }; - * assert.increases(fn, obj, 'val'); - * - * @name increases - * @param {Function} modifier function - * @param {Object} object - * @param {String} property name - * @param {String} message _optional_ - * @api public - */ - - assert.increases = function (fn, obj, prop) { - new Assertion(fn).to.increase(obj, prop); - } - - /** - * ### .doesNotIncrease(function, object, property) - * - * Asserts that a function does not increase object property - * - * var obj = { val: 10 }; - * var fn = function() { obj.val = 8 }; - * assert.doesNotIncrease(fn, obj, 'val'); - * - * @name doesNotIncrease - * @param {Function} modifier function - * @param {Object} object - * @param {String} property name - * @param {String} message _optional_ - * @api public - */ - - assert.doesNotIncrease = function (fn, obj, prop) { - new Assertion(fn).to.not.increase(obj, prop); - } - - /** - * ### .decreases(function, object, property) - * - * Asserts that a function decreases an object property - * - * var obj = { val: 10 }; - * var fn = function() { obj.val = 5 }; - * assert.decreases(fn, obj, 'val'); - * - * @name decreases - * @param {Function} modifier function - * @param {Object} object - * @param {String} property name - * @param {String} message _optional_ - * @api public - */ - - assert.decreases = function (fn, obj, prop) { - new Assertion(fn).to.decrease(obj, prop); - } - - /** - * ### .doesNotDecrease(function, object, property) - * - * Asserts that a function does not decreases an object property - * - * var obj = { val: 10 }; - * var fn = function() { obj.val = 15 }; - * assert.doesNotDecrease(fn, obj, 'val'); - * - * @name doesNotDecrease - * @param {Function} modifier function - * @param {Object} object - * @param {String} property name - * @param {String} message _optional_ - * @api public - */ - - assert.doesNotDecrease = function (fn, obj, prop) { - new Assertion(fn).to.not.decrease(obj, prop); - } - - /*! - * Undocumented / untested - */ - - assert.ifError = function (val, msg) { - new Assertion(val, msg).to.not.be.ok; - }; - - /*! - * Aliases. - */ - - (function alias(name, as){ - assert[as] = assert[name]; - return alias; - }) - ('Throw', 'throw') - ('Throw', 'throws'); -}; - -}); - -require.register("chai/lib/chai/interface/expect.js", function (exports, module) { -/*! - * chai - * Copyright(c) 2011-2014 Jake Luer - * MIT Licensed - */ - -module.exports = function (chai, util) { - chai.expect = function (val, message) { - return new chai.Assertion(val, message); - }; - - /** - * ### .fail(actual, expected, [message], [operator]) - * - * Throw a failure. - * - * @name fail - * @param {Mixed} actual - * @param {Mixed} expected - * @param {String} message - * @param {String} operator - * @api public - */ - - chai.expect.fail = function (actual, expected, message, operator) { - message = message || 'expect.fail()'; - throw new chai.AssertionError(message, { - actual: actual - , expected: expected - , operator: operator - }, chai.expect.fail); - }; -}; - -}); - -require.register("chai/lib/chai/interface/should.js", function (exports, module) { -/*! - * chai - * Copyright(c) 2011-2014 Jake Luer - * MIT Licensed - */ - -module.exports = function (chai, util) { - var Assertion = chai.Assertion; - - function loadShould () { - // explicitly define this method as function as to have it's name to include as `ssfi` - function shouldGetter() { - if (this instanceof String || this instanceof Number) { - return new Assertion(this.constructor(this), null, shouldGetter); - } else if (this instanceof Boolean) { - return new Assertion(this == true, null, shouldGetter); - } - return new Assertion(this, null, shouldGetter); - } - function shouldSetter(value) { - // See https://github.com/chaijs/chai/issues/86: this makes - // `whatever.should = someValue` actually set `someValue`, which is - // especially useful for `global.should = require('chai').should()`. - // - // Note that we have to use [[DefineProperty]] instead of [[Put]] - // since otherwise we would trigger this very setter! - Object.defineProperty(this, 'should', { - value: value, - enumerable: true, - configurable: true, - writable: true - }); - } - // modify Object.prototype to have `should` - Object.defineProperty(Object.prototype, 'should', { - set: shouldSetter - , get: shouldGetter - , configurable: true - }); - - var should = {}; - - /** - * ### .fail(actual, expected, [message], [operator]) - * - * Throw a failure. - * - * @name fail - * @param {Mixed} actual - * @param {Mixed} expected - * @param {String} message - * @param {String} operator - * @api public - */ - - should.fail = function (actual, expected, message, operator) { - message = message || 'should.fail()'; - throw new chai.AssertionError(message, { - actual: actual - , expected: expected - , operator: operator - }, should.fail); - }; - - should.equal = function (val1, val2, msg) { - new Assertion(val1, msg).to.equal(val2); - }; - - should.Throw = function (fn, errt, errs, msg) { - new Assertion(fn, msg).to.Throw(errt, errs); - }; - - should.exist = function (val, msg) { - new Assertion(val, msg).to.exist; - } - - // negation - should.not = {} - - should.not.equal = function (val1, val2, msg) { - new Assertion(val1, msg).to.not.equal(val2); - }; - - should.not.Throw = function (fn, errt, errs, msg) { - new Assertion(fn, msg).to.not.Throw(errt, errs); - }; - - should.not.exist = function (val, msg) { - new Assertion(val, msg).to.not.exist; - } - - should['throw'] = should['Throw']; - should.not['throw'] = should.not['Throw']; - - return should; - }; - - chai.should = loadShould; - chai.Should = loadShould; -}; - -}); - -require.register("chai/lib/chai/utils/addChainableMethod.js", function (exports, module) { -/*! - * Chai - addChainingMethod utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -/*! - * Module dependencies - */ - -var transferFlags = require('chai/lib/chai/utils/transferFlags.js'); -var flag = require('chai/lib/chai/utils/flag.js'); -var config = require('chai/lib/chai/config.js'); - -/*! - * Module variables - */ - -// Check whether `__proto__` is supported -var hasProtoSupport = '__proto__' in Object; - -// Without `__proto__` support, this module will need to add properties to a function. -// However, some Function.prototype methods cannot be overwritten, -// and there seems no easy cross-platform way to detect them (@see chaijs/chai/issues/69). -var excludeNames = /^(?:length|name|arguments|caller)$/; - -// Cache `Function` properties -var call = Function.prototype.call, - apply = Function.prototype.apply; - -/** - * ### addChainableMethod (ctx, name, method, chainingBehavior) - * - * Adds a method to an object, such that the method can also be chained. - * - * utils.addChainableMethod(chai.Assertion.prototype, 'foo', function (str) { - * var obj = utils.flag(this, 'object'); - * new chai.Assertion(obj).to.be.equal(str); - * }); - * - * Can also be accessed directly from `chai.Assertion`. - * - * chai.Assertion.addChainableMethod('foo', fn, chainingBehavior); - * - * The result can then be used as both a method assertion, executing both `method` and - * `chainingBehavior`, or as a language chain, which only executes `chainingBehavior`. - * - * expect(fooStr).to.be.foo('bar'); - * expect(fooStr).to.be.foo.equal('foo'); - * - * @param {Object} ctx object to which the method is added - * @param {String} name of method to add - * @param {Function} method function to be used for `name`, when called - * @param {Function} chainingBehavior function to be called every time the property is accessed - * @name addChainableMethod - * @api public - */ - -module.exports = function (ctx, name, method, chainingBehavior) { - if (typeof chainingBehavior !== 'function') { - chainingBehavior = function () { }; - } - - var chainableBehavior = { - method: method - , chainingBehavior: chainingBehavior - }; - - // save the methods so we can overwrite them later, if we need to. - if (!ctx.__methods) { - ctx.__methods = {}; - } - ctx.__methods[name] = chainableBehavior; - - Object.defineProperty(ctx, name, - { get: function () { - chainableBehavior.chainingBehavior.call(this); - - var assert = function assert() { - var old_ssfi = flag(this, 'ssfi'); - if (old_ssfi && config.includeStack === false) - flag(this, 'ssfi', assert); - var result = chainableBehavior.method.apply(this, arguments); - return result === undefined ? this : result; - }; - - // Use `__proto__` if available - if (hasProtoSupport) { - // Inherit all properties from the object by replacing the `Function` prototype - var prototype = assert.__proto__ = Object.create(this); - // Restore the `call` and `apply` methods from `Function` - prototype.call = call; - prototype.apply = apply; - } - // Otherwise, redefine all properties (slow!) - else { - var asserterNames = Object.getOwnPropertyNames(ctx); - asserterNames.forEach(function (asserterName) { - if (!excludeNames.test(asserterName)) { - var pd = Object.getOwnPropertyDescriptor(ctx, asserterName); - Object.defineProperty(assert, asserterName, pd); - } - }); - } - - transferFlags(this, assert); - return assert; - } - , configurable: true - }); -}; - -}); - -require.register("chai/lib/chai/utils/addMethod.js", function (exports, module) { -/*! - * Chai - addMethod utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -var config = require('chai/lib/chai/config.js'); - -/** - * ### .addMethod (ctx, name, method) - * - * Adds a method to the prototype of an object. - * - * utils.addMethod(chai.Assertion.prototype, 'foo', function (str) { - * var obj = utils.flag(this, 'object'); - * new chai.Assertion(obj).to.be.equal(str); - * }); - * - * Can also be accessed directly from `chai.Assertion`. - * - * chai.Assertion.addMethod('foo', fn); - * - * Then can be used as any other assertion. - * - * expect(fooStr).to.be.foo('bar'); - * - * @param {Object} ctx object to which the method is added - * @param {String} name of method to add - * @param {Function} method function to be used for name - * @name addMethod - * @api public - */ -var flag = require('chai/lib/chai/utils/flag.js'); - -module.exports = function (ctx, name, method) { - ctx[name] = function () { - var old_ssfi = flag(this, 'ssfi'); - if (old_ssfi && config.includeStack === false) - flag(this, 'ssfi', ctx[name]); - var result = method.apply(this, arguments); - return result === undefined ? this : result; - }; -}; - -}); - -require.register("chai/lib/chai/utils/addProperty.js", function (exports, module) { -/*! - * Chai - addProperty utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -/** - * ### addProperty (ctx, name, getter) - * - * Adds a property to the prototype of an object. - * - * utils.addProperty(chai.Assertion.prototype, 'foo', function () { - * var obj = utils.flag(this, 'object'); - * new chai.Assertion(obj).to.be.instanceof(Foo); - * }); - * - * Can also be accessed directly from `chai.Assertion`. - * - * chai.Assertion.addProperty('foo', fn); - * - * Then can be used as any other assertion. - * - * expect(myFoo).to.be.foo; - * - * @param {Object} ctx object to which the property is added - * @param {String} name of property to add - * @param {Function} getter function to be used for name - * @name addProperty - * @api public - */ - -module.exports = function (ctx, name, getter) { - Object.defineProperty(ctx, name, - { get: function () { - var result = getter.call(this); - return result === undefined ? this : result; - } - , configurable: true - }); -}; - -}); - -require.register("chai/lib/chai/utils/flag.js", function (exports, module) { -/*! - * Chai - flag utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -/** - * ### flag(object, key, [value]) - * - * Get or set a flag value on an object. If a - * value is provided it will be set, else it will - * return the currently set value or `undefined` if - * the value is not set. - * - * utils.flag(this, 'foo', 'bar'); // setter - * utils.flag(this, 'foo'); // getter, returns `bar` - * - * @param {Object} object constructed Assertion - * @param {String} key - * @param {Mixed} value (optional) - * @name flag - * @api private - */ - -module.exports = function (obj, key, value) { - var flags = obj.__flags || (obj.__flags = Object.create(null)); - if (arguments.length === 3) { - flags[key] = value; - } else { - return flags[key]; - } -}; - -}); - -require.register("chai/lib/chai/utils/getActual.js", function (exports, module) { -/*! - * Chai - getActual utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -/** - * # getActual(object, [actual]) - * - * Returns the `actual` value for an Assertion - * - * @param {Object} object (constructed Assertion) - * @param {Arguments} chai.Assertion.prototype.assert arguments - */ - -module.exports = function (obj, args) { - return args.length > 4 ? args[4] : obj._obj; -}; - -}); - -require.register("chai/lib/chai/utils/getEnumerableProperties.js", function (exports, module) { -/*! - * Chai - getEnumerableProperties utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -/** - * ### .getEnumerableProperties(object) - * - * This allows the retrieval of enumerable property names of an object, - * inherited or not. - * - * @param {Object} object - * @returns {Array} - * @name getEnumerableProperties - * @api public - */ - -module.exports = function getEnumerableProperties(object) { - var result = []; - for (var name in object) { - result.push(name); - } - return result; -}; - -}); - -require.register("chai/lib/chai/utils/getMessage.js", function (exports, module) { -/*! - * Chai - message composition utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -/*! - * Module dependancies - */ - -var flag = require('chai/lib/chai/utils/flag.js') - , getActual = require('chai/lib/chai/utils/getActual.js') - , inspect = require('chai/lib/chai/utils/inspect.js') - , objDisplay = require('chai/lib/chai/utils/objDisplay.js'); - -/** - * ### .getMessage(object, message, negateMessage) - * - * Construct the error message based on flags - * and template tags. Template tags will return - * a stringified inspection of the object referenced. - * - * Message template tags: - * - `#{this}` current asserted object - * - `#{act}` actual value - * - `#{exp}` expected value - * - * @param {Object} object (constructed Assertion) - * @param {Arguments} chai.Assertion.prototype.assert arguments - * @name getMessage - * @api public - */ - -module.exports = function (obj, args) { - var negate = flag(obj, 'negate') - , val = flag(obj, 'object') - , expected = args[3] - , actual = getActual(obj, args) - , msg = negate ? args[2] : args[1] - , flagMsg = flag(obj, 'message'); - - if(typeof msg === "function") msg = msg(); - msg = msg || ''; - msg = msg - .replace(/#{this}/g, objDisplay(val)) - .replace(/#{act}/g, objDisplay(actual)) - .replace(/#{exp}/g, objDisplay(expected)); - - return flagMsg ? flagMsg + ': ' + msg : msg; -}; - -}); - -require.register("chai/lib/chai/utils/getName.js", function (exports, module) { -/*! - * Chai - getName utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -/** - * # getName(func) - * - * Gets the name of a function, in a cross-browser way. - * - * @param {Function} a function (usually a constructor) - */ - -module.exports = function (func) { - if (func.name) return func.name; - - var match = /^\s?function ([^(]*)\(/.exec(func); - return match && match[1] ? match[1] : ""; -}; - -}); - -require.register("chai/lib/chai/utils/getPathValue.js", function (exports, module) { -/*! - * Chai - getPathValue utility - * Copyright(c) 2012-2014 Jake Luer - * @see https://github.com/logicalparadox/filtr - * MIT Licensed - */ - -var getPathInfo = require('chai/lib/chai/utils/getPathInfo.js'); - -/** - * ### .getPathValue(path, object) - * - * This allows the retrieval of values in an - * object given a string path. - * - * var obj = { - * prop1: { - * arr: ['a', 'b', 'c'] - * , str: 'Hello' - * } - * , prop2: { - * arr: [ { nested: 'Universe' } ] - * , str: 'Hello again!' - * } - * } - * - * The following would be the results. - * - * getPathValue('prop1.str', obj); // Hello - * getPathValue('prop1.att[2]', obj); // b - * getPathValue('prop2.arr[0].nested', obj); // Universe - * - * @param {String} path - * @param {Object} object - * @returns {Object} value or `undefined` - * @name getPathValue - * @api public - */ -module.exports = function(path, obj) { - var info = getPathInfo(path, obj); - return info.value; -}; - -}); - -require.register("chai/lib/chai/utils/getPathInfo.js", function (exports, module) { -/*! - * Chai - getPathInfo utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -var hasProperty = require('chai/lib/chai/utils/hasProperty.js'); - -/** - * ### .getPathInfo(path, object) - * - * This allows the retrieval of property info in an - * object given a string path. - * - * The path info consists of an object with the - * following properties: - * - * * parent - The parent object of the property referenced by `path` - * * name - The name of the final property, a number if it was an array indexer - * * value - The value of the property, if it exists, otherwise `undefined` - * * exists - Whether the property exists or not - * - * @param {String} path - * @param {Object} object - * @returns {Object} info - * @name getPathInfo - * @api public - */ - -module.exports = function getPathInfo(path, obj) { - var parsed = parsePath(path), - last = parsed[parsed.length - 1]; - - var info = { - parent: _getPathValue(parsed, obj, parsed.length - 1), - name: last.p || last.i, - value: _getPathValue(parsed, obj), - }; - info.exists = hasProperty(info.name, info.parent); - - return info; -}; - - -/*! - * ## parsePath(path) - * - * Helper function used to parse string object - * paths. Use in conjunction with `_getPathValue`. - * - * var parsed = parsePath('myobject.property.subprop'); - * - * ### Paths: - * - * * Can be as near infinitely deep and nested - * * Arrays are also valid using the formal `myobject.document[3].property`. - * - * @param {String} path - * @returns {Object} parsed - * @api private - */ - -function parsePath (path) { - var str = path.replace(/\[/g, '.[') - , parts = str.match(/(\\\.|[^.]+?)+/g); - return parts.map(function (value) { - var re = /\[(\d+)\]$/ - , mArr = re.exec(value); - if (mArr) return { i: parseFloat(mArr[1]) }; - else return { p: value }; - }); -} - - -/*! - * ## _getPathValue(parsed, obj) - * - * Helper companion function for `.parsePath` that returns - * the value located at the parsed address. - * - * var value = getPathValue(parsed, obj); - * - * @param {Object} parsed definition from `parsePath`. - * @param {Object} object to search against - * @param {Number} object to search against - * @returns {Object|Undefined} value - * @api private - */ - -function _getPathValue (parsed, obj, index) { - var tmp = obj - , res; - - index = (index === undefined ? parsed.length : index); - - for (var i = 0, l = index; i < l; i++) { - var part = parsed[i]; - if (tmp) { - if ('undefined' !== typeof part.p) - tmp = tmp[part.p]; - else if ('undefined' !== typeof part.i) - tmp = tmp[part.i]; - if (i == (l - 1)) res = tmp; - } else { - res = undefined; - } - } - return res; -} - -}); - -require.register("chai/lib/chai/utils/hasProperty.js", function (exports, module) { -/*! - * Chai - hasProperty utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -var type = require('chai/lib/chai/utils/type.js'); - -/** - * ### .hasProperty(object, name) - * - * This allows checking whether an object has - * named property or numeric array index. - * - * Basically does the same thing as the `in` - * operator but works properly with natives - * and null/undefined values. - * - * var obj = { - * arr: ['a', 'b', 'c'] - * , str: 'Hello' - * } - * - * The following would be the results. - * - * hasProperty('str', obj); // true - * hasProperty('constructor', obj); // true - * hasProperty('bar', obj); // false - * - * hasProperty('length', obj.str); // true - * hasProperty(1, obj.str); // true - * hasProperty(5, obj.str); // false - * - * hasProperty('length', obj.arr); // true - * hasProperty(2, obj.arr); // true - * hasProperty(3, obj.arr); // false - * - * @param {Objuect} object - * @param {String|Number} name - * @returns {Boolean} whether it exists - * @name getPathInfo - * @api public - */ - -var literals = { - 'number': Number - , 'string': String -}; - -module.exports = function hasProperty(name, obj) { - var ot = type(obj); - - // Bad Object, obviously no props at all - if(ot === 'null' || ot === 'undefined') - return false; - - // The `in` operator does not work with certain literals - // box these before the check - if(literals[ot] && typeof obj !== 'object') - obj = new literals[ot](obj); - - return name in obj; -}; - -}); - -require.register("chai/lib/chai/utils/getProperties.js", function (exports, module) { -/*! - * Chai - getProperties utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -/** - * ### .getProperties(object) - * - * This allows the retrieval of property names of an object, enumerable or not, - * inherited or not. - * - * @param {Object} object - * @returns {Array} - * @name getProperties - * @api public - */ - -module.exports = function getProperties(object) { - var result = Object.getOwnPropertyNames(subject); - - function addProperty(property) { - if (result.indexOf(property) === -1) { - result.push(property); - } - } - - var proto = Object.getPrototypeOf(subject); - while (proto !== null) { - Object.getOwnPropertyNames(proto).forEach(addProperty); - proto = Object.getPrototypeOf(proto); - } - - return result; -}; - -}); - -require.register("chai/lib/chai/utils/index.js", function (exports, module) { -/*! - * chai - * Copyright(c) 2011 Jake Luer - * MIT Licensed - */ - -/*! - * Main exports - */ - -var exports = module.exports = {}; - -/*! - * test utility - */ - -exports.test = require('chai/lib/chai/utils/test.js'); - -/*! - * type utility - */ - -exports.type = require('chai/lib/chai/utils/type.js'); - -/*! - * message utility - */ - -exports.getMessage = require('chai/lib/chai/utils/getMessage.js'); - -/*! - * actual utility - */ - -exports.getActual = require('chai/lib/chai/utils/getActual.js'); - -/*! - * Inspect util - */ - -exports.inspect = require('chai/lib/chai/utils/inspect.js'); - -/*! - * Object Display util - */ - -exports.objDisplay = require('chai/lib/chai/utils/objDisplay.js'); - -/*! - * Flag utility - */ - -exports.flag = require('chai/lib/chai/utils/flag.js'); - -/*! - * Flag transferring utility - */ - -exports.transferFlags = require('chai/lib/chai/utils/transferFlags.js'); - -/*! - * Deep equal utility - */ - -exports.eql = require('chaijs~deep-eql@0.1.3'); - -/*! - * Deep path value - */ - -exports.getPathValue = require('chai/lib/chai/utils/getPathValue.js'); - -/*! - * Deep path info - */ - -exports.getPathInfo = require('chai/lib/chai/utils/getPathInfo.js'); - -/*! - * Check if a property exists - */ - -exports.hasProperty = require('chai/lib/chai/utils/hasProperty.js'); - -/*! - * Function name - */ - -exports.getName = require('chai/lib/chai/utils/getName.js'); - -/*! - * add Property - */ - -exports.addProperty = require('chai/lib/chai/utils/addProperty.js'); - -/*! - * add Method - */ - -exports.addMethod = require('chai/lib/chai/utils/addMethod.js'); - -/*! - * overwrite Property - */ - -exports.overwriteProperty = require('chai/lib/chai/utils/overwriteProperty.js'); - -/*! - * overwrite Method - */ - -exports.overwriteMethod = require('chai/lib/chai/utils/overwriteMethod.js'); - -/*! - * Add a chainable method - */ - -exports.addChainableMethod = require('chai/lib/chai/utils/addChainableMethod.js'); - -/*! - * Overwrite chainable method - */ - -exports.overwriteChainableMethod = require('chai/lib/chai/utils/overwriteChainableMethod.js'); - - -}); - -require.register("chai/lib/chai/utils/inspect.js", function (exports, module) { -// This is (almost) directly from Node.js utils -// https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/util.js - -var getName = require('chai/lib/chai/utils/getName.js'); -var getProperties = require('chai/lib/chai/utils/getProperties.js'); -var getEnumerableProperties = require('chai/lib/chai/utils/getEnumerableProperties.js'); - -module.exports = inspect; - -/** - * Echos the value of a value. Trys to print the value out - * in the best way possible given the different types. - * - * @param {Object} obj The object to print out. - * @param {Boolean} showHidden Flag that shows hidden (not enumerable) - * properties of objects. - * @param {Number} depth Depth in which to descend in object. Default is 2. - * @param {Boolean} colors Flag to turn on ANSI escape codes to color the - * output. Default is false (no coloring). - */ -function inspect(obj, showHidden, depth, colors) { - var ctx = { - showHidden: showHidden, - seen: [], - stylize: function (str) { return str; } - }; - return formatValue(ctx, obj, (typeof depth === 'undefined' ? 2 : depth)); -} - -// Returns true if object is a DOM element. -var isDOMElement = function (object) { - if (typeof HTMLElement === 'object') { - return object instanceof HTMLElement; - } else { - return object && - typeof object === 'object' && - object.nodeType === 1 && - typeof object.nodeName === 'string'; - } -}; - -function formatValue(ctx, value, recurseTimes) { - // Provide a hook for user-specified inspect functions. - // Check that value is an object with an inspect function on it - if (value && typeof value.inspect === 'function' && - // Filter out the util module, it's inspect function is special - value.inspect !== exports.inspect && - // Also filter out any prototype objects using the circular check. - !(value.constructor && value.constructor.prototype === value)) { - var ret = value.inspect(recurseTimes); - if (typeof ret !== 'string') { - ret = formatValue(ctx, ret, recurseTimes); - } - return ret; - } - - // Primitive types cannot have properties - var primitive = formatPrimitive(ctx, value); - if (primitive) { - return primitive; - } - - // If this is a DOM element, try to get the outer HTML. - if (isDOMElement(value)) { - if ('outerHTML' in value) { - return value.outerHTML; - // This value does not have an outerHTML attribute, - // it could still be an XML element - } else { - // Attempt to serialize it - try { - if (document.xmlVersion) { - var xmlSerializer = new XMLSerializer(); - return xmlSerializer.serializeToString(value); - } else { - // Firefox 11- do not support outerHTML - // It does, however, support innerHTML - // Use the following to render the element - var ns = "http://www.w3.org/1999/xhtml"; - var container = document.createElementNS(ns, '_'); - - container.appendChild(value.cloneNode(false)); - html = container.innerHTML - .replace('><', '>' + value.innerHTML + '<'); - container.innerHTML = ''; - return html; - } - } catch (err) { - // This could be a non-native DOM implementation, - // continue with the normal flow: - // printing the element as if it is an object. - } - } - } - - // Look up the keys of the object. - var visibleKeys = getEnumerableProperties(value); - var keys = ctx.showHidden ? getProperties(value) : visibleKeys; - - // Some type of object without properties can be shortcutted. - // In IE, errors have a single `stack` property, or if they are vanilla `Error`, - // a `stack` plus `description` property; ignore those for consistency. - if (keys.length === 0 || (isError(value) && ( - (keys.length === 1 && keys[0] === 'stack') || - (keys.length === 2 && keys[0] === 'description' && keys[1] === 'stack') - ))) { - if (typeof value === 'function') { - var name = getName(value); - var nameSuffix = name ? ': ' + name : ''; - return ctx.stylize('[Function' + nameSuffix + ']', 'special'); - } - if (isRegExp(value)) { - return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); - } - if (isDate(value)) { - return ctx.stylize(Date.prototype.toUTCString.call(value), 'date'); - } - if (isError(value)) { - return formatError(value); - } - } - - var base = '', array = false, braces = ['{', '}']; - - // Make Array say that they are Array - if (isArray(value)) { - array = true; - braces = ['[', ']']; - } - - // Make functions say that they are functions - if (typeof value === 'function') { - var name = getName(value); - var nameSuffix = name ? ': ' + name : ''; - base = ' [Function' + nameSuffix + ']'; - } - - // Make RegExps say that they are RegExps - if (isRegExp(value)) { - base = ' ' + RegExp.prototype.toString.call(value); - } - - // Make dates with properties first say the date - if (isDate(value)) { - base = ' ' + Date.prototype.toUTCString.call(value); - } - - // Make error with message first say the error - if (isError(value)) { - return formatError(value); - } - - if (keys.length === 0 && (!array || value.length == 0)) { - return braces[0] + base + braces[1]; - } - - if (recurseTimes < 0) { - if (isRegExp(value)) { - return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); - } else { - return ctx.stylize('[Object]', 'special'); - } - } - - ctx.seen.push(value); - - var output; - if (array) { - output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); - } else { - output = keys.map(function(key) { - return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); - }); - } - - ctx.seen.pop(); - - return reduceToSingleString(output, base, braces); -} - - -function formatPrimitive(ctx, value) { - switch (typeof value) { - case 'undefined': - return ctx.stylize('undefined', 'undefined'); - - case 'string': - var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') - .replace(/'/g, "\\'") - .replace(/\\"/g, '"') + '\''; - return ctx.stylize(simple, 'string'); - - case 'number': - if (value === 0 && (1/value) === -Infinity) { - return ctx.stylize('-0', 'number'); - } - return ctx.stylize('' + value, 'number'); - - case 'boolean': - return ctx.stylize('' + value, 'boolean'); - } - // For some reason typeof null is "object", so special case here. - if (value === null) { - return ctx.stylize('null', 'null'); - } -} - - -function formatError(value) { - return '[' + Error.prototype.toString.call(value) + ']'; -} - - -function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { - var output = []; - for (var i = 0, l = value.length; i < l; ++i) { - if (Object.prototype.hasOwnProperty.call(value, String(i))) { - output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - String(i), true)); - } else { - output.push(''); - } - } - keys.forEach(function(key) { - if (!key.match(/^\d+$/)) { - output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - key, true)); - } - }); - return output; -} - - -function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { - var name, str; - if (value.__lookupGetter__) { - if (value.__lookupGetter__(key)) { - if (value.__lookupSetter__(key)) { - str = ctx.stylize('[Getter/Setter]', 'special'); - } else { - str = ctx.stylize('[Getter]', 'special'); - } - } else { - if (value.__lookupSetter__(key)) { - str = ctx.stylize('[Setter]', 'special'); - } - } - } - if (visibleKeys.indexOf(key) < 0) { - name = '[' + key + ']'; - } - if (!str) { - if (ctx.seen.indexOf(value[key]) < 0) { - if (recurseTimes === null) { - str = formatValue(ctx, value[key], null); - } else { - str = formatValue(ctx, value[key], recurseTimes - 1); - } - if (str.indexOf('\n') > -1) { - if (array) { - str = str.split('\n').map(function(line) { - return ' ' + line; - }).join('\n').substr(2); - } else { - str = '\n' + str.split('\n').map(function(line) { - return ' ' + line; - }).join('\n'); - } - } - } else { - str = ctx.stylize('[Circular]', 'special'); - } - } - if (typeof name === 'undefined') { - if (array && key.match(/^\d+$/)) { - return str; - } - name = JSON.stringify('' + key); - if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { - name = name.substr(1, name.length - 2); - name = ctx.stylize(name, 'name'); - } else { - name = name.replace(/'/g, "\\'") - .replace(/\\"/g, '"') - .replace(/(^"|"$)/g, "'"); - name = ctx.stylize(name, 'string'); - } - } - - return name + ': ' + str; -} - - -function reduceToSingleString(output, base, braces) { - var numLinesEst = 0; - var length = output.reduce(function(prev, cur) { - numLinesEst++; - if (cur.indexOf('\n') >= 0) numLinesEst++; - return prev + cur.length + 1; - }, 0); - - if (length > 60) { - return braces[0] + - (base === '' ? '' : base + '\n ') + - ' ' + - output.join(',\n ') + - ' ' + - braces[1]; - } - - return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; -} - -function isArray(ar) { - return Array.isArray(ar) || - (typeof ar === 'object' && objectToString(ar) === '[object Array]'); -} - -function isRegExp(re) { - return typeof re === 'object' && objectToString(re) === '[object RegExp]'; -} - -function isDate(d) { - return typeof d === 'object' && objectToString(d) === '[object Date]'; -} - -function isError(e) { - return typeof e === 'object' && objectToString(e) === '[object Error]'; -} - -function objectToString(o) { - return Object.prototype.toString.call(o); -} - -}); - -require.register("chai/lib/chai/utils/objDisplay.js", function (exports, module) { -/*! - * Chai - flag utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -/*! - * Module dependancies - */ - -var inspect = require('chai/lib/chai/utils/inspect.js'); -var config = require('chai/lib/chai/config.js'); - -/** - * ### .objDisplay (object) - * - * Determines if an object or an array matches - * criteria to be inspected in-line for error - * messages or should be truncated. - * - * @param {Mixed} javascript object to inspect - * @name objDisplay - * @api public - */ - -module.exports = function (obj) { - var str = inspect(obj) - , type = Object.prototype.toString.call(obj); - - if (config.truncateThreshold && str.length >= config.truncateThreshold) { - if (type === '[object Function]') { - return !obj.name || obj.name === '' - ? '[Function]' - : '[Function: ' + obj.name + ']'; - } else if (type === '[object Array]') { - return '[ Array(' + obj.length + ') ]'; - } else if (type === '[object Object]') { - var keys = Object.keys(obj) - , kstr = keys.length > 2 - ? keys.splice(0, 2).join(', ') + ', ...' - : keys.join(', '); - return '{ Object (' + kstr + ') }'; - } else { - return str; - } - } else { - return str; - } -}; - -}); - -require.register("chai/lib/chai/utils/overwriteMethod.js", function (exports, module) { -/*! - * Chai - overwriteMethod utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -/** - * ### overwriteMethod (ctx, name, fn) - * - * Overwites an already existing method and provides - * access to previous function. Must return function - * to be used for name. - * - * utils.overwriteMethod(chai.Assertion.prototype, 'equal', function (_super) { - * return function (str) { - * var obj = utils.flag(this, 'object'); - * if (obj instanceof Foo) { - * new chai.Assertion(obj.value).to.equal(str); - * } else { - * _super.apply(this, arguments); - * } - * } - * }); - * - * Can also be accessed directly from `chai.Assertion`. - * - * chai.Assertion.overwriteMethod('foo', fn); - * - * Then can be used as any other assertion. - * - * expect(myFoo).to.equal('bar'); - * - * @param {Object} ctx object whose method is to be overwritten - * @param {String} name of method to overwrite - * @param {Function} method function that returns a function to be used for name - * @name overwriteMethod - * @api public - */ - -module.exports = function (ctx, name, method) { - var _method = ctx[name] - , _super = function () { return this; }; - - if (_method && 'function' === typeof _method) - _super = _method; - - ctx[name] = function () { - var result = method(_super).apply(this, arguments); - return result === undefined ? this : result; - } -}; - -}); - -require.register("chai/lib/chai/utils/overwriteProperty.js", function (exports, module) { -/*! - * Chai - overwriteProperty utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -/** - * ### overwriteProperty (ctx, name, fn) - * - * Overwites an already existing property getter and provides - * access to previous value. Must return function to use as getter. - * - * utils.overwriteProperty(chai.Assertion.prototype, 'ok', function (_super) { - * return function () { - * var obj = utils.flag(this, 'object'); - * if (obj instanceof Foo) { - * new chai.Assertion(obj.name).to.equal('bar'); - * } else { - * _super.call(this); - * } - * } - * }); - * - * - * Can also be accessed directly from `chai.Assertion`. - * - * chai.Assertion.overwriteProperty('foo', fn); - * - * Then can be used as any other assertion. - * - * expect(myFoo).to.be.ok; - * - * @param {Object} ctx object whose property is to be overwritten - * @param {String} name of property to overwrite - * @param {Function} getter function that returns a getter function to be used for name - * @name overwriteProperty - * @api public - */ - -module.exports = function (ctx, name, getter) { - var _get = Object.getOwnPropertyDescriptor(ctx, name) - , _super = function () {}; - - if (_get && 'function' === typeof _get.get) - _super = _get.get - - Object.defineProperty(ctx, name, - { get: function () { - var result = getter(_super).call(this); - return result === undefined ? this : result; - } - , configurable: true - }); -}; - -}); - -require.register("chai/lib/chai/utils/overwriteChainableMethod.js", function (exports, module) { -/*! - * Chai - overwriteChainableMethod utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -/** - * ### overwriteChainableMethod (ctx, name, method, chainingBehavior) - * - * Overwites an already existing chainable method - * and provides access to the previous function or - * property. Must return functions to be used for - * name. - * - * utils.overwriteChainableMethod(chai.Assertion.prototype, 'length', - * function (_super) { - * } - * , function (_super) { - * } - * ); - * - * Can also be accessed directly from `chai.Assertion`. - * - * chai.Assertion.overwriteChainableMethod('foo', fn, fn); - * - * Then can be used as any other assertion. - * - * expect(myFoo).to.have.length(3); - * expect(myFoo).to.have.length.above(3); - * - * @param {Object} ctx object whose method / property is to be overwritten - * @param {String} name of method / property to overwrite - * @param {Function} method function that returns a function to be used for name - * @param {Function} chainingBehavior function that returns a function to be used for property - * @name overwriteChainableMethod - * @api public - */ - -module.exports = function (ctx, name, method, chainingBehavior) { - var chainableBehavior = ctx.__methods[name]; - - var _chainingBehavior = chainableBehavior.chainingBehavior; - chainableBehavior.chainingBehavior = function () { - var result = chainingBehavior(_chainingBehavior).call(this); - return result === undefined ? this : result; - }; - - var _method = chainableBehavior.method; - chainableBehavior.method = function () { - var result = method(_method).apply(this, arguments); - return result === undefined ? this : result; - }; -}; - -}); - -require.register("chai/lib/chai/utils/test.js", function (exports, module) { -/*! - * Chai - test utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -/*! - * Module dependancies - */ - -var flag = require('chai/lib/chai/utils/flag.js'); - -/** - * # test(object, expression) - * - * Test and object for expression. - * - * @param {Object} object (constructed Assertion) - * @param {Arguments} chai.Assertion.prototype.assert arguments - */ - -module.exports = function (obj, args) { - var negate = flag(obj, 'negate') - , expr = args[0]; - return negate ? !expr : expr; -}; - -}); - -require.register("chai/lib/chai/utils/transferFlags.js", function (exports, module) { -/*! - * Chai - transferFlags utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -/** - * ### transferFlags(assertion, object, includeAll = true) - * - * Transfer all the flags for `assertion` to `object`. If - * `includeAll` is set to `false`, then the base Chai - * assertion flags (namely `object`, `ssfi`, and `message`) - * will not be transferred. - * - * - * var newAssertion = new Assertion(); - * utils.transferFlags(assertion, newAssertion); - * - * var anotherAsseriton = new Assertion(myObj); - * utils.transferFlags(assertion, anotherAssertion, false); - * - * @param {Assertion} assertion the assertion to transfer the flags from - * @param {Object} object the object to transfer the flags to; usually a new assertion - * @param {Boolean} includeAll - * @name transferFlags - * @api private - */ - -module.exports = function (assertion, object, includeAll) { - var flags = assertion.__flags || (assertion.__flags = Object.create(null)); - - if (!object.__flags) { - object.__flags = Object.create(null); - } - - includeAll = arguments.length === 3 ? includeAll : true; - - for (var flag in flags) { - if (includeAll || - (flag !== 'object' && flag !== 'ssfi' && flag != 'message')) { - object.__flags[flag] = flags[flag]; - } - } -}; - -}); - -require.register("chai/lib/chai/utils/type.js", function (exports, module) { -/*! - * Chai - type utility - * Copyright(c) 2012-2014 Jake Luer - * MIT Licensed - */ - -/*! - * Detectable javascript natives - */ - -var natives = { - '[object Arguments]': 'arguments' - , '[object Array]': 'array' - , '[object Date]': 'date' - , '[object Function]': 'function' - , '[object Number]': 'number' - , '[object RegExp]': 'regexp' - , '[object String]': 'string' -}; - -/** - * ### type(object) - * - * Better implementation of `typeof` detection that can - * be used cross-browser. Handles the inconsistencies of - * Array, `null`, and `undefined` detection. - * - * utils.type({}) // 'object' - * utils.type(null) // `null' - * utils.type(undefined) // `undefined` - * utils.type([]) // `array` - * - * @param {Mixed} object to detect type of - * @name type - * @api private - */ - -module.exports = function (obj) { - var str = Object.prototype.toString.call(obj); - if (natives[str]) return natives[str]; - if (obj === null) return 'null'; - if (obj === undefined) return 'undefined'; - if (obj === Object(obj)) return 'object'; - return typeof obj; -}; - -}); - -if (typeof exports == "object") { - module.exports = require("chai"); -} else if (typeof define == "function" && define.amd) { - define("chai", [], function(){ return require("chai"); }); -} else { - (this || window)["chai"] = require("chai"); -} -})() \ No newline at end of file diff --git a/test/lib/mocha.js b/test/lib/mocha.js deleted file mode 100755 index 061ee3cf52b..00000000000 --- a/test/lib/mocha.js +++ /dev/null @@ -1,4597 +0,0 @@ -;(function(){ - - -// CommonJS require() - -function require(p){ - var path = require.resolve(p) - , mod = require.modules[path]; - if (!mod) throw new Error('failed to require "' + p + '"'); - if (!mod.exports) { - mod.exports = {}; - mod.call(mod.exports, mod, mod.exports, require.relative(path)); - } - return mod.exports; - } - -require.modules = {}; - -require.resolve = function (path){ - var orig = path - , reg = path + '.js' - , index = path + '/index.js'; - return require.modules[reg] && reg - || require.modules[index] && index - || orig; - }; - -require.register = function (path, fn){ - require.modules[path] = fn; - }; - -require.relative = function (parent) { - return function(p){ - if ('.' != p.charAt(0)) return require(p); - - var path = parent.split('/') - , segs = p.split('/'); - path.pop(); - - for (var i = 0; i < segs.length; i++) { - var seg = segs[i]; - if ('..' == seg) path.pop(); - else if ('.' != seg) path.push(seg); - } - - return require(path.join('/')); - }; - }; - - -require.register("browser/debug.js", function(module, exports, require){ - -module.exports = function(type){ - return function(){ - - } -}; -}); // module: browser/debug.js - -require.register("browser/diff.js", function(module, exports, require){ - -}); // module: browser/diff.js - -require.register("browser/events.js", function(module, exports, require){ - -/** - * Module exports. - */ - -exports.EventEmitter = EventEmitter; - -/** - * Check if `obj` is an array. - */ - -function isArray(obj) { - return '[object Array]' == {}.toString.call(obj); -} - -/** - * Event emitter constructor. - * - * @api public - */ - -function EventEmitter(){}; - -/** - * Adds a listener. - * - * @api public - */ - -EventEmitter.prototype.on = function (name, fn) { - if (!this.$events) { - this.$events = {}; - } - - if (!this.$events[name]) { - this.$events[name] = fn; - } else if (isArray(this.$events[name])) { - this.$events[name].push(fn); - } else { - this.$events[name] = [this.$events[name], fn]; - } - - return this; -}; - -EventEmitter.prototype.addListener = EventEmitter.prototype.on; - -/** - * Adds a volatile listener. - * - * @api public - */ - -EventEmitter.prototype.once = function (name, fn) { - var self = this; - - function on () { - self.removeListener(name, on); - fn.apply(this, arguments); - }; - - on.listener = fn; - this.on(name, on); - - return this; -}; - -/** - * Removes a listener. - * - * @api public - */ - -EventEmitter.prototype.removeListener = function (name, fn) { - if (this.$events && this.$events[name]) { - var list = this.$events[name]; - - if (isArray(list)) { - var pos = -1; - - for (var i = 0, l = list.length; i < l; i++) { - if (list[i] === fn || (list[i].listener && list[i].listener === fn)) { - pos = i; - break; - } - } - - if (pos < 0) { - return this; - } - - list.splice(pos, 1); - - if (!list.length) { - delete this.$events[name]; - } - } else if (list === fn || (list.listener && list.listener === fn)) { - delete this.$events[name]; - } - } - - return this; -}; - -/** - * Removes all listeners for an event. - * - * @api public - */ - -EventEmitter.prototype.removeAllListeners = function (name) { - if (name === undefined) { - this.$events = {}; - return this; - } - - if (this.$events && this.$events[name]) { - this.$events[name] = null; - } - - return this; -}; - -/** - * Gets all listeners for a certain event. - * - * @api public - */ - -EventEmitter.prototype.listeners = function (name) { - if (!this.$events) { - this.$events = {}; - } - - if (!this.$events[name]) { - this.$events[name] = []; - } - - if (!isArray(this.$events[name])) { - this.$events[name] = [this.$events[name]]; - } - - return this.$events[name]; -}; - -/** - * Emits an event. - * - * @api public - */ - -EventEmitter.prototype.emit = function (name) { - if (!this.$events) { - return false; - } - - var handler = this.$events[name]; - - if (!handler) { - return false; - } - - var args = [].slice.call(arguments, 1); - - if ('function' == typeof handler) { - handler.apply(this, args); - } else if (isArray(handler)) { - var listeners = handler.slice(); - - for (var i = 0, l = listeners.length; i < l; i++) { - listeners[i].apply(this, args); - } - } else { - return false; - } - - return true; -}; -}); // module: browser/events.js - -require.register("browser/fs.js", function(module, exports, require){ - -}); // module: browser/fs.js - -require.register("browser/path.js", function(module, exports, require){ - -}); // module: browser/path.js - -require.register("browser/progress.js", function(module, exports, require){ - -/** - * Expose `Progress`. - */ - -module.exports = Progress; - -/** - * Initialize a new `Progress` indicator. - */ - -function Progress() { - this.percent = 0; - this.size(0); - this.fontSize(11); - this.font('helvetica, arial, sans-serif'); -} - -/** - * Set progress size to `n`. - * - * @param {Number} n - * @return {Progress} for chaining - * @api public - */ - -Progress.prototype.size = function(n){ - this._size = n; - return this; -}; - -/** - * Set text to `str`. - * - * @param {String} str - * @return {Progress} for chaining - * @api public - */ - -Progress.prototype.text = function(str){ - this._text = str; - return this; -}; - -/** - * Set font size to `n`. - * - * @param {Number} n - * @return {Progress} for chaining - * @api public - */ - -Progress.prototype.fontSize = function(n){ - this._fontSize = n; - return this; -}; - -/** - * Set font `family`. - * - * @param {String} family - * @return {Progress} for chaining - */ - -Progress.prototype.font = function(family){ - this._font = family; - return this; -}; - -/** - * Update percentage to `n`. - * - * @param {Number} n - * @return {Progress} for chaining - */ - -Progress.prototype.update = function(n){ - this.percent = n; - return this; -}; - -/** - * Draw on `ctx`. - * - * @param {CanvasRenderingContext2d} ctx - * @return {Progress} for chaining - */ - -Progress.prototype.draw = function(ctx){ - var percent = Math.min(this.percent, 100) - , size = this._size - , half = size / 2 - , x = half - , y = half - , rad = half - 1 - , fontSize = this._fontSize; - - ctx.font = fontSize + 'px ' + this._font; - - var angle = Math.PI * 2 * (percent / 100); - ctx.clearRect(0, 0, size, size); - - // outer circle - ctx.strokeStyle = '#9f9f9f'; - ctx.beginPath(); - ctx.arc(x, y, rad, 0, angle, false); - ctx.stroke(); - - // inner circle - ctx.strokeStyle = '#eee'; - ctx.beginPath(); - ctx.arc(x, y, rad - 1, 0, angle, true); - ctx.stroke(); - - // text - var text = this._text || (percent | 0) + '%' - , w = ctx.measureText(text).width; - - ctx.fillText( - text - , x - w / 2 + 1 - , y + fontSize / 2 - 1); - - return this; -}; - -}); // module: browser/progress.js - -require.register("browser/tty.js", function(module, exports, require){ - -exports.isatty = function(){ - return true; -}; - -exports.getWindowSize = function(){ - return [window.innerHeight, window.innerWidth]; -}; -}); // module: browser/tty.js - -require.register("context.js", function(module, exports, require){ - -/** - * Expose `Context`. - */ - -module.exports = Context; - -/** - * Initialize a new `Context`. - * - * @api private - */ - -function Context(){} - -/** - * Set or get the context `Runnable` to `runnable`. - * - * @param {Runnable} runnable - * @return {Context} - * @api private - */ - -Context.prototype.runnable = function(runnable){ - if (0 == arguments.length) return this._runnable; - this.test = this._runnable = runnable; - return this; -}; - -/** - * Set test timeout `ms`. - * - * @param {Number} ms - * @return {Context} self - * @api private - */ - -Context.prototype.timeout = function(ms){ - this.runnable().timeout(ms); - return this; -}; - -/** - * Inspect the context void of `._runnable`. - * - * @return {String} - * @api private - */ - -Context.prototype.inspect = function(){ - return JSON.stringify(this, function(key, val){ - if ('_runnable' == key) return; - if ('test' == key) return; - return val; - }, 2); -}; - -}); // module: context.js - -require.register("hook.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Runnable = require('./runnable'); - -/** - * Expose `Hook`. - */ - -module.exports = Hook; - -/** - * Initialize a new `Hook` with the given `title` and callback `fn`. - * - * @param {String} title - * @param {Function} fn - * @api private - */ - -function Hook(title, fn) { - Runnable.call(this, title, fn); - this.type = 'hook'; -} - -/** - * Inherit from `Runnable.prototype`. - */ - -Hook.prototype = new Runnable; -Hook.prototype.constructor = Hook; - - -/** - * Get or set the test `err`. - * - * @param {Error} err - * @return {Error} - * @api public - */ - -Hook.prototype.error = function(err){ - if (0 == arguments.length) { - var err = this._error; - this._error = null; - return err; - } - - this._error = err; -}; - - -}); // module: hook.js - -require.register("interfaces/bdd.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Suite = require('../suite') - , Test = require('../test'); - -/** - * BDD-style interface: - * - * describe('Array', function(){ - * describe('#indexOf()', function(){ - * it('should return -1 when not present', function(){ - * - * }); - * - * it('should return the index when present', function(){ - * - * }); - * }); - * }); - * - */ - -module.exports = function(suite){ - var suites = [suite]; - - suite.on('pre-require', function(context){ - - /** - * Execute before running tests. - */ - - context.before = function(fn){ - suites[0].beforeAll(fn); - }; - - /** - * Execute after running tests. - */ - - context.after = function(fn){ - suites[0].afterAll(fn); - }; - - /** - * Execute before each test case. - */ - - context.beforeEach = function(fn){ - suites[0].beforeEach(fn); - }; - - /** - * Execute after each test case. - */ - - context.afterEach = function(fn){ - suites[0].afterEach(fn); - }; - - /** - * Pending describe. - */ - - context.xdescribe = context.xcontext = function(title, fn){ - var suite = Suite.create(suites[0], title); - suite.pending = true; - suites.unshift(suite); - fn(); - suites.shift(); - }; - - /** - * Describe a "suite" with the given `title` - * and callback `fn` containing nested suites - * and/or tests. - */ - - context.describe = context.context = function(title, fn){ - var suite = Suite.create(suites[0], title); - suites.unshift(suite); - fn(); - suites.shift(); - }; - - /** - * Describe a specification or test-case - * with the given `title` and callback `fn` - * acting as a thunk. - */ - - context.it = context.specify = function(title, fn){ - var suite = suites[0]; - if (suite.pending) var fn = null; - suite.addTest(new Test(title, fn)); - }; - - /** - * Pending test case. - */ - - context.xit = context.xspecify = function(title){ - context.it(title); - }; - }); -}; - -}); // module: interfaces/bdd.js - -require.register("interfaces/exports.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Suite = require('../suite') - , Test = require('../test'); - -/** - * TDD-style interface: - * - * exports.Array = { - * '#indexOf()': { - * 'should return -1 when the value is not present': function(){ - * - * }, - * - * 'should return the correct index when the value is present': function(){ - * - * } - * } - * }; - * - */ - -module.exports = function(suite){ - var suites = [suite]; - - suite.on('require', visit); - - function visit(obj) { - var suite; - for (var key in obj) { - if ('function' == typeof obj[key]) { - var fn = obj[key]; - switch (key) { - case 'before': - suites[0].beforeAll(fn); - break; - case 'after': - suites[0].afterAll(fn); - break; - case 'beforeEach': - suites[0].beforeEach(fn); - break; - case 'afterEach': - suites[0].afterEach(fn); - break; - default: - suites[0].addTest(new Test(key, fn)); - } - } else { - var suite = Suite.create(suites[0], key); - suites.unshift(suite); - visit(obj[key]); - suites.shift(); - } - } - } -}; -}); // module: interfaces/exports.js - -require.register("interfaces/index.js", function(module, exports, require){ - -exports.bdd = require('./bdd'); -exports.tdd = require('./tdd'); -exports.qunit = require('./qunit'); -exports.exports = require('./exports'); - -}); // module: interfaces/index.js - -require.register("interfaces/qunit.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Suite = require('../suite') - , Test = require('../test'); - -/** - * QUnit-style interface: - * - * suite('Array'); - * - * test('#length', function(){ - * var arr = [1,2,3]; - * ok(arr.length == 3); - * }); - * - * test('#indexOf()', function(){ - * var arr = [1,2,3]; - * ok(arr.indexOf(1) == 0); - * ok(arr.indexOf(2) == 1); - * ok(arr.indexOf(3) == 2); - * }); - * - * suite('String'); - * - * test('#length', function(){ - * ok('foo'.length == 3); - * }); - * - */ - -module.exports = function(suite){ - var suites = [suite]; - - suite.on('pre-require', function(context){ - - /** - * Execute before running tests. - */ - - context.before = function(fn){ - suites[0].beforeAll(fn); - }; - - /** - * Execute after running tests. - */ - - context.after = function(fn){ - suites[0].afterAll(fn); - }; - - /** - * Execute before each test case. - */ - - context.beforeEach = function(fn){ - suites[0].beforeEach(fn); - }; - - /** - * Execute after each test case. - */ - - context.afterEach = function(fn){ - suites[0].afterEach(fn); - }; - - /** - * Describe a "suite" with the given `title`. - */ - - context.suite = function(title){ - if (suites.length > 1) suites.shift(); - var suite = Suite.create(suites[0], title); - suites.unshift(suite); - }; - - /** - * Describe a specification or test-case - * with the given `title` and callback `fn` - * acting as a thunk. - */ - - context.test = function(title, fn){ - suites[0].addTest(new Test(title, fn)); - }; - }); -}; - -}); // module: interfaces/qunit.js - -require.register("interfaces/tdd.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Suite = require('../suite') - , Test = require('../test'); - -/** - * TDD-style interface: - * - * suite('Array', function(){ - * suite('#indexOf()', function(){ - * suiteSetup(function(){ - * - * }); - * - * test('should return -1 when not present', function(){ - * - * }); - * - * test('should return the index when present', function(){ - * - * }); - * - * suiteTeardown(function(){ - * - * }); - * }); - * }); - * - */ - -module.exports = function(suite){ - var suites = [suite]; - - suite.on('pre-require', function(context){ - - /** - * Execute before each test case. - */ - - context.setup = function(fn){ - suites[0].beforeEach(fn); - }; - - /** - * Execute after each test case. - */ - - context.teardown = function(fn){ - suites[0].afterEach(fn); - }; - - /** - * Execute before the suite. - */ - - context.suiteSetup = function(fn){ - suites[0].beforeAll(fn); - }; - - /** - * Execute after the suite. - */ - - context.suiteTeardown = function(fn){ - suites[0].afterAll(fn); - }; - - /** - * Describe a "suite" with the given `title` - * and callback `fn` containing nested suites - * and/or tests. - */ - - context.suite = function(title, fn){ - var suite = Suite.create(suites[0], title); - suites.unshift(suite); - fn(); - suites.shift(); - }; - - /** - * Describe a specification or test-case - * with the given `title` and callback `fn` - * acting as a thunk. - */ - - context.test = function(title, fn){ - suites[0].addTest(new Test(title, fn)); - }; - }); -}; - -}); // module: interfaces/tdd.js - -require.register("mocha.js", function(module, exports, require){ -/*! - * mocha - * Copyright(c) 2011 TJ Holowaychuk - * MIT Licensed - */ - -/** - * Module dependencies. - */ - -var path = require('browser/path'); - -/** - * Expose `Mocha`. - */ - -exports = module.exports = Mocha; - -/** - * Expose internals. - */ - -exports.utils = require('./utils'); -exports.interfaces = require('./interfaces'); -exports.reporters = require('./reporters'); -exports.Runnable = require('./runnable'); -exports.Context = require('./context'); -exports.Runner = require('./runner'); -exports.Suite = require('./suite'); -exports.Hook = require('./hook'); -exports.Test = require('./test'); - -/** - * Return image `name` path. - * - * @param {String} name - * @return {String} - * @api private - */ - -function image(name) { - return __dirname + '/../images/' + name + '.png'; -} - -/** - * Setup mocha with `options`. - * - * Options: - * - * - `ui` name "bdd", "tdd", "exports" etc - * - `reporter` reporter instance, defaults to `mocha.reporters.Dot` - * - `globals` array of accepted globals - * - `timeout` timeout in milliseconds - * - `ignoreLeaks` ignore global leaks - * - `grep` string or regexp to filter tests with - * - * @param {Object} options - * @api public - */ - -function Mocha(options) { - options = options || {}; - this.files = []; - this.options = options; - this.grep(options.grep); - this.suite = new exports.Suite('', new exports.Context); - this.ui(options.ui); - this.reporter(options.reporter); - if (options.timeout) this.suite.timeout(options.timeout); -} - -/** - * Add test `file`. - * - * @param {String} file - * @api public - */ - -Mocha.prototype.addFile = function(file){ - this.files.push(file); - return this; -}; - -/** - * Set reporter to `name`, defaults to "dot". - * - * @param {String} name - * @api public - */ - -Mocha.prototype.reporter = function(name){ - name = name || 'dot'; - this._reporter = require('./reporters/' + name); - if (!this._reporter) throw new Error('invalid reporter "' + name + '"'); - return this; -}; - -/** - * Set test UI `name`, defaults to "bdd". - * - * @param {String} bdd - * @api public - */ - -Mocha.prototype.ui = function(name){ - name = name || 'bdd'; - this._ui = exports.interfaces[name]; - if (!this._ui) throw new Error('invalid interface "' + name + '"'); - this._ui = this._ui(this.suite); - return this; -}; - -/** - * Load registered files. - * - * @api private - */ - -Mocha.prototype.loadFiles = function(fn){ - var suite = this.suite; - var pending = this.files.length; - this.files.forEach(function(file){ - file = path.resolve(file); - suite.emit('pre-require', global, file); - suite.emit('require', require(file), file); - suite.emit('post-require', global, file); - --pending || (fn && fn()); - }); -}; - -/** - * Enable growl support. - * - * @api private - */ - -Mocha.prototype._growl = function(runner, reporter) { - var notify = require('growl'); - - runner.on('end', function(){ - var stats = reporter.stats; - if (stats.failures) { - var msg = stats.failures + ' of ' + runner.total + ' tests failed'; - notify(msg, { name: 'mocha', title: 'Failed', image: image('error') }); - } else { - notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', { - name: 'mocha' - , title: 'Passed' - , image: image('ok') - }); - } - }); -}; - -/** - * Add regexp to grep for to the options object - * - * @param {RegExp} or {String} re - * @return {Mocha} - * @api public - */ - -Mocha.prototype.grep = function(re){ - this.options.grep = 'string' == typeof re - ? new RegExp(re) - : re; - return this; -}; - -/** - * Invert `.grep()` matches. - * - * @return {Mocha} - * @api public - */ - -Mocha.prototype.invert = function(){ - this.options.invert = true; - return this; -}; - -/** - * Ignore global leaks. - * - * @return {Mocha} - * @api public - */ - -Mocha.prototype.ignoreLeaks = function(){ - this.options.ignoreLeaks = true; - return this; -}; - -/** - * Enable growl support. - * - * @return {Mocha} - * @api public - */ - -Mocha.prototype.growl = function(){ - this.options.growl = true; - return this; -}; - -/** - * Ignore `globals`. - * - * @param {Array} globals - * @return {Mocha} - * @api public - */ - -Mocha.prototype.globals = function(globals){ - this.options.globals = globals; - return this; -}; - -/** - * Run tests and invoke `fn()` when complete. - * - * @param {Function} fn - * @return {Runner} - * @api public - */ - -Mocha.prototype.run = function(fn){ - this.loadFiles(); - var suite = this.suite; - var options = this.options; - var runner = new exports.Runner(suite); - var reporter = new this._reporter(runner); - runner.ignoreLeaks = options.ignoreLeaks; - if (options.grep) runner.grep(options.grep, options.invert); - if (options.globals) runner.globals(options.globals); - if (options.growl) this._growl(runner, reporter); - return runner.run(fn); -}; - -}); // module: mocha.js - -require.register("reporters/base.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var tty = require('browser/tty') - , diff = require('browser/diff'); - -/** - * Save timer references to avoid Sinon interfering (see GH-237). - */ - -var Date = global.Date - , setTimeout = global.setTimeout - , setInterval = global.setInterval - , clearTimeout = global.clearTimeout - , clearInterval = global.clearInterval; - -/** - * Check if both stdio streams are associated with a tty. - */ - -var isatty = tty.isatty(1) && tty.isatty(2); - -/** - * Expose `Base`. - */ - -exports = module.exports = Base; - -/** - * Enable coloring by default. - */ - -exports.useColors = isatty; - -/** - * Default color map. - */ - -exports.colors = { - 'pass': 90 - , 'fail': 31 - , 'bright pass': 92 - , 'bright fail': 91 - , 'bright yellow': 93 - , 'pending': 36 - , 'suite': 0 - , 'error title': 0 - , 'error message': 31 - , 'error stack': 90 - , 'checkmark': 32 - , 'fast': 90 - , 'medium': 33 - , 'slow': 31 - , 'green': 32 - , 'light': 90 - , 'diff gutter': 90 - , 'diff added': 42 - , 'diff removed': 41 -}; - -/** - * Color `str` with the given `type`, - * allowing colors to be disabled, - * as well as user-defined color - * schemes. - * - * @param {String} type - * @param {String} str - * @return {String} - * @api private - */ - -var color = exports.color = function(type, str) { - if (!exports.useColors) return str; - return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m'; -}; - -/** - * Expose term window size, with some - * defaults for when stderr is not a tty. - */ - -exports.window = { - width: isatty - ? process.stdout.getWindowSize - ? process.stdout.getWindowSize(1)[0] - : tty.getWindowSize()[1] - : 75 -}; - -/** - * Expose some basic cursor interactions - * that are common among reporters. - */ - -exports.cursor = { - hide: function(){ - process.stdout.write('\u001b[?25l'); - }, - - show: function(){ - process.stdout.write('\u001b[?25h'); - }, - - deleteLine: function(){ - process.stdout.write('\u001b[2K'); - }, - - beginningOfLine: function(){ - process.stdout.write('\u001b[0G'); - }, - - CR: function(){ - exports.cursor.deleteLine(); - exports.cursor.beginningOfLine(); - } -}; - -/** - * A test is considered slow if it - * exceeds the following value in milliseconds. - */ - -exports.slow = 75; - -/** - * Outut the given `failures` as a list. - * - * @param {Array} failures - * @api public - */ - -exports.list = function(failures){ - console.error(); - failures.forEach(function(test, i){ - // format - var fmt = color('error title', ' %s) %s:\n') - + color('error message', ' %s') - + color('error stack', '\n%s\n'); - - // msg - var err = test.err - , message = err.message || '' - , stack = err.stack || message - , index = stack.indexOf(message) + message.length - , msg = stack.slice(0, index) - , actual = err.actual - , expected = err.expected; - - // actual / expected diff - if ('string' == typeof actual && 'string' == typeof expected) { - var len = Math.max(actual.length, expected.length); - - if (len < 20) msg = errorDiff(err, 'Chars'); - else msg = errorDiff(err, 'Words'); - - // linenos - var lines = msg.split('\n'); - if (lines.length > 4) { - var width = String(lines.length).length; - msg = lines.map(function(str, i){ - return pad(++i, width) + ' |' + ' ' + str; - }).join('\n'); - } - - // legend - msg = '\n' - + color('diff removed', 'actual') - + ' ' - + color('diff added', 'expected') - + '\n\n' - + msg - + '\n'; - - // indent - msg = msg.replace(/^/gm, ' '); - - fmt = color('error title', ' %s) %s:\n%s') - + color('error stack', '\n%s\n'); - } - - // indent stack trace without msg - stack = stack.slice(index ? index + 1 : index) - .replace(/^/gm, ' '); - - console.error(fmt, (i + 1), test.fullTitle(), msg, stack); - }); -}; - -/** - * Initialize a new `Base` reporter. - * - * All other reporters generally - * inherit from this reporter, providing - * stats such as test duration, number - * of tests passed / failed etc. - * - * @param {Runner} runner - * @api public - */ - -function Base(runner) { - var self = this - , stats = this.stats = { suites: 0, tests: 0, passes: 0, pending: 0, failures: 0 } - , failures = this.failures = []; - - if (!runner) return; - this.runner = runner; - - runner.on('start', function(){ - stats.start = new Date; - }); - - runner.on('suite', function(suite){ - stats.suites = stats.suites || 0; - suite.root || stats.suites++; - }); - - runner.on('test end', function(test){ - stats.tests = stats.tests || 0; - stats.tests++; - }); - - runner.on('pass', function(test){ - stats.passes = stats.passes || 0; - - var medium = exports.slow / 2; - test.speed = test.duration > exports.slow - ? 'slow' - : test.duration > medium - ? 'medium' - : 'fast'; - - stats.passes++; - }); - - runner.on('fail', function(test, err){ - stats.failures = stats.failures || 0; - stats.failures++; - test.err = err; - failures.push(test); - }); - - runner.on('end', function(){ - stats.end = new Date; - stats.duration = new Date - stats.start; - }); - - runner.on('pending', function(){ - stats.pending++; - }); -} - -/** - * Output common epilogue used by many of - * the bundled reporters. - * - * @api public - */ - -Base.prototype.epilogue = function(){ - var stats = this.stats - , fmt - , tests; - - console.log(); - - function pluralize(n) { - return 1 == n ? 'test' : 'tests'; - } - - // failure - if (stats.failures) { - fmt = color('bright fail', ' ✖') - + color('fail', ' %d of %d %s failed') - + color('light', ':') - - console.error(fmt, - stats.failures, - this.runner.total, - pluralize(this.runner.total)); - - Base.list(this.failures); - console.error(); - return; - } - - // pass - fmt = color('bright pass', ' ✔') - + color('green', ' %d %s complete') - + color('light', ' (%dms)'); - - console.log(fmt, - stats.tests || 0, - pluralize(stats.tests), - stats.duration); - - // pending - if (stats.pending) { - fmt = color('pending', ' •') - + color('pending', ' %d %s pending'); - - console.log(fmt, stats.pending, pluralize(stats.pending)); - } - - console.log(); -}; - -/** - * Pad the given `str` to `len`. - * - * @param {String} str - * @param {String} len - * @return {String} - * @api private - */ - -function pad(str, len) { - str = String(str); - return Array(len - str.length + 1).join(' ') + str; -} - -/** - * Return a character diff for `err`. - * - * @param {Error} err - * @return {String} - * @api private - */ - -function errorDiff(err, type) { - return diff['diff' + type](err.actual, err.expected).map(function(str){ - if (/^(\n+)$/.test(str.value)) str.value = Array(++RegExp.$1.length).join(''); - if (str.added) return colorLines('diff added', str.value); - if (str.removed) return colorLines('diff removed', str.value); - return str.value; - }).join(''); -} - -/** - * Color lines for `str`, using the color `name`. - * - * @param {String} name - * @param {String} str - * @return {String} - * @api private - */ - -function colorLines(name, str) { - return str.split('\n').map(function(str){ - return color(name, str); - }).join('\n'); -} - -}); // module: reporters/base.js - -require.register("reporters/doc.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , utils = require('../utils'); - -/** - * Expose `Doc`. - */ - -exports = module.exports = Doc; - -/** - * Initialize a new `Doc` reporter. - * - * @param {Runner} runner - * @api public - */ - -function Doc(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , total = runner.total - , indents = 2; - - function indent() { - return Array(indents).join(' '); - } - - runner.on('suite', function(suite){ - if (suite.root) return; - ++indents; - console.log('%s
', indent()); - ++indents; - console.log('%s

%s

', indent(), suite.title); - console.log('%s
', indent()); - }); - - runner.on('suite end', function(suite){ - if (suite.root) return; - console.log('%s
', indent()); - --indents; - console.log('%s
', indent()); - --indents; - }); - - runner.on('pass', function(test){ - console.log('%s
%s
', indent(), test.title); - var code = utils.escape(utils.clean(test.fn.toString())); - console.log('%s
%s
', indent(), code); - }); -} - -}); // module: reporters/doc.js - -require.register("reporters/dot.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , color = Base.color; - -/** - * Expose `Dot`. - */ - -exports = module.exports = Dot; - -/** - * Initialize a new `Dot` matrix test reporter. - * - * @param {Runner} runner - * @api public - */ - -function Dot(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , width = Base.window.width * .75 | 0 - , c = '․' - , n = 0; - - runner.on('start', function(){ - process.stdout.write('\n '); - }); - - runner.on('pending', function(test){ - process.stdout.write(color('pending', c)); - }); - - runner.on('pass', function(test){ - if (++n % width == 0) process.stdout.write('\n '); - if ('slow' == test.speed) { - process.stdout.write(color('bright yellow', c)); - } else { - process.stdout.write(color(test.speed, c)); - } - }); - - runner.on('fail', function(test, err){ - if (++n % width == 0) process.stdout.write('\n '); - process.stdout.write(color('fail', c)); - }); - - runner.on('end', function(){ - console.log(); - self.epilogue(); - }); -} - -/** - * Inherit from `Base.prototype`. - */ - -Dot.prototype = new Base; -Dot.prototype.constructor = Dot; - -}); // module: reporters/dot.js - -require.register("reporters/html-cov.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var JSONCov = require('./json-cov') - , fs = require('browser/fs'); - -/** - * Expose `HTMLCov`. - */ - -exports = module.exports = HTMLCov; - -/** - * Initialize a new `JsCoverage` reporter. - * - * @param {Runner} runner - * @api public - */ - -function HTMLCov(runner) { - var jade = require('jade') - , file = __dirname + '/templates/coverage.jade' - , str = fs.readFileSync(file, 'utf8') - , fn = jade.compile(str, { filename: file }) - , self = this; - - JSONCov.call(this, runner, false); - - runner.on('end', function(){ - process.stdout.write(fn({ - cov: self.cov - , coverageClass: coverageClass - })); - }); -} - -/** - * Return coverage class for `n`. - * - * @return {String} - * @api private - */ - -function coverageClass(n) { - if (n >= 75) return 'high'; - if (n >= 50) return 'medium'; - if (n >= 25) return 'low'; - return 'terrible'; -} -}); // module: reporters/html-cov.js - -require.register("reporters/html.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , utils = require('../utils') - , Progress = require('../browser/progress') - , escape = utils.escape; - -/** - * Save timer references to avoid Sinon interfering (see GH-237). - */ - -var Date = global.Date - , setTimeout = global.setTimeout - , setInterval = global.setInterval - , clearTimeout = global.clearTimeout - , clearInterval = global.clearInterval; - -/** - * Expose `Doc`. - */ - -exports = module.exports = HTML; - -/** - * Stats template. - */ - -var statsTemplate = ''; - -/** - * Initialize a new `Doc` reporter. - * - * @param {Runner} runner - * @api public - */ - -function HTML(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , total = runner.total - , root = document.getElementById('mocha') - , stat = fragment(statsTemplate) - , items = stat.getElementsByTagName('li') - , passes = items[1].getElementsByTagName('em')[0] - , passesLink = items[1].getElementsByTagName('a')[0] - , failures = items[2].getElementsByTagName('em')[0] - , failuresLink = items[2].getElementsByTagName('a')[0] - , duration = items[3].getElementsByTagName('em')[0] - , canvas = stat.getElementsByTagName('canvas')[0] - , report = fragment('
    ') - , stack = [report] - , progress - , ctx - - if (canvas.getContext) { - ctx = canvas.getContext('2d'); - progress = new Progress; - } - - if (!root) return error('#mocha div missing, add it to your document'); - - // pass toggle - on(passesLink, 'click', function () { - var className = /pass/.test(report.className) ? '' : ' pass'; - report.className = report.className.replace(/fail|pass/g, '') + className; - }); - - // failure toggle - on(failuresLink, 'click', function () { - var className = /fail/.test(report.className) ? '' : ' fail'; - report.className = report.className.replace(/fail|pass/g, '') + className; - }); - - root.appendChild(stat); - root.appendChild(report); - - if (progress) progress.size(40); - - runner.on('suite', function(suite){ - if (suite.root) return; - - // suite - var url = location.protocol + '//' + location.host + location.pathname + '?grep=^' + utils.escapeRegexp(suite.fullTitle()); - var el = fragment('
  • %s

  • ', url, escape(suite.title)); - - // container - stack[0].appendChild(el); - stack.unshift(document.createElement('ul')); - el.appendChild(stack[0]); - }); - - runner.on('suite end', function(suite){ - if (suite.root) return; - stack.shift(); - }); - - runner.on('fail', function(test, err){ - if ('hook' == test.type || err.uncaught) runner.emit('test end', test); - }); - - runner.on('test end', function(test){ - window.scrollTo(0, document.body.scrollHeight); - - // TODO: add to stats - var percent = stats.tests / total * 100 | 0; - if (progress) progress.update(percent).draw(ctx); - - // update stats - var ms = new Date - stats.start; - text(passes, stats.passes); - text(failures, stats.failures); - text(duration, (ms / 1000).toFixed(2)); - - // test - if ('passed' == test.state) { - var el = fragment('
  • %e%ems

  • ', test.speed, test.title, test.duration); - } else if (test.pending) { - var el = fragment('
  • %e

  • ', test.title); - } else { - var el = fragment('
  • %e

  • ', test.title); - var str = test.err.stack || test.err.toString(); - - // FF / Opera do not add the message - if (!~str.indexOf(test.err.message)) { - str = test.err.message + '\n' + str; - } - - // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we - // check for the result of the stringifying. - if ('[object Error]' == str) str = test.err.message; - - // Safari doesn't give you a stack. Let's at least provide a source line. - if (!test.err.stack && test.err.sourceURL && test.err.line !== undefined) { - str += "\n(" + test.err.sourceURL + ":" + test.err.line + ")"; - } - - el.appendChild(fragment('
    %e
    ', str)); - } - - // toggle code - // TODO: defer - if (!test.pending) { - var h2 = el.getElementsByTagName('h2')[0]; - - on(h2, 'click', function(){ - pre.style.display = 'none' == pre.style.display - ? 'inline-block' - : 'none'; - }); - - var pre = fragment('
    %e
    ', utils.clean(test.fn.toString())); - el.appendChild(pre); - pre.style.display = 'none'; - } - - stack[0].appendChild(el); - }); -} - -/** - * Display error `msg`. - */ - -function error(msg) { - document.body.appendChild(fragment('
    %s
    ', msg)); -} - -/** - * Return a DOM fragment from `html`. - */ - -function fragment(html) { - var args = arguments - , div = document.createElement('div') - , i = 1; - - div.innerHTML = html.replace(/%([se])/g, function(_, type){ - switch (type) { - case 's': return String(args[i++]); - case 'e': return escape(args[i++]); - } - }); - - return div.firstChild; -} - -/** - * Set `el` text to `str`. - */ - -function text(el, str) { - if (el.textContent) { - el.textContent = str; - } else { - el.innerText = str; - } -} - -/** - * Listen on `event` with callback `fn`. - */ - -function on(el, event, fn) { - if (el.addEventListener) { - el.addEventListener(event, fn, false); - } else { - el.attachEvent('on' + event, fn); - } -} - -}); // module: reporters/html.js - -require.register("reporters/index.js", function(module, exports, require){ - -exports.Base = require('./base'); -exports.Dot = require('./dot'); -exports.Doc = require('./doc'); -exports.TAP = require('./tap'); -exports.JSON = require('./json'); -exports.HTML = require('./html'); -exports.List = require('./list'); -exports.Min = require('./min'); -exports.Spec = require('./spec'); -exports.Nyan = require('./nyan'); -exports.XUnit = require('./xunit'); -exports.Progress = require('./progress'); -exports.Landing = require('./landing'); -exports.JSONCov = require('./json-cov'); -exports.HTMLCov = require('./html-cov'); -exports.JSONStream = require('./json-stream'); -exports.Teamcity = require('./teamcity'); - -}); // module: reporters/index.js - -require.register("reporters/json-cov.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Base = require('./base'); - -/** - * Expose `JSONCov`. - */ - -exports = module.exports = JSONCov; - -/** - * Initialize a new `JsCoverage` reporter. - * - * @param {Runner} runner - * @param {Boolean} output - * @api public - */ - -function JSONCov(runner, output) { - var self = this - , output = 1 == arguments.length ? true : output; - - Base.call(this, runner); - - var tests = [] - , failures = [] - , passes = []; - - runner.on('test end', function(test){ - tests.push(test); - }); - - runner.on('pass', function(test){ - passes.push(test); - }); - - runner.on('fail', function(test){ - failures.push(test); - }); - - runner.on('end', function(){ - var cov = global._$jscoverage || {}; - var result = self.cov = map(cov); - result.stats = self.stats; - result.tests = tests.map(clean); - result.failures = failures.map(clean); - result.passes = passes.map(clean); - if (!output) return; - process.stdout.write(JSON.stringify(result, null, 2 )); - }); -} - -/** - * Map jscoverage data to a JSON structure - * suitable for reporting. - * - * @param {Object} cov - * @return {Object} - * @api private - */ - -function map(cov) { - var ret = { - instrumentation: 'node-jscoverage' - , sloc: 0 - , hits: 0 - , misses: 0 - , coverage: 0 - , files: [] - }; - - for (var filename in cov) { - var data = coverage(filename, cov[filename]); - ret.files.push(data); - ret.hits += data.hits; - ret.misses += data.misses; - ret.sloc += data.sloc; - } - - if (ret.sloc > 0) { - ret.coverage = (ret.hits / ret.sloc) * 100; - } - - return ret; -}; - -/** - * Map jscoverage data for a single source file - * to a JSON structure suitable for reporting. - * - * @param {String} filename name of the source file - * @param {Object} data jscoverage coverage data - * @return {Object} - * @api private - */ - -function coverage(filename, data) { - var ret = { - filename: filename, - coverage: 0, - hits: 0, - misses: 0, - sloc: 0, - source: {} - }; - - data.source.forEach(function(line, num){ - num++; - - if (data[num] === 0) { - ret.misses++; - ret.sloc++; - } else if (data[num] !== undefined) { - ret.hits++; - ret.sloc++; - } - - ret.source[num] = { - source: line - , coverage: data[num] === undefined - ? '' - : data[num] - }; - }); - - ret.coverage = ret.hits / ret.sloc * 100; - - return ret; -} - -/** - * Return a plain-object representation of `test` - * free of cyclic properties etc. - * - * @param {Object} test - * @return {Object} - * @api private - */ - -function clean(test) { - return { - title: test.title - , fullTitle: test.fullTitle() - , duration: test.duration - } -} - -}); // module: reporters/json-cov.js - -require.register("reporters/json-stream.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , color = Base.color; - -/** - * Expose `List`. - */ - -exports = module.exports = List; - -/** - * Initialize a new `List` test reporter. - * - * @param {Runner} runner - * @api public - */ - -function List(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , total = runner.total; - - runner.on('start', function(){ - console.log(JSON.stringify(['start', { total: total }])); - }); - - runner.on('pass', function(test){ - console.log(JSON.stringify(['pass', clean(test)])); - }); - - runner.on('fail', function(test, err){ - console.log(JSON.stringify(['fail', clean(test)])); - }); - - runner.on('end', function(){ - process.stdout.write(JSON.stringify(['end', self.stats])); - }); -} - -/** - * Return a plain-object representation of `test` - * free of cyclic properties etc. - * - * @param {Object} test - * @return {Object} - * @api private - */ - -function clean(test) { - return { - title: test.title - , fullTitle: test.fullTitle() - , duration: test.duration - } -} -}); // module: reporters/json-stream.js - -require.register("reporters/json.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , cursor = Base.cursor - , color = Base.color; - -/** - * Expose `JSON`. - */ - -exports = module.exports = JSONReporter; - -/** - * Initialize a new `JSON` reporter. - * - * @param {Runner} runner - * @api public - */ - -function JSONReporter(runner) { - var self = this; - Base.call(this, runner); - - var tests = [] - , failures = [] - , passes = []; - - runner.on('test end', function(test){ - tests.push(test); - }); - - runner.on('pass', function(test){ - passes.push(test); - }); - - runner.on('fail', function(test){ - failures.push(test); - }); - - runner.on('end', function(){ - var obj = { - stats: self.stats - , tests: tests.map(clean) - , failures: failures.map(clean) - , passes: passes.map(clean) - }; - - process.stdout.write(JSON.stringify(obj, null, 2)); - }); -} - -/** - * Return a plain-object representation of `test` - * free of cyclic properties etc. - * - * @param {Object} test - * @return {Object} - * @api private - */ - -function clean(test) { - return { - title: test.title - , fullTitle: test.fullTitle() - , duration: test.duration - } -} -}); // module: reporters/json.js - -require.register("reporters/landing.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , cursor = Base.cursor - , color = Base.color; - -/** - * Expose `Landing`. - */ - -exports = module.exports = Landing; - -/** - * Airplane color. - */ - -Base.colors.plane = 0; - -/** - * Airplane crash color. - */ - -Base.colors['plane crash'] = 31; - -/** - * Runway color. - */ - -Base.colors.runway = 90; - -/** - * Initialize a new `Landing` reporter. - * - * @param {Runner} runner - * @api public - */ - -function Landing(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , width = Base.window.width * .75 | 0 - , total = runner.total - , stream = process.stdout - , plane = color('plane', '✈') - , crashed = -1 - , n = 0; - - function runway() { - var buf = Array(width).join('-'); - return ' ' + color('runway', buf); - } - - runner.on('start', function(){ - stream.write('\n '); - cursor.hide(); - }); - - runner.on('test end', function(test){ - // check if the plane crashed - var col = -1 == crashed - ? width * ++n / total | 0 - : crashed; - - // show the crash - if ('failed' == test.state) { - plane = color('plane crash', '✈'); - crashed = col; - } - - // render landing strip - stream.write('\u001b[4F\n\n'); - stream.write(runway()); - stream.write('\n '); - stream.write(color('runway', Array(col).join('⋅'))); - stream.write(plane) - stream.write(color('runway', Array(width - col).join('⋅') + '\n')); - stream.write(runway()); - stream.write('\u001b[0m'); - }); - - runner.on('end', function(){ - cursor.show(); - console.log(); - self.epilogue(); - }); -} - -/** - * Inherit from `Base.prototype`. - */ - -Landing.prototype = new Base; -Landing.prototype.constructor = Landing; - -}); // module: reporters/landing.js - -require.register("reporters/list.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , cursor = Base.cursor - , color = Base.color; - -/** - * Expose `List`. - */ - -exports = module.exports = List; - -/** - * Initialize a new `List` test reporter. - * - * @param {Runner} runner - * @api public - */ - -function List(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , n = 0; - - runner.on('start', function(){ - console.log(); - }); - - runner.on('test', function(test){ - process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); - }); - - runner.on('pending', function(test){ - var fmt = color('checkmark', ' -') - + color('pending', ' %s'); - console.log(fmt, test.fullTitle()); - }); - - runner.on('pass', function(test){ - var fmt = color('checkmark', ' ✓') - + color('pass', ' %s: ') - + color(test.speed, '%dms'); - cursor.CR(); - console.log(fmt, test.fullTitle(), test.duration); - }); - - runner.on('fail', function(test, err){ - cursor.CR(); - console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); - }); - - runner.on('end', self.epilogue.bind(self)); -} - -/** - * Inherit from `Base.prototype`. - */ - -List.prototype = new Base; -List.prototype.constructor = List; - - -}); // module: reporters/list.js - -require.register("reporters/markdown.js", function(module, exports, require){ -/** - * Module dependencies. - */ - -var Base = require('./base') - , utils = require('../utils'); - -/** - * Expose `Markdown`. - */ - -exports = module.exports = Markdown; - -/** - * Initialize a new `Markdown` reporter. - * - * @param {Runner} runner - * @api public - */ - -function Markdown(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , total = runner.total - , level = 0 - , buf = ''; - - function title(str) { - return Array(level).join('#') + ' ' + str; - } - - function indent() { - return Array(level).join(' '); - } - - function mapTOC(suite, obj) { - var ret = obj; - obj = obj[suite.title] = obj[suite.title] || { suite: suite }; - suite.suites.forEach(function(suite){ - mapTOC(suite, obj); - }); - return ret; - } - - function stringifyTOC(obj, level) { - ++level; - var buf = ''; - var link; - for (var key in obj) { - if ('suite' == key) continue; - if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n'; - if (key) buf += Array(level).join(' ') + link; - buf += stringifyTOC(obj[key], level); - } - --level; - return buf; - } - - function generateTOC(suite) { - var obj = mapTOC(suite, {}); - return stringifyTOC(obj, 0); - } - - generateTOC(runner.suite); - - runner.on('suite', function(suite){ - ++level; - var slug = utils.slug(suite.fullTitle()); - buf += '' + '\n'; - buf += title(suite.title) + '\n'; - }); - - runner.on('suite end', function(suite){ - --level; - }); - - runner.on('pass', function(test){ - var code = utils.clean(test.fn.toString()); - buf += test.title + '.\n'; - buf += '\n```js\n'; - buf += code + '\n'; - buf += '```\n\n'; - }); - - runner.on('end', function(){ - process.stdout.write('# TOC\n'); - process.stdout.write(generateTOC(runner.suite)); - process.stdout.write(buf); - }); -} -}); // module: reporters/markdown.js - -require.register("reporters/min.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Base = require('./base'); - -/** - * Expose `Min`. - */ - -exports = module.exports = Min; - -/** - * Initialize a new `Min` minimal test reporter (best used with --watch). - * - * @param {Runner} runner - * @api public - */ - -function Min(runner) { - Base.call(this, runner); - - runner.on('start', function(){ - // clear screen - process.stdout.write('\u001b[2J'); - // set cursor position - process.stdout.write('\u001b[1;3H'); - }); - - runner.on('end', this.epilogue.bind(this)); -} - -/** - * Inherit from `Base.prototype`. - */ - -Min.prototype = new Base; -Min.prototype.constructor = Min; - -}); // module: reporters/min.js - -require.register("reporters/nyan.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , color = Base.color; - -/** - * Expose `Dot`. - */ - -exports = module.exports = NyanCat; - -/** - * Initialize a new `Dot` matrix test reporter. - * - * @param {Runner} runner - * @api public - */ - -function NyanCat(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , width = Base.window.width * .75 | 0 - , rainbowColors = this.rainbowColors = self.generateColors() - , colorIndex = this.colorIndex = 0 - , numerOfLines = this.numberOfLines = 4 - , trajectories = this.trajectories = [[], [], [], []] - , nyanCatWidth = this.nyanCatWidth = 11 - , trajectoryWidthMax = this.trajectoryWidthMax = (width - nyanCatWidth) - , scoreboardWidth = this.scoreboardWidth = 5 - , tick = this.tick = 0 - , n = 0; - - runner.on('start', function(){ - Base.cursor.hide(); - self.draw('start'); - }); - - runner.on('pending', function(test){ - self.draw('pending'); - }); - - runner.on('pass', function(test){ - self.draw('pass'); - }); - - runner.on('fail', function(test, err){ - self.draw('fail'); - }); - - runner.on('end', function(){ - Base.cursor.show(); - for (var i = 0; i < self.numberOfLines; i++) write('\n'); - self.epilogue(); - }); -} - -/** - * Draw the nyan cat with runner `status`. - * - * @param {String} status - * @api private - */ - -NyanCat.prototype.draw = function(status){ - this.appendRainbow(); - this.drawScoreboard(); - this.drawRainbow(); - this.drawNyanCat(status); - this.tick = !this.tick; -}; - -/** - * Draw the "scoreboard" showing the number - * of passes, failures and pending tests. - * - * @api private - */ - -NyanCat.prototype.drawScoreboard = function(){ - var stats = this.stats; - var colors = Base.colors; - - function draw(color, n) { - write(' '); - write('\u001b[' + color + 'm' + n + '\u001b[0m'); - write('\n'); - } - - draw(colors.green, stats.passes); - draw(colors.fail, stats.failures); - draw(colors.pending, stats.pending); - write('\n'); - - this.cursorUp(this.numberOfLines); -}; - -/** - * Append the rainbow. - * - * @api private - */ - -NyanCat.prototype.appendRainbow = function(){ - var segment = this.tick ? '_' : '-'; - var rainbowified = this.rainbowify(segment); - - for (var index = 0; index < this.numberOfLines; index++) { - var trajectory = this.trajectories[index]; - if (trajectory.length >= this.trajectoryWidthMax) trajectory.shift(); - trajectory.push(rainbowified); - } -}; - -/** - * Draw the rainbow. - * - * @api private - */ - -NyanCat.prototype.drawRainbow = function(){ - var self = this; - - this.trajectories.forEach(function(line, index) { - write('\u001b[' + self.scoreboardWidth + 'C'); - write(line.join('')); - write('\n'); - }); - - this.cursorUp(this.numberOfLines); -}; - -/** - * Draw the nyan cat with `status`. - * - * @param {String} status - * @api private - */ - -NyanCat.prototype.drawNyanCat = function(status) { - var self = this; - var startWidth = this.scoreboardWidth + this.trajectories[0].length; - - [0, 1, 2, 3].forEach(function(index) { - write('\u001b[' + startWidth + 'C'); - - switch (index) { - case 0: - write('_,------,'); - write('\n'); - break; - case 1: - var padding = self.tick ? ' ' : ' '; - write('_|' + padding + '/\\_/\\ '); - write('\n'); - break; - case 2: - var padding = self.tick ? '_' : '__'; - var tail = self.tick ? '~' : '^'; - var face; - switch (status) { - case 'pass': - face = '( ^ .^)'; - break; - case 'fail': - face = '( o .o)'; - break; - default: - face = '( - .-)'; - } - write(tail + '|' + padding + face + ' '); - write('\n'); - break; - case 3: - var padding = self.tick ? ' ' : ' '; - write(padding + '"" "" '); - write('\n'); - break; - } - }); - - this.cursorUp(this.numberOfLines); -}; - -/** - * Move cursor up `n`. - * - * @param {Number} n - * @api private - */ - -NyanCat.prototype.cursorUp = function(n) { - write('\u001b[' + n + 'A'); -}; - -/** - * Move cursor down `n`. - * - * @param {Number} n - * @api private - */ - -NyanCat.prototype.cursorDown = function(n) { - write('\u001b[' + n + 'B'); -}; - -/** - * Generate rainbow colors. - * - * @return {Array} - * @api private - */ - -NyanCat.prototype.generateColors = function(){ - var colors = []; - - for (var i = 0; i < (6 * 7); i++) { - var pi3 = Math.floor(Math.PI / 3); - var n = (i * (1.0 / 6)); - var r = Math.floor(3 * Math.sin(n) + 3); - var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3); - var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3); - colors.push(36 * r + 6 * g + b + 16); - } - - return colors; -}; - -/** - * Apply rainbow to the given `str`. - * - * @param {String} str - * @return {String} - * @api private - */ - -NyanCat.prototype.rainbowify = function(str){ - var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length]; - this.colorIndex += 1; - return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m'; -}; - -/** - * Stdout helper. - */ - -function write(string) { - process.stdout.write(string); -} - -/** - * Inherit from `Base.prototype`. - */ - -NyanCat.prototype = new Base; -NyanCat.prototype.constructor = NyanCat; - - -}); // module: reporters/nyan.js - -require.register("reporters/progress.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , cursor = Base.cursor - , color = Base.color; - -/** - * Expose `Progress`. - */ - -exports = module.exports = Progress; - -/** - * General progress bar color. - */ - -Base.colors.progress = 90; - -/** - * Initialize a new `Progress` bar test reporter. - * - * @param {Runner} runner - * @param {Object} options - * @api public - */ - -function Progress(runner, options) { - Base.call(this, runner); - - var self = this - , options = options || {} - , stats = this.stats - , width = Base.window.width * .50 | 0 - , total = runner.total - , complete = 0 - , max = Math.max; - - // default chars - options.open = options.open || '['; - options.complete = options.complete || '▬'; - options.incomplete = options.incomplete || '⋅'; - options.close = options.close || ']'; - options.verbose = false; - - // tests started - runner.on('start', function(){ - console.log(); - cursor.hide(); - }); - - // tests complete - runner.on('test end', function(){ - complete++; - var incomplete = total - complete - , percent = complete / total - , n = width * percent | 0 - , i = width - n; - - cursor.CR(); - process.stdout.write('\u001b[J'); - process.stdout.write(color('progress', ' ' + options.open)); - process.stdout.write(Array(n).join(options.complete)); - process.stdout.write(Array(i).join(options.incomplete)); - process.stdout.write(color('progress', options.close)); - if (options.verbose) { - process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); - } - }); - - // tests are complete, output some stats - // and the failures if any - runner.on('end', function(){ - cursor.show(); - console.log(); - self.epilogue(); - }); -} - -/** - * Inherit from `Base.prototype`. - */ - -Progress.prototype = new Base; -Progress.prototype.constructor = Progress; - - -}); // module: reporters/progress.js - -require.register("reporters/spec.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , cursor = Base.cursor - , color = Base.color; - -/** - * Expose `Spec`. - */ - -exports = module.exports = Spec; - -/** - * Initialize a new `Spec` test reporter. - * - * @param {Runner} runner - * @api public - */ - -function Spec(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , indents = 0 - , n = 0; - - function indent() { - return Array(indents).join(' ') - } - - runner.on('start', function(){ - console.log(); - }); - - runner.on('suite', function(suite){ - ++indents; - console.log(color('suite', '%s%s'), indent(), suite.title); - }); - - runner.on('suite end', function(suite){ - --indents; - if (1 == indents) console.log(); - }); - - runner.on('test', function(test){ - process.stdout.write(indent() + color('pass', ' ◦ ' + test.title + ': ')); - }); - - runner.on('pending', function(test){ - var fmt = indent() + color('pending', ' - %s'); - console.log(fmt, test.title); - }); - - runner.on('pass', function(test){ - if ('fast' == test.speed) { - var fmt = indent() - + color('checkmark', ' ✓') - + color('pass', ' %s '); - cursor.CR(); - console.log(fmt, test.title); - } else { - var fmt = indent() - + color('checkmark', ' ✓') - + color('pass', ' %s ') - + color(test.speed, '(%dms)'); - cursor.CR(); - console.log(fmt, test.title, test.duration); - } - }); - - runner.on('fail', function(test, err){ - cursor.CR(); - console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); - }); - - runner.on('end', self.epilogue.bind(self)); -} - -/** - * Inherit from `Base.prototype`. - */ - -Spec.prototype = new Base; -Spec.prototype.constructor = Spec; - - -}); // module: reporters/spec.js - -require.register("reporters/tap.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , cursor = Base.cursor - , color = Base.color; - -/** - * Expose `TAP`. - */ - -exports = module.exports = TAP; - -/** - * Initialize a new `TAP` reporter. - * - * @param {Runner} runner - * @api public - */ - -function TAP(runner) { - Base.call(this, runner); - - var self = this - , stats = this.stats - , total = runner.total - , n = 1; - - runner.on('start', function(){ - console.log('%d..%d', 1, total); - }); - - runner.on('test end', function(){ - ++n; - }); - - runner.on('pending', function(test){ - console.log('ok %d %s # SKIP -', n, title(test)); - }); - - runner.on('pass', function(test){ - console.log('ok %d %s', n, title(test)); - }); - - runner.on('fail', function(test, err){ - console.log('not ok %d %s', n, title(test)); - console.log(err.stack.replace(/^/gm, ' ')); - }); -} - -/** - * Return a TAP-safe title of `test` - * - * @param {Object} test - * @return {String} - * @api private - */ - -function title(test) { - return test.fullTitle().replace(/#/g, ''); -} - -}); // module: reporters/tap.js - -require.register("reporters/teamcity.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Base = require('./base'); - -/** - * Expose `Teamcity`. - */ - -exports = module.exports = Teamcity; - -/** - * Initialize a new `Teamcity` reporter. - * - * @param {Runner} runner - * @api public - */ - -function Teamcity(runner) { - Base.call(this, runner); - var stats = this.stats; - - runner.on('start', function() { - console.log("##teamcity[testSuiteStarted name='mocha.suite']"); - }); - - runner.on('test', function(test) { - console.log("##teamcity[testStarted name='" + escape(test.fullTitle()) + "']"); - }); - - runner.on('fail', function(test, err) { - console.log("##teamcity[testFailed name='" + escape(test.fullTitle()) + "' message='" + escape(err.message) + "']"); - }); - - runner.on('pending', function(test) { - console.log("##teamcity[testIgnored name='" + escape(test.fullTitle()) + "' message='pending']"); - }); - - runner.on('test end', function(test) { - console.log("##teamcity[testFinished name='" + escape(test.fullTitle()) + "' duration='" + test.duration + "']"); - }); - - runner.on('end', function() { - console.log("##teamcity[testSuiteFinished name='mocha.suite' duration='" + stats.duration + "']"); - }); -} - -/** - * Escape the given `str`. - */ - -function escape(str) { - return str - .replace(/\|/g, "||") - .replace(/\n/g, "|n") - .replace(/\r/g, "|r") - .replace(/\[/g, "|[") - .replace(/\]/g, "|]") - .replace(/\u0085/g, "|x") - .replace(/\u2028/g, "|l") - .replace(/\u2029/g, "|p") - .replace(/'/g, "|'"); -} - -}); // module: reporters/teamcity.js - -require.register("reporters/xunit.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Base = require('./base') - , utils = require('../utils') - , escape = utils.escape; - -/** - * Save timer references to avoid Sinon interfering (see GH-237). - */ - -var Date = global.Date - , setTimeout = global.setTimeout - , setInterval = global.setInterval - , clearTimeout = global.clearTimeout - , clearInterval = global.clearInterval; - -/** - * Expose `XUnit`. - */ - -exports = module.exports = XUnit; - -/** - * Initialize a new `XUnit` reporter. - * - * @param {Runner} runner - * @api public - */ - -function XUnit(runner) { - Base.call(this, runner); - var stats = this.stats - , tests = [] - , self = this; - - runner.on('pass', function(test){ - tests.push(test); - }); - - runner.on('fail', function(test){ - tests.push(test); - }); - - runner.on('end', function(){ - console.log(tag('testsuite', { - name: 'Mocha Tests' - , tests: stats.tests - , failures: stats.failures - , errors: stats.failures - , skip: stats.tests - stats.failures - stats.passes - , timestamp: (new Date).toUTCString() - , time: stats.duration / 1000 - }, false)); - - tests.forEach(test); - console.log(''); - }); -} - -/** - * Inherit from `Base.prototype`. - */ - -XUnit.prototype = new Base; -XUnit.prototype.constructor = XUnit; - - -/** - * Output tag for the given `test.` - */ - -function test(test) { - var attrs = { - classname: test.parent.fullTitle() - , name: test.title - , time: test.duration / 1000 - }; - - if ('failed' == test.state) { - var err = test.err; - attrs.message = escape(err.message); - console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack)))); - } else if (test.pending) { - console.log(tag('testcase', attrs, false, tag('skipped', {}, true))); - } else { - console.log(tag('testcase', attrs, true) ); - } -} - -/** - * HTML tag helper. - */ - -function tag(name, attrs, close, content) { - var end = close ? '/>' : '>' - , pairs = [] - , tag; - - for (var key in attrs) { - pairs.push(key + '="' + escape(attrs[key]) + '"'); - } - - tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end; - if (content) tag += content + ''; -} - -}); // module: reporters/xunit.js - -require.register("runnable.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var EventEmitter = require('browser/events').EventEmitter - , debug = require('browser/debug')('mocha:runnable'); - -/** - * Save timer references to avoid Sinon interfering (see GH-237). - */ - -var Date = global.Date - , setTimeout = global.setTimeout - , setInterval = global.setInterval - , clearTimeout = global.clearTimeout - , clearInterval = global.clearInterval; - -/** - * Expose `Runnable`. - */ - -module.exports = Runnable; - -/** - * Initialize a new `Runnable` with the given `title` and callback `fn`. - * - * @param {String} title - * @param {Function} fn - * @api private - */ - -function Runnable(title, fn) { - this.title = title; - this.fn = fn; - this.async = fn && fn.length; - this.sync = ! this.async; - this._timeout = 2000; - this.timedOut = false; -} - -/** - * Inherit from `EventEmitter.prototype`. - */ - -Runnable.prototype = new EventEmitter; -Runnable.prototype.constructor = Runnable; - - -/** - * Set & get timeout `ms`. - * - * @param {Number} ms - * @return {Runnable|Number} ms or self - * @api private - */ - -Runnable.prototype.timeout = function(ms){ - if (0 == arguments.length) return this._timeout; - debug('timeout %d', ms); - this._timeout = ms; - if (this.timer) this.resetTimeout(); - return this; -}; - -/** - * Return the full title generated by recursively - * concatenating the parent's full title. - * - * @return {String} - * @api public - */ - -Runnable.prototype.fullTitle = function(){ - return this.parent.fullTitle() + ' ' + this.title; -}; - -/** - * Clear the timeout. - * - * @api private - */ - -Runnable.prototype.clearTimeout = function(){ - clearTimeout(this.timer); -}; - -/** - * Inspect the runnable void of private properties. - * - * @return {String} - * @api private - */ - -Runnable.prototype.inspect = function(){ - return JSON.stringify(this, function(key, val){ - if ('_' == key[0]) return; - if ('parent' == key) return '#'; - if ('ctx' == key) return '#'; - return val; - }, 2); -}; - -/** - * Reset the timeout. - * - * @api private - */ - -Runnable.prototype.resetTimeout = function(){ - var self = this - , ms = this.timeout(); - - this.clearTimeout(); - if (ms) { - this.timer = setTimeout(function(){ - self.callback(new Error('timeout of ' + ms + 'ms exceeded')); - self.timedOut = true; - }, ms); - } -}; - -/** - * Run the test and invoke `fn(err)`. - * - * @param {Function} fn - * @api private - */ - -Runnable.prototype.run = function(fn){ - var self = this - , ms = this.timeout() - , start = new Date - , ctx = this.ctx - , finished - , emitted; - - if (ctx) ctx.runnable(this); - - // timeout - if (this.async) { - if (ms) { - this.timer = setTimeout(function(){ - done(new Error('timeout of ' + ms + 'ms exceeded')); - self.timedOut = true; - }, ms); - } - } - - // called multiple times - function multiple(err) { - if (emitted) return; - emitted = true; - self.emit('error', err || new Error('done() called multiple times')); - } - - // finished - function done(err) { - if (self.timedOut) return; - if (finished) return multiple(err); - self.clearTimeout(); - self.duration = new Date - start; - finished = true; - fn(err); - } - - // for .resetTimeout() - this.callback = done; - - // async - if (this.async) { - try { - this.fn.call(ctx, function(err){ - if (err instanceof Error) return done(err); - if (null != err) return done(new Error('done() invoked with non-Error: ' + err)); - done(); - }); - } catch (err) { - done(err); - } - return; - } - - // sync - try { - if (!this.pending) this.fn.call(ctx); - this.duration = new Date - start; - fn(); - } catch (err) { - fn(err); - } -}; - -}); // module: runnable.js - -require.register("runner.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var EventEmitter = require('browser/events').EventEmitter - , debug = require('browser/debug')('mocha:runner') - , Test = require('./test') - , utils = require('./utils') - , filter = utils.filter - , keys = utils.keys - , noop = function(){}; - -/** - * Expose `Runner`. - */ - -module.exports = Runner; - -/** - * Initialize a `Runner` for the given `suite`. - * - * Events: - * - * - `start` execution started - * - `end` execution complete - * - `suite` (suite) test suite execution started - * - `suite end` (suite) all tests (and sub-suites) have finished - * - `test` (test) test execution started - * - `test end` (test) test completed - * - `hook` (hook) hook execution started - * - `hook end` (hook) hook complete - * - `pass` (test) test passed - * - `fail` (test, err) test failed - * - * @api public - */ - -function Runner(suite) { - var self = this; - this._globals = []; - this.suite = suite; - this.total = suite.total(); - this.failures = 0; - this.on('test end', function(test){ self.checkGlobals(test); }); - this.on('hook end', function(hook){ self.checkGlobals(hook); }); - this.grep(/.*/); - this.globals(utils.keys(global).concat(['errno'])); -} - -/** - * Inherit from `EventEmitter.prototype`. - */ - -Runner.prototype = new EventEmitter; -Runner.prototype.constructor = Runner; - - -/** - * Run tests with full titles matching `re`. Updates runner.total - * with number of tests matched. - * - * @param {RegExp} re - * @param {Boolean} invert - * @return {Runner} for chaining - * @api public - */ - -Runner.prototype.grep = function(re, invert){ - debug('grep %s', re); - this._grep = re; - this._invert = invert; - this.total = this.grepTotal(this.suite); - return this; -}; - -/** - * Returns the number of tests matching the grep search for the - * given suite. - * - * @param {Suite} suite - * @return {Number} - * @api public - */ - -Runner.prototype.grepTotal = function(suite) { - var self = this; - var total = 0; - - suite.eachTest(function(test){ - var match = self._grep.test(test.fullTitle()); - if (self._invert) match = !match; - if (match) total++; - }); - - return total; -}; - -/** - * Allow the given `arr` of globals. - * - * @param {Array} arr - * @return {Runner} for chaining - * @api public - */ - -Runner.prototype.globals = function(arr){ - if (0 == arguments.length) return this._globals; - debug('globals %j', arr); - utils.forEach(arr, function(arr){ - this._globals.push(arr); - }, this); - return this; -}; - -/** - * Check for global variable leaks. - * - * @api private - */ - -Runner.prototype.checkGlobals = function(test){ - if (this.ignoreLeaks) return; - var leaks = filterLeaks(this._globals); - - this._globals = this._globals.concat(leaks); - - if (leaks.length > 1) { - this.fail(test, new Error('global leaks detected: ' + leaks.join(', ') + '')); - } else if (leaks.length) { - this.fail(test, new Error('global leak detected: ' + leaks[0])); - } -}; - -/** - * Fail the given `test`. - * - * @param {Test} test - * @param {Error} err - * @api private - */ - -Runner.prototype.fail = function(test, err){ - ++this.failures; - test.state = 'failed'; - if ('string' == typeof err) { - err = new Error('the string "' + err + '" was thrown, throw an Error :)'); - } - this.emit('fail', test, err); -}; - -/** - * Fail the given `hook` with `err`. - * - * Hook failures (currently) hard-end due - * to that fact that a failing hook will - * surely cause subsequent tests to fail, - * causing jumbled reporting. - * - * @param {Hook} hook - * @param {Error} err - * @api private - */ - -Runner.prototype.failHook = function(hook, err){ - this.fail(hook, err); - this.emit('end'); -}; - -/** - * Run hook `name` callbacks and then invoke `fn()`. - * - * @param {String} name - * @param {Function} function - * @api private - */ - -Runner.prototype.hook = function(name, fn){ - var suite = this.suite - , hooks = suite['_' + name] - , ms = suite._timeout - , self = this - , timer; - - function next(i) { - var hook = hooks[i]; - if (!hook) return fn(); - self.currentRunnable = hook; - - self.emit('hook', hook); - - hook.on('error', function(err){ - self.failHook(hook, err); - }); - - hook.run(function(err){ - hook.removeAllListeners('error'); - var testError = hook.error(); - if (testError) self.fail(self.test, testError); - if (err) return self.failHook(hook, err); - self.emit('hook end', hook); - next(++i); - }); - } - - process.nextTick(function(){ - next(0); - }); -}; - -/** - * Run hook `name` for the given array of `suites` - * in order, and callback `fn(err)`. - * - * @param {String} name - * @param {Array} suites - * @param {Function} fn - * @api private - */ - -Runner.prototype.hooks = function(name, suites, fn){ - var self = this - , orig = this.suite; - - function next(suite) { - self.suite = suite; - - if (!suite) { - self.suite = orig; - return fn(); - } - - self.hook(name, function(err){ - if (err) { - self.suite = orig; - return fn(err); - } - - next(suites.pop()); - }); - } - - next(suites.pop()); -}; - -/** - * Run hooks from the top level down. - * - * @param {String} name - * @param {Function} fn - * @api private - */ - -Runner.prototype.hookUp = function(name, fn){ - var suites = [this.suite].concat(this.parents()).reverse(); - this.hooks(name, suites, fn); -}; - -/** - * Run hooks from the bottom up. - * - * @param {String} name - * @param {Function} fn - * @api private - */ - -Runner.prototype.hookDown = function(name, fn){ - var suites = [this.suite].concat(this.parents()); - this.hooks(name, suites, fn); -}; - -/** - * Return an array of parent Suites from - * closest to furthest. - * - * @return {Array} - * @api private - */ - -Runner.prototype.parents = function(){ - var suite = this.suite - , suites = []; - while (suite = suite.parent) suites.push(suite); - return suites; -}; - -/** - * Run the current test and callback `fn(err)`. - * - * @param {Function} fn - * @api private - */ - -Runner.prototype.runTest = function(fn){ - var test = this.test - , self = this; - - try { - test.on('error', function(err){ - self.fail(test, err); - }); - test.run(fn); - } catch (err) { - fn(err); - } -}; - -/** - * Run tests in the given `suite` and invoke - * the callback `fn()` when complete. - * - * @param {Suite} suite - * @param {Function} fn - * @api private - */ - -Runner.prototype.runTests = function(suite, fn){ - var self = this - , tests = suite.tests - , test; - - function next(err) { - // if we bail after first err - if (self.failures && suite._bail) return fn(); - - // next test - test = tests.shift(); - - // all done - if (!test) return fn(); - - // grep - var match = self._grep.test(test.fullTitle()); - if (self._invert) match = !match; - if (!match) return next(); - - // pending - if (test.pending) { - self.emit('pending', test); - self.emit('test end', test); - return next(); - } - - // execute test and hook(s) - self.emit('test', self.test = test); - self.hookDown('beforeEach', function(){ - self.currentRunnable = self.test; - self.runTest(function(err){ - test = self.test; - - if (err) { - self.fail(test, err); - self.emit('test end', test); - return self.hookUp('afterEach', next); - } - - test.state = 'passed'; - self.emit('pass', test); - self.emit('test end', test); - self.hookUp('afterEach', next); - }); - }); - } - - this.next = next; - next(); -}; - -/** - * Run the given `suite` and invoke the - * callback `fn()` when complete. - * - * @param {Suite} suite - * @param {Function} fn - * @api private - */ - -Runner.prototype.runSuite = function(suite, fn){ - var total = this.grepTotal(suite) - , self = this - , i = 0; - - debug('run suite %s', suite.fullTitle()); - - if (!total) return fn(); - - this.emit('suite', this.suite = suite); - - function next() { - var curr = suite.suites[i++]; - if (!curr) return done(); - self.runSuite(curr, next); - } - - function done() { - self.suite = suite; - self.hook('afterAll', function(){ - self.emit('suite end', suite); - fn(); - }); - } - - this.hook('beforeAll', function(){ - self.runTests(suite, next); - }); -}; - -/** - * Handle uncaught exceptions. - * - * @param {Error} err - * @api private - */ - -Runner.prototype.uncaught = function(err){ - debug('uncaught exception %s', err.message); - var runnable = this.currentRunnable; - if (!runnable || 'failed' == runnable.state) return; - runnable.clearTimeout(); - err.uncaught = true; - this.fail(runnable, err); - - // recover from test - if ('test' == runnable.type) { - this.emit('test end', runnable); - this.hookUp('afterEach', this.next); - return; - } - - // bail on hooks - this.emit('end'); -}; - -/** - * Run the root suite and invoke `fn(failures)` - * on completion. - * - * @param {Function} fn - * @return {Runner} for chaining - * @api public - */ - -Runner.prototype.run = function(fn){ - var self = this - , fn = fn || function(){}; - - debug('start'); - - // uncaught callback - function uncaught(err) { - self.uncaught(err); - } - - // callback - this.on('end', function(){ - debug('end'); - process.removeListener('uncaughtException', uncaught); - fn(self.failures); - }); - - // run suites - this.emit('start'); - this.runSuite(this.suite, function(){ - debug('finished running'); - self.emit('end'); - }); - - // uncaught exception - process.on('uncaughtException', uncaught); - - return this; -}; - -/** - * Filter leaks with the given globals flagged as `ok`. - * - * @param {Array} ok - * @return {Array} - * @api private - */ - -function filterLeaks(ok) { - return filter(keys(global), function(key){ - var matched = filter(ok, function(ok){ - if (~ok.indexOf('*')) return 0 == key.indexOf(ok.split('*')[0]); - return key == ok; - }); - return matched.length == 0 && (!global.navigator || 'onerror' !== key); - }); -} -}); // module: runner.js - -require.register("suite.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var EventEmitter = require('browser/events').EventEmitter - , debug = require('browser/debug')('mocha:suite') - , utils = require('./utils') - , Hook = require('./hook'); - -/** - * Expose `Suite`. - */ - -exports = module.exports = Suite; - -/** - * Create a new `Suite` with the given `title` - * and parent `Suite`. When a suite with the - * same title is already present, that suite - * is returned to provide nicer reporter - * and more flexible meta-testing. - * - * @param {Suite} parent - * @param {String} title - * @return {Suite} - * @api public - */ - -exports.create = function(parent, title){ - var suite = new Suite(title, parent.ctx); - suite.parent = parent; - if (parent.pending) suite.pending = true; - title = suite.fullTitle(); - parent.addSuite(suite); - return suite; -}; - -/** - * Initialize a new `Suite` with the given - * `title` and `ctx`. - * - * @param {String} title - * @param {Context} ctx - * @api private - */ - -function Suite(title, ctx) { - this.title = title; - this.ctx = ctx; - this.suites = []; - this.tests = []; - this.pending = false; - this._beforeEach = []; - this._beforeAll = []; - this._afterEach = []; - this._afterAll = []; - this.root = !title; - this._timeout = 2000; - this._bail = false; -} - -/** - * Inherit from `EventEmitter.prototype`. - */ - -Suite.prototype = new EventEmitter; -Suite.prototype.constructor = Suite; - - -/** - * Return a clone of this `Suite`. - * - * @return {Suite} - * @api private - */ - -Suite.prototype.clone = function(){ - var suite = new Suite(this.title); - debug('clone'); - suite.ctx = this.ctx; - suite.timeout(this.timeout()); - suite.bail(this.bail()); - return suite; -}; - -/** - * Set timeout `ms` or short-hand such as "2s". - * - * @param {Number|String} ms - * @return {Suite|Number} for chaining - * @api private - */ - -Suite.prototype.timeout = function(ms){ - if (0 == arguments.length) return this._timeout; - if (String(ms).match(/s$/)) ms = parseFloat(ms) * 1000; - debug('timeout %d', ms); - this._timeout = parseInt(ms, 10); - return this; -}; - -/** - * Sets whether to bail after first error. - * - * @parma {Boolean} bail - * @return {Suite|Number} for chaining - * @api private - */ - -Suite.prototype.bail = function(bail){ - if (0 == arguments.length) return this._bail; - debug('bail %s', bail); - this._bail = bail; - return this; -}; - -/** - * Run `fn(test[, done])` before running tests. - * - * @param {Function} fn - * @return {Suite} for chaining - * @api private - */ - -Suite.prototype.beforeAll = function(fn){ - if (this.pending) return this; - var hook = new Hook('"before all" hook', fn); - hook.parent = this; - hook.timeout(this.timeout()); - hook.ctx = this.ctx; - this._beforeAll.push(hook); - this.emit('beforeAll', hook); - return this; -}; - -/** - * Run `fn(test[, done])` after running tests. - * - * @param {Function} fn - * @return {Suite} for chaining - * @api private - */ - -Suite.prototype.afterAll = function(fn){ - if (this.pending) return this; - var hook = new Hook('"after all" hook', fn); - hook.parent = this; - hook.timeout(this.timeout()); - hook.ctx = this.ctx; - this._afterAll.push(hook); - this.emit('afterAll', hook); - return this; -}; - -/** - * Run `fn(test[, done])` before each test case. - * - * @param {Function} fn - * @return {Suite} for chaining - * @api private - */ - -Suite.prototype.beforeEach = function(fn){ - if (this.pending) return this; - var hook = new Hook('"before each" hook', fn); - hook.parent = this; - hook.timeout(this.timeout()); - hook.ctx = this.ctx; - this._beforeEach.push(hook); - this.emit('beforeEach', hook); - return this; -}; - -/** - * Run `fn(test[, done])` after each test case. - * - * @param {Function} fn - * @return {Suite} for chaining - * @api private - */ - -Suite.prototype.afterEach = function(fn){ - if (this.pending) return this; - var hook = new Hook('"after each" hook', fn); - hook.parent = this; - hook.timeout(this.timeout()); - hook.ctx = this.ctx; - this._afterEach.push(hook); - this.emit('afterEach', hook); - return this; -}; - -/** - * Add a test `suite`. - * - * @param {Suite} suite - * @return {Suite} for chaining - * @api private - */ - -Suite.prototype.addSuite = function(suite){ - suite.parent = this; - suite.timeout(this.timeout()); - suite.bail(this.bail()); - this.suites.push(suite); - this.emit('suite', suite); - return this; -}; - -/** - * Add a `test` to this suite. - * - * @param {Test} test - * @return {Suite} for chaining - * @api private - */ - -Suite.prototype.addTest = function(test){ - test.parent = this; - test.timeout(this.timeout()); - test.ctx = this.ctx; - this.tests.push(test); - this.emit('test', test); - return this; -}; - -/** - * Return the full title generated by recursively - * concatenating the parent's full title. - * - * @return {String} - * @api public - */ - -Suite.prototype.fullTitle = function(){ - if (this.parent) { - var full = this.parent.fullTitle(); - if (full) return full + ' ' + this.title; - } - return this.title; -}; - -/** - * Return the total number of tests. - * - * @return {Number} - * @api public - */ - -Suite.prototype.total = function(){ - return utils.reduce(this.suites, function(sum, suite){ - return sum + suite.total(); - }, 0) + this.tests.length; -}; - -/** - * Iterates through each suite recursively to find - * all tests. Applies a function in the format - * `fn(test)`. - * - * @param {Function} fn - * @return {Suite} - * @api private - */ - -Suite.prototype.eachTest = function(fn){ - utils.forEach(this.tests, fn); - utils.forEach(this.suites, function(suite){ - suite.eachTest(fn); - }); - return this; -}; - -}); // module: suite.js - -require.register("test.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var Runnable = require('./runnable'); - -/** - * Expose `Test`. - */ - -module.exports = Test; - -/** - * Initialize a new `Test` with the given `title` and callback `fn`. - * - * @param {String} title - * @param {Function} fn - * @api private - */ - -function Test(title, fn) { - Runnable.call(this, title, fn); - this.pending = !fn; - this.type = 'test'; -} - -/** - * Inherit from `Runnable.prototype`. - */ - -Test.prototype = new Runnable; -Test.prototype.constructor = Test; - - -}); // module: test.js - -require.register("utils.js", function(module, exports, require){ - -/** - * Module dependencies. - */ - -var fs = require('browser/fs') - , path = require('browser/path') - , join = path.join - , debug = require('browser/debug')('mocha:watch'); - -/** - * Ignored directories. - */ - -var ignore = ['node_modules', '.git']; - -/** - * Escape special characters in the given string of html. - * - * @param {String} html - * @return {String} - * @api private - */ - -exports.escape = function(html) { - return String(html) - .replace(/&/g, '&') - .replace(/"/g, '"') - .replace(//g, '>'); -}; - -/** - * Array#forEach (<=IE8) - * - * @param {Array} array - * @param {Function} fn - * @param {Object} scope - * @api private - */ - -exports.forEach = function(arr, fn, scope) { - for (var i = 0, l = arr.length; i < l; i++) - fn.call(scope, arr[i], i); -}; - -/** - * Array#indexOf (<=IE8) - * - * @parma {Array} arr - * @param {Object} obj to find index of - * @param {Number} start - * @api private - */ - -exports.indexOf = function (arr, obj, start) { - for (var i = start || 0, l = arr.length; i < l; i++) { - if (arr[i] === obj) - return i; - } - return -1; -}; - -/** - * Array#reduce (<=IE8) - * - * @param {Array} array - * @param {Function} fn - * @param {Object} initial value - * @param {Object} scope - * @api private - */ - -exports.reduce = function(arr, fn, val, scope) { - var rval = val; - - for (var i = 0, l = arr.length; i < l; i++) { - rval = fn.call(scope, rval, arr[i], i, arr); - } - - return rval; -}; - -/** - * Array#filter (<=IE8) - * - * @param {Array} array - * @param {Function} fn - * @param {Object} scope - * @api private - */ - -exports.filter = function(arr, fn, scope) { - var ret = []; - - for (var i = 0, l = arr.length; i < l; i++) { - var val = arr[i]; - if (fn.call(scope, val, i, arr)) - ret.push(val); - } - - return ret; -}; - -/** - * Object.keys (<=IE8) - * - * @param {Object} obj - * @return {Array} keys - * @api private - */ - -exports.keys = Object.keys || function(obj) { - var keys = [] - , has = Object.prototype.hasOwnProperty // for `window` on <=IE8 - - for (var key in obj) { - if (has.call(obj, key)) { - keys.push(key); - } - } - - return keys; -}; - -/** - * Watch the given `files` for changes - * and invoke `fn(file)` on modification. - * - * @param {Array} files - * @param {Function} fn - * @api private - */ - -exports.watch = function(files, fn){ - var options = { interval: 100 }; - files.forEach(function(file){ - debug('file %s', file); - fs.watchFile(file, options, function(curr, prev){ - if (prev.mtime < curr.mtime) fn(file); - }); - }); -}; - -/** - * Ignored files. - */ - -function ignored(path){ - return !~ignore.indexOf(path); -} - -/** - * Lookup files in the given `dir`. - * - * @return {Array} - * @api private - */ - -exports.files = function(dir, ret){ - ret = ret || []; - - fs.readdirSync(dir) - .filter(ignored) - .forEach(function(path){ - path = join(dir, path); - if (fs.statSync(path).isDirectory()) { - exports.files(path, ret); - } else if (path.match(/\.(js|coffee)$/)) { - ret.push(path); - } - }); - - return ret; -}; - -/** - * Compute a slug from the given `str`. - * - * @param {String} str - * @return {String} - * @api private - */ - -exports.slug = function(str){ - return str - .toLowerCase() - .replace(/ +/g, '-') - .replace(/[^-\w]/g, ''); -}; - -/** - * Strip the function definition from `str`, - * and re-indent for pre whitespace. - */ - -exports.clean = function(str) { - str = str - .replace(/^function *\(.*\) *{/, '') - .replace(/\s+\}$/, ''); - - var spaces = str.match(/^\n?( *)/)[1].length - , re = new RegExp('^ {' + spaces + '}', 'gm'); - - str = str.replace(re, ''); - - return str.trim(); -}; - -/** - * Escape regular expression characters in `str`. - * - * @param {String} str - * @return {String} - * @api private - */ - -exports.escapeRegexp = function(str){ - return str.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&"); -}; -}); // module: utils.js -/** - * Node shims. - * - * These are meant only to allow - * mocha.js to run untouched, not - * to allow running node code in - * the browser. - */ - -process = {}; -process.exit = function(status){}; -process.stdout = {}; -global = window; - -/** - * next tick implementation. - */ - -process.nextTick = (function(){ - // postMessage behaves badly on IE8 - if (window.ActiveXObject || !window.postMessage) { - return function(fn){ fn() }; - } - - // based on setZeroTimeout by David Baron - // - http://dbaron.org/log/20100309-faster-timeouts - var timeouts = [] - , name = 'mocha-zero-timeout' - - window.addEventListener('message', function(e){ - if (e.source == window && e.data == name) { - if (e.stopPropagation) e.stopPropagation(); - if (timeouts.length) timeouts.shift()(); - } - }, true); - - return function(fn){ - timeouts.push(fn); - window.postMessage(name, '*'); - } -})(); - -/** - * Remove uncaughtException listener. - */ - -process.removeListener = function(e){ - if ('uncaughtException' == e) { - window.onerror = null; - } -}; - -/** - * Implements uncaughtException listener. - */ - -process.on = function(e, fn){ - if ('uncaughtException' == e) { - window.onerror = fn; - } -}; - -/** - * Expose mocha. - */ - -window.mocha = require('mocha'); - -// boot -;(function(){ - var utils = mocha.utils - , options = {} - - mocha.suite = new mocha.Suite('', new mocha.Context()); - - /** - * Highlight the given string of `js`. - */ - - function highlight(js) { - return js - .replace(//g, '>') - .replace(/\/\/(.*)/gm, '//$1') - .replace(/('.*?')/gm, '$1') - .replace(/(\d+\.\d+)/gm, '$1') - .replace(/(\d+)/gm, '$1') - .replace(/\bnew *(\w+)/gm, 'new $1') - .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1') - } - - /** - * Highlight code contents. - */ - - function highlightCode() { - var code = document.getElementsByTagName('code'); - for (var i = 0, len = code.length; i < len; ++i) { - code[i].innerHTML = highlight(code[i].innerHTML); - } - } - - /** - * Parse the given `qs`. - */ - - function parse(qs) { - return utils.reduce(qs.replace('?', '').split('&'), function(obj, pair){ - var i = pair.indexOf('=') - , key = pair.slice(0, i) - , val = pair.slice(++i); - - obj[key] = decodeURIComponent(val); - return obj; - }, {}); - } - - /** - * Setup mocha with the given setting options. - */ - - mocha.setup = function(opts){ - if ('string' === typeof opts) options.ui = opts; - else options = opts; - - ui = mocha.interfaces[options.ui]; - if (!ui) throw new Error('invalid mocha interface "' + ui + '"'); - if (options.timeout) mocha.suite.timeout(options.timeout); - ui(mocha.suite); - mocha.suite.emit('pre-require', window); - }; - - /** - * Run mocha, returning the Runner. - */ - - mocha.run = function(fn){ - mocha.suite.emit('run'); - var runner = new mocha.Runner(mocha.suite); - var Reporter = options.reporter || mocha.reporters.HTML; - var reporter = new Reporter(runner); - var query = parse(window.location.search || ""); - if (query.grep) runner.grep(new RegExp(query.grep)); - if (options.ignoreLeaks) runner.ignoreLeaks = true; - if (options.globals) runner.globals(options.globals); - runner.globals(['location']); - runner.on('end', highlightCode); - return runner.run(fn); - }; -})(); -})(); \ No newline at end of file diff --git a/test/runner.html b/test/runner.html deleted file mode 100755 index 6130020a1ee..00000000000 --- a/test/runner.html +++ /dev/null @@ -1,33 +0,0 @@ - - - Prebid.js UnitTests - - - - - - - - - - - - - - - - - - -
    - - diff --git a/test/spec/adUnits_spec.js b/test/spec/adUnits_spec.js index 9fbc29b6860..5bf39584493 100644 --- a/test/spec/adUnits_spec.js +++ b/test/spec/adUnits_spec.js @@ -1,131 +1,125 @@ -describe("Publisher API _ AdUnits", function() { - var assert = require('chai').assert, - expect = require('chai').expect, - should = require('chai').should(); - - var prebid = require('../../src/prebid'); - - - before(function(){ - var adUnits = [{ - code: "/1996833/slot-1", - sizes: [[300, 250], [728, 90]], - bids: [ +describe('Publisher API _ AdUnits', function () { + var assert = require('chai').assert; + var expect = require('chai').expect; + var pbjsTestOnly = require('../helpers/pbjs-test-only').pbjsTestOnly; + + before(function () { + var adUnits = [{ + code: '/1996833/slot-1', + sizes: [[300, 250], [728, 90]], + bids: [ { - bidder: "openx", - params: { - pgid: "2342353", - unit: "234234", - jstag_url: "http://" - } - },{ - bidder: "appnexus", - params: { - placementId: "234235" - } + bidder: 'openx', + params: { + pgid: '2342353', + unit: '234234', + jstag_url: 'http://' + } + }, { + bidder: 'appnexus', + params: { + placementId: '234235' + } } ] - },{ - code: "/1996833/slot-2", - sizes: [[468, 60]], - bids: [ + }, { + code: '/1996833/slot-2', + sizes: [[468, 60]], + bids: [ { - bidder: "rubicon", - params: { - rp_account: "4934", - rp_site: "13945", - rp_zonesize: "23948-15" - } - },{ - bidder: "appnexus", - params: { - placementId: "827326" - } + bidder: 'rubicon', + params: { + rp_account: '4934', + rp_site: '13945', + rp_zonesize: '23948-15' + } + }, { + bidder: 'appnexus', + params: { + placementId: '827326' + } } ] - }]; + }]; + + pbjs.addAdUnits(adUnits); + }); + + after(function () { + pbjsTestOnly.clearAllAdUnits(); + }); + + describe('addAdUnits', function () { + + var adUnits, adUnit1, bids1, adUnit2, bids2; - pbjs.addAdUnits(adUnits); + it('should have two adUnits', function () { + adUnits = pbjsTestOnly.getAdUnits(); + adUnit1 = adUnits[0]; + bids1 = adUnit1.bids; + adUnit2 = adUnits[1]; + bids2 = adUnit2.bids; }); - after(function(){ - pbjs_testonly.clearAllAdUnits(); + it('the first adUnits value should be same with the adUnits that is added by pbjs.addAdUnits();', function () { + assert.strictEqual(adUnit1.code, '/1996833/slot-1', 'adUnit1 code'); + assert.deepEqual(adUnit1.sizes, [[300, 250], [728, 90]], 'adUnit1 sizes'); + assert.strictEqual(bids1[0].bidder, 'openx', 'adUnit1 bids1 bidder'); + assert.strictEqual(bids1[0].params.pgid, '2342353', 'adUnit1 bids1 params.pgid'); + assert.strictEqual(bids1[0].params.unit, '234234', 'adUnit1 bids1 params.unit'); + assert.strictEqual(bids1[0].params.jstag_url, 'http://', 'adUnit1 bids1 params.jstag_url'); + + assert.strictEqual(bids1[1].bidder, 'appnexus', 'adUnit1 bids2 bidder'); + assert.strictEqual(bids1[1].params.placementId, '234235', 'adUnit1 bids2 params.placementId'); + + assert.strictEqual(adUnit2.code, '/1996833/slot-2', 'adUnit2 code'); + assert.deepEqual(adUnit2.sizes, [[468, 60]], 'adUnit2 sizes'); + assert.strictEqual(bids2[0].bidder, 'rubicon', 'adUnit2 bids1 bidder'); + assert.strictEqual(bids2[0].params.rp_account, '4934', 'adUnit2 bids1 params.rp_account'); + assert.strictEqual(bids2[0].params.rp_zonesize, '23948-15', 'adUnit2 bids1 params.rp_zonesize'); + assert.strictEqual(bids2[0].params.rp_site, '13945', 'adUnit2 bids1 params.rp_site'); + + assert.strictEqual(bids2[1].bidder, 'appnexus', 'adUnit2 bids2 bidder'); + assert.strictEqual(bids2[1].params.placementId, '827326', 'adUnit2 bids2 params.placementId'); }); - - - describe('addAdUnits', function() { - - var adUnits,adUnit1,bids1,adUnit2,bids2; - - it('should have two adUnits',function(){ - adUnits = pbjs_testonly.getAdUnits(); - adUnit1 = adUnits[0]; - bids1 = adUnit1.bids; - adUnit2 = adUnits[1]; - bids2 = adUnit2.bids; - }); - - it('the first adUnits value should be same with the adUnits that is added by pbjs.addAdUnits();',function(){ - assert.strictEqual(adUnit1.code,'/1996833/slot-1','adUnit1 code'); - assert.deepEqual(adUnit1.sizes,[[300, 250], [728, 90]],'adUnit1 sizes'); - assert.strictEqual(bids1[0].bidder,'openx','adUnit1 bids1 bidder'); - assert.strictEqual(bids1[0].params.pgid,'2342353','adUnit1 bids1 params.pgid'); - assert.strictEqual(bids1[0].params.unit,'234234','adUnit1 bids1 params.unit'); - assert.strictEqual(bids1[0].params.jstag_url,'http://','adUnit1 bids1 params.jstag_url'); - - assert.strictEqual(bids1[1].bidder,'appnexus','adUnit1 bids2 bidder'); - assert.strictEqual(bids1[1].params.placementId,'234235','adUnit1 bids2 params.placementId'); - - assert.strictEqual(adUnit2.code,'/1996833/slot-2','adUnit2 code'); - assert.deepEqual(adUnit2.sizes,[[468, 60]],'adUnit2 sizes'); - assert.strictEqual(bids2[0].bidder,'rubicon','adUnit2 bids1 bidder'); - assert.strictEqual(bids2[0].params.rp_account,'4934','adUnit2 bids1 params.rp_account'); - assert.strictEqual(bids2[0].params.rp_zonesize,'23948-15','adUnit2 bids1 params.rp_zonesize'); - assert.strictEqual(bids2[0].params.rp_site,'13945','adUnit2 bids1 params.rp_site'); - - assert.strictEqual(bids2[1].bidder,'appnexus','adUnit2 bids2 bidder'); - assert.strictEqual(bids2[1].params.placementId,'827326','adUnit2 bids2 params.placementId'); - }); - - it('the second adUnits value should be same with the adUnits that is added by pbjs.addAdUnits();',function(){ - - assert.strictEqual(adUnit2.code,'/1996833/slot-2','adUnit2 code'); - assert.deepEqual(adUnit2.sizes,[[468, 60]],'adUnit2 sizes'); - assert.strictEqual(bids2[0].bidder,'rubicon','adUnit2 bids1 bidder'); - assert.strictEqual(bids2[0].params.rp_account,'4934','adUnit2 bids1 params.rp_account'); - assert.strictEqual(bids2[0].params.rp_zonesize,'23948-15','adUnit2 bids1 params.rp_zonesize'); - assert.strictEqual(bids2[0].params.rp_site,'13945','adUnit2 bids1 params.rp_site'); - - assert.strictEqual(bids2[1].bidder,'appnexus','adUnit2 bids2 bidder'); - assert.strictEqual(bids2[1].params.placementId,'827326','adUnit2 bids2 params.placementId'); - }); + + it('the second adUnits value should be same with the adUnits that is added by pbjs.addAdUnits();', function () { + + assert.strictEqual(adUnit2.code, '/1996833/slot-2', 'adUnit2 code'); + assert.deepEqual(adUnit2.sizes, [[468, 60]], 'adUnit2 sizes'); + assert.strictEqual(bids2[0].bidder, 'rubicon', 'adUnit2 bids1 bidder'); + assert.strictEqual(bids2[0].params.rp_account, '4934', 'adUnit2 bids1 params.rp_account'); + assert.strictEqual(bids2[0].params.rp_zonesize, '23948-15', 'adUnit2 bids1 params.rp_zonesize'); + assert.strictEqual(bids2[0].params.rp_site, '13945', 'adUnit2 bids1 params.rp_site'); + + assert.strictEqual(bids2[1].bidder, 'appnexus', 'adUnit2 bids2 bidder'); + assert.strictEqual(bids2[1].params.placementId, '827326', 'adUnit2 bids2 params.placementId'); + }); + }); + + describe('removeAdUnit', function () { + + var adUnits, adUnit2, bids2; + + it('the first adUnit should be not existed', function () { + pbjs.removeAdUnit('/1996833/slot-1'); + adUnits = pbjsTestOnly.getAdUnits(); + adUnit2 = adUnits[0]; + bids2 = adUnit2.bids; + expect(adUnits[1]).not.exist; }); - describe('removeAdUnit',function(){ - - var adUnits,adUnit2,bids2; - - - it('the first adUnit should be not existed',function(){ - pbjs.removeAdUnit('/1996833/slot-1'); - adUnits = pbjs_testonly.getAdUnits(); - adUnit2 = adUnits[0]; - bids2 = adUnit2.bids; - expect(adUnits[1]).not.exist; - }); - - it('the second adUnit should be still existed',function(){ - assert.strictEqual(adUnit2.code,'/1996833/slot-2','adUnit2 code'); - assert.deepEqual(adUnit2.sizes,[[468, 60]],'adUnit2 sizes'); - assert.strictEqual(bids2[0].bidder,'rubicon','adUnit2 bids1 bidder'); - assert.strictEqual(bids2[0].params.rp_account,'4934','adUnit2 bids1 params.rp_account'); - assert.strictEqual(bids2[0].params.rp_zonesize,'23948-15','adUnit2 bids1 params.rp_zonesize'); - assert.strictEqual(bids2[0].params.rp_site,'13945','adUnit2 bids1 params.rp_site'); - - assert.strictEqual(bids2[1].bidder,'appnexus','adUnit2 bids2 bidder'); - assert.strictEqual(bids2[1].params.placementId,'827326','adUnit2 bids2 params.placementId'); - }); + it('the second adUnit should be still existed', function () { + assert.strictEqual(adUnit2.code, '/1996833/slot-2', 'adUnit2 code'); + assert.deepEqual(adUnit2.sizes, [[468, 60]], 'adUnit2 sizes'); + assert.strictEqual(bids2[0].bidder, 'rubicon', 'adUnit2 bids1 bidder'); + assert.strictEqual(bids2[0].params.rp_account, '4934', 'adUnit2 bids1 params.rp_account'); + assert.strictEqual(bids2[0].params.rp_zonesize, '23948-15', 'adUnit2 bids1 params.rp_zonesize'); + assert.strictEqual(bids2[0].params.rp_site, '13945', 'adUnit2 bids1 params.rp_site'); + + assert.strictEqual(bids2[1].bidder, 'appnexus', 'adUnit2 bids2 bidder'); + assert.strictEqual(bids2[1].params.placementId, '827326', 'adUnit2 bids2 params.placementId'); }); + }); - }); diff --git a/test/spec/adloader_spec.js b/test/spec/adloader_spec.js new file mode 100644 index 00000000000..49513251f60 --- /dev/null +++ b/test/spec/adloader_spec.js @@ -0,0 +1,40 @@ +describe('adLoader', function () { + var assert = require('chai').assert, + adLoader = require('../../src/adloader'); + + describe('trackPixel', function () { + it('correctly appends a cachebuster query paramter to a pixel with no existing parameters', function () { + var inputUrl = 'http://www.example.com/tracking_pixel', + token = '?rnd=', + expectedPartialUrl = inputUrl + token, + actual = adLoader.trackPixel(inputUrl), + actualPartialUrl = actual.split(token)[0] + token, + randomNumber = parseInt(actual.split(token)[1]); + assert.strictEqual(actualPartialUrl, expectedPartialUrl); + assert.isNumber(randomNumber); + }); + }); + + it('correctly appends a cachebuster query paramter to a pixel with one existing parameter', function () { + var inputUrl = 'http://www.example.com/tracking_pixel?food=bard', + token = '&rnd=', + expectedPartialUrl = inputUrl + token, + actual = adLoader.trackPixel(inputUrl), + actualPartialUrl = actual.split(token)[0] + token, + randomNumber = parseInt(actual.split(token)[1]); + assert.strictEqual(actualPartialUrl, expectedPartialUrl); + assert.isNumber(randomNumber); + }); + + it('correctly appends a cachebuster query paramter to a pixel with multiple existing parameters', function () { + var inputUrl = 'http://www.example.com/tracking_pixel?food=bard&zing=zang', + token = '&rnd=', + expectedPartialUrl = inputUrl + token, + actual = adLoader.trackPixel(inputUrl), + actualPartialUrl = actual.split(token)[0] + token, + randomNumber = parseInt(actual.split(token)[1]); + assert.strictEqual(actualPartialUrl, expectedPartialUrl); + assert.isNumber(randomNumber); + }); + +}); diff --git a/test/spec/aliasBidder_spec.js b/test/spec/aliasBidder_spec.js index c2410399bfc..9a537dcf86d 100644 --- a/test/spec/aliasBidder_spec.js +++ b/test/spec/aliasBidder_spec.js @@ -1,39 +1,42 @@ -describe("Publisher API _ Alias Bidder", function() { - var assert = require('chai').assert, - expect = require('chai').expect, - should = require('chai').should(); - var prebid = require('../../src/prebid'); - - before(function(){ - - var topSlotCode = '/19968336/header-bid-tag1'; - var topSlotSizes = [[728, 90], [970, 90]]; - var adUnit = { - code: topSlotCode, - sizes: topSlotSizes, - bids: [{ - bidder: 'appnexus', - params: { - placementId : '5215561' - } - }] - }; - - pbjs.addAdUnits(adUnit); - }); +import { pbjsTestOnly } from 'test/helpers/pbjs-test-only'; - after(function(){ - pbjs_testonly.clearAllAdUnits(); - }); - - describe('set Alias Bidder', function () { +describe('Publisher API _ Alias Bidder', function () { + var assert = require('chai').assert; + var expect = require('chai').expect; + var should = require('chai').should(); + var prebid = require('../../src/prebid'); + + before(function () { + + var topSlotCode = '/19968336/header-bid-tag1'; + var topSlotSizes = [[728, 90], [970, 90]]; + var adUnit = { + code: topSlotCode, + sizes: topSlotSizes, + bids: [ + { + bidder: 'appnexus', + params: { + placementId: '5215561' + } + } + ] + }; + + pbjs.addAdUnits(adUnit); + }); + + after(function () { + pbjsTestOnly.clearAllAdUnits(); + }); + + describe('set Alias Bidder', function () { + + it('should have both of target bidder and alias bidder', function () { - it('should have both of target bidder and alias bidder', function() { - - pbjs.aliasBidder('appnexus','bRealTime1'); + pbjs.aliasBidder('appnexus', 'bRealTime1'); - }); }); + }); - }); diff --git a/test/spec/api_spec.js b/test/spec/api_spec.js index fd6f296e746..c0be859958e 100755 --- a/test/spec/api_spec.js +++ b/test/spec/api_spec.js @@ -1,65 +1,76 @@ var assert = require('chai').assert; var prebid = require('../../src/prebid'); - -describe("Publisher API", function() { +describe('Publisher API', function () { // var assert = chai.assert; - describe('api of command queue',function(){ + describe('api of command queue', function () { - it('should have a global variable pbjs', function() { + it('should have a global variable pbjs', function () { assert.isObject(pbjs); }); - it('should have a global variable pbjs.que as an array',function(){ + it('should have a global variable pbjs.que as an array', function () { assert.isArray(pbjs.que); }); - it('should have pbjs.que.push function', function(){ + it('should have pbjs.que.push function', function () { assert.isFunction(pbjs.que.push); }); }); - describe('has function',function(){ + describe('has function', function () { - it('should have function pbjs.getAdserverTargeting',function(){ + it('should have function pbjs.getAdserverTargeting', function () { assert.isFunction(pbjs.getAdserverTargeting); }); - it('should have function pbjs.getAdserverTargetingForAdUnitCode',function(){ - assert.isFunction(pbjs.getAdserverTargetingForAdUnitCode); + + it('should have function pbjs.getAdserverTargetingForAdUnitCode', function () { + assert.isFunction(pbjs.getAdserverTargetingForAdUnitCode); }); - it('should have function pbjs.getBidResponses',function(){ - assert.isFunction(pbjs.getBidResponses); + + it('should have function pbjs.getBidResponses', function () { + assert.isFunction(pbjs.getBidResponses); }); - it('should have function pbjs.getBidResponsesForAdUnitCode',function(){ - assert.isFunction(pbjs.getBidResponsesForAdUnitCode); + + it('should have function pbjs.getBidResponsesForAdUnitCode', function () { + assert.isFunction(pbjs.getBidResponsesForAdUnitCode); }); - it('should have function pbjs.setTargetingForGPTAsync',function(){ - assert.isFunction(pbjs.setTargetingForGPTAsync); + + it('should have function pbjs.setTargetingForGPTAsync', function () { + assert.isFunction(pbjs.setTargetingForGPTAsync); }); - it('should have function pbjs.allBidsAvailable',function(){ - assert.isFunction(pbjs.allBidsAvailable); + + it('should have function pbjs.allBidsAvailable', function () { + assert.isFunction(pbjs.allBidsAvailable); }); - it('should have function pbjs.renderAd',function(){ - assert.isFunction(pbjs.renderAd); + + it('should have function pbjs.renderAd', function () { + assert.isFunction(pbjs.renderAd); }); - it('should have function pbjs.removeAdUnit',function(){ - assert.isFunction(pbjs.removeAdUnit); + + it('should have function pbjs.removeAdUnit', function () { + assert.isFunction(pbjs.removeAdUnit); }); - it('should have function pbjs.requestBids',function(){ - assert.isFunction(pbjs.requestBids); + + it('should have function pbjs.requestBids', function () { + assert.isFunction(pbjs.requestBids); }); - it('should have function pbjs.addAdUnits',function(){ - assert.isFunction(pbjs.addAdUnits); + + it('should have function pbjs.addAdUnits', function () { + assert.isFunction(pbjs.addAdUnits); }); - it('should have function pbjs.addCallback',function(){ - assert.isFunction(pbjs.addCallback); + + it('should have function pbjs.addCallback', function () { + assert.isFunction(pbjs.addCallback); }); - it('should have function pbjs.removeCallback',function(){ - assert.isFunction(pbjs.removeCallback); + + it('should have function pbjs.removeCallback', function () { + assert.isFunction(pbjs.removeCallback); }); - it('should have function pbjs.aliasBidder',function(){ - assert.isFunction(pbjs.aliasBidder); + + it('should have function pbjs.aliasBidder', function () { + assert.isFunction(pbjs.aliasBidder); }); }); diff --git a/test/test.js b/test/spec/bidmanager_spec.js similarity index 85% rename from test/test.js rename to test/spec/bidmanager_spec.js index 800ec0f6817..4a503a5f3d6 100644 --- a/test/test.js +++ b/test/spec/bidmanager_spec.js @@ -3,50 +3,8 @@ var assert = require("assert"); /* use this method to test individual files instead of the whole prebid.js project */ //TODO refactor to use the spec files -var utils = require('../src/utils'); -var bidmanager = require('../src/bidmanager'); -var appnexus = require('../src/adapters/appnexus'); - - describe('appnexus adapter unit tests', function(){ - - var adapterInstance = null; - var bidRequest = { - bidder: "appnexus", - params: { - memberId : "123", - placementId: "123345", - invCode : 'inv_code', - referrer: "url.com", - alt_referrer: "url.com", - extraParam: "foobar", - somethingElse : 'hello', - query : { - foo : 'bar', - tasty : 'treat' - } - }, - placementCode: "/19968336/header-bid-tag-0", - sizes: [ - [300, 250], - [300, 600] - ] - }; - var callbackId = 'cbId'; - - - it('Get instance', function() { - adapterInstance = appnexus.createNew(); - assert.ok(adapterInstance); - }); - - it('buildJPTCall()', function() { - var expectedUrl = 'http://ib.adnxs.com/jpt?callback=pbjs.handleAnCB&callback_uid=cbId&psa=0&id=123345&member_id=123&code=inv_code&size=300x250&promo_sizes=300x600&foo=bar&tasty=treat&extraParam=foobar&somethingElse=hello&referrer=url.com&alt_referrer=url.com'; - var url = adapterInstance.buildJPTCall(bidRequest, callbackId); - assert.equal(url, expectedUrl); - }); - - - }); +var utils = require('../../src/utils'); +var bidmanager = require('../../src/bidmanager'); describe('replaceTokenInString', function(){ @@ -106,7 +64,7 @@ var appnexus = require('../src/adapters/appnexus'); }); it('Custom configuration for all bidders', function() { - pbjs.bidderSettings = + pbjs.bidderSettings = { standard: { adserverTargeting: [{ @@ -132,7 +90,7 @@ var appnexus = require('../src/adapters/appnexus'); } }] - + } }; @@ -143,7 +101,7 @@ var appnexus = require('../src/adapters/appnexus'); }); it('Custom configuration for one bidder', function() { - pbjs.bidderSettings = + pbjs.bidderSettings = { appnexus: { adserverTargeting: [{ @@ -169,7 +127,7 @@ var appnexus = require('../src/adapters/appnexus'); } }] - + } }; @@ -180,7 +138,7 @@ var appnexus = require('../src/adapters/appnexus'); }); it('Custom configuration for one bidder - not matched', function() { - pbjs.bidderSettings = + pbjs.bidderSettings = { nonExistentBidder: { adserverTargeting: [{ @@ -206,7 +164,7 @@ var appnexus = require('../src/adapters/appnexus'); } }] - + } }; @@ -217,7 +175,7 @@ var appnexus = require('../src/adapters/appnexus'); }); it('Custom bidCpmAdjustment for one bidder and inherit standard', function() { - pbjs.bidderSettings = + pbjs.bidderSettings = { appnexus: { bidCpmAdjustment : function(bidCpm){ @@ -242,7 +200,7 @@ var appnexus = require('../src/adapters/appnexus'); return 10.00; } }] - + } }; @@ -253,7 +211,7 @@ var appnexus = require('../src/adapters/appnexus'); }); it('Custom bidCpmAdjustment AND custom configuration for one bidder and do NOT inherit standard', function() { - pbjs.bidderSettings = + pbjs.bidderSettings = { appnexus: { bidCpmAdjustment : function(bidCpm){ @@ -302,7 +260,7 @@ var appnexus = require('../src/adapters/appnexus'); } }] - + } }; @@ -313,4 +271,4 @@ var appnexus = require('../src/adapters/appnexus'); }); }); - }); \ No newline at end of file + }); diff --git a/test/spec/unit/pbjs_api_spec.js b/test/spec/unit/pbjs_api_spec.js new file mode 100644 index 00000000000..08c7a947f23 --- /dev/null +++ b/test/spec/unit/pbjs_api_spec.js @@ -0,0 +1,172 @@ +var assert = require('chai').assert; + +var prebid = require('src/prebid'); +var utils = require('src/utils'); +var bidmanager = require('src/bidmanager'); + +var bidResponses = require('test/fixtures/bid-responses.json'); +var targetingMap = require('test/fixtures/targeting-map.json'); +var config = require('test/fixtures/config.json'); +var targetingString = 'hb_bidder=rubicon&hb_adid=148018fe5e&hb_pb=10.00&foobar=300x250&'; +var spyLogMessage = sinon.spy(utils, 'logMessage'); + +var Slot = function Slot(elementId, pathId) { + var slot = { + getSlotElementId: function getSlotElementId() { + return elementId; + }, + + getAdUnitPath: function getAdUnitPath() { + return pathId; + }, + + setTargeting: function setTargeting(key, value) { + } + }; + slot.spySetTargeting = sinon.spy(slot, 'setTargeting'); + return slot; +}; + +var createSlotArray = function createSlotArray() { + return [ + new Slot(config.adUnitElementIDs[0], config.adUnitCodes[0]), + new Slot(config.adUnitElementIDs[1], config.adUnitCodes[1]), + new Slot(config.adUnitElementIDs[2], config.adUnitCodes[2]) + ]; +}; + +window.googletag = { + _slots: [], + pubads: function () { + var self = this; + return { + getSlots: function () { + return self._slots; + }, + + setSlots: function (slots) { + self._slots = slots; + } + }; + } +}; + +bidmanager.pbBidResponseByPlacement = bidResponses; + +after(function () { + utils.logMessage.restore(); +}); + +describe('Unit: Prebid API', function () { + describe('getAdserverTargetingForAdUnitCodeStr', function () { + it('should return targeting info as a string', function () { + var result = pbjs.getAdserverTargetingForAdUnitCodeStr(config.adUnitCodes[0]); + assert.equal(result, targetingString, 'returns expected string of ad targeting info'); + }); + + it('should log message if adunitCode param is falsey', function () { + var result = pbjs.getAdserverTargetingForAdUnitCodeStr(); + assert.ok(spyLogMessage.calledWith('Need to call getAdserverTargetingForAdUnitCodeStr with adunitCode'), 'expected message was logged'); + assert.equal(result, undefined, 'result is undefined'); + }); + }); + + describe('getAdserverTargetingForAdUnitCode', function () { + it('should return targeting info as an object', function () { + var result = pbjs.getAdserverTargetingForAdUnitCode(config.adUnitCodes[0]); + assert.deepEqual(result, targetingMap[config.adUnitCodes[0]], 'returns expected targeting info object'); + }); + + it('should return full targeting map object if adunitCode is falsey', function () { + var result = pbjs.getAdserverTargetingForAdUnitCode(); + assert.deepEqual(result, targetingMap, 'the complete targeting map object is returned'); + }); + }); + + describe('getAdServerTargeting', function () { + it('should call getAdServerTargetingForAdUnitCode', function () { + var spyGetAdServerTargetingForAdUnitCode = sinon.spy(pbjs, 'getAdserverTargetingForAdUnitCode'); + pbjs.getAdserverTargeting(); + assert.ok(spyGetAdServerTargetingForAdUnitCode.calledOnce, 'called the expected function'); + pbjs.getAdserverTargetingForAdUnitCode.restore(); + }); + }); + + describe('getBidResponses', function () { + it('should return expected bid responses when passed an adunitCode', function () { + var result = pbjs.getBidResponses(config.adUnitCodes[0]); + var compare = require('test/fixtures/bid-responses-cloned.json')[config.adUnitCodes[0]]; + + assert.deepEqual(result, compare); + }); + + it('should return expected bid responses when not passed an adunitCode', function () { + var result = pbjs.getBidResponses(); + var compare = require('test/fixtures/bid-responses-cloned.json'); + + assert.deepEqual(result, compare); + }); + }); + + describe('getBidResponsesForAdUnitCode', function () { + it('should call getBidResponses with passed in adUnitCode', function () { + var adUnitCode = 'xyz'; + var spyGetBidResponses = sinon.spy(pbjs, 'getBidResponses'); + + pbjs.getBidResponsesForAdUnitCode(adUnitCode); + assert.ok(spyGetBidResponses.calledWith(adUnitCode)); + pbjs.getBidResponses.restore(); + }); + }); + + describe('setTargetingForGPTAsync', function () { + it('should log a message when googletag functions not defined', function () { + var pubads = window.googletag.pubads; + + window.googletag.pubads = undefined; + pbjs.setTargetingForAdUnitsGPTAsync(); + spyLogMessage.calledWith('window.googletag is not defined on the page'); + window.googletag.pubads = pubads; + }); + + it('should set targeting when passed an array of ad unit codes', function () { + var slots = createSlotArray(); + window.googletag.pubads().setSlots(slots); + + pbjs.setTargetingForGPTAsync(config.adUnitCodes); + assert.ok(slots[0].spySetTargeting.calledWithExactly('hb_bidder', ''), 'clears hb_bidder param'); + assert.ok(slots[0].spySetTargeting.calledWithExactly('hb_adid', ''), 'clears hb_adid param'); + assert.ok(slots[0].spySetTargeting.calledWithExactly('hb_pb', ''), 'clears hb_pb param'); + assert.ok(slots[0].spySetTargeting.calledWithExactly('foobar', ''), 'clears foobar param'); + assert.ok(slots[0].spySetTargeting.calledWithExactly('hb_bidder', 'rubicon'), 'sets hb_bidder param'); + assert.ok(slots[0].spySetTargeting.calledWithExactly('hb_adid', '148018fe5e'), 'sets hb_adid param'); + assert.ok(slots[0].spySetTargeting.calledWithExactly('hb_pb', '10.00'), 'sets hb_pb param'); + assert.ok(slots[0].spySetTargeting.calledWithExactly('foobar', '300x250'), 'sets foobar param'); + }); + + it('should set targeting from googletag data', function () { + var slots = createSlotArray(); + window.googletag.pubads().setSlots(slots); + + pbjs.setTargetingForGPTAsync(); + assert.ok(slots[0].spySetTargeting.calledWithExactly('hb_bidder', ''), 'clears hb_bidder param'); + assert.ok(slots[0].spySetTargeting.calledWithExactly('hb_adid', ''), 'clears hb_adid param'); + assert.ok(slots[0].spySetTargeting.calledWithExactly('hb_pb', ''), 'clears hb_pb param'); + assert.ok(slots[0].spySetTargeting.calledWithExactly('foobar', ''), 'clears foobar param'); + assert.ok(slots[0].spySetTargeting.calledWithExactly('hb_bidder', 'rubicon'), 'sets hb_bidder param'); + assert.ok(slots[0].spySetTargeting.calledWithExactly('hb_adid', '148018fe5e'), 'sets hb_adid param'); + assert.ok(slots[0].spySetTargeting.calledWithExactly('hb_pb', '10.00'), 'sets hb_pb param'); + assert.ok(slots[0].spySetTargeting.calledWithExactly('foobar', '300x250'), 'sets foobar param'); + }); + }); + + describe('allBidsAvailable', function () { + it('should call bidmanager.allBidsBack', function () { + var spyAllBidsBack = sinon.spy(bidmanager, 'allBidsBack'); + + pbjs.allBidsAvailable(); + assert.ok(spyAllBidsBack.called, 'called bidmanager.allBidsBack'); + bidmanager.allBidsBack.restore(); + }); + }); +}); diff --git a/test/spec/utils_spec.js b/test/spec/utils_spec.js index 73d5ae7113a..6b273473a65 100755 --- a/test/spec/utils_spec.js +++ b/test/spec/utils_spec.js @@ -1,475 +1,493 @@ -var assert = require("assert"); +var assert = require('assert'); var utils = require('../../src/utils'); -describe("Utils", function() { - - var obj_string = 's', - obj_number = 1, - obj_object = {}, - obj_array = [], - obj_function = function(){}; - - var type_string = 'String', - type_number = 'Number', - type_object = 'Object', - type_array = 'Array', - type_function = 'Function'; - - describe('replaceTokenInString', function(){ - - it('should replace all given tokens in a String', function() { - var tokensToReplace = { - 'foo': 'bar', - 'zap': 'quux' - }; - - var output = utils.replaceTokenInString("hello %FOO%, I am %ZAP%", tokensToReplace, "%"); - assert.equal(output, "hello bar, I am quux"); - }); - - it('should ignore tokens it does not see', function() { - var output = utils.replaceTokenInString("hello %FOO%", {}, "%"); - - assert.equal(output, "hello %FOO%"); - }); - }); - - describe('getBidIdParamater',function(){ - it('should return value of the key in input object',function(){ - var obj = { - 'a' : 'valueA', - 'b' : 'valueB' - } - var output = utils.getBidIdParamater('a',obj); - assert.equal(output,'valueA'); - }); - - it('should return empty string, if the key is not existsed in the object',function(){ - var obj = { - 'a' : 'valueA', - 'b' : 'valueB' - } - var output = utils.getBidIdParamater('c',obj); - assert.equal(output,''); - }); - }); - - describe('tryAppendQueryString',function(){ - it('should append query string to existing url',function(){ - var url = 'www.a.com?'; - var key = 'b'; - var value = 'c'; - - var output = utils.tryAppendQueryString(url, key, value); - - var expectedResult = url + key + "=" + encodeURIComponent(value) + '&'; - assert.equal(output,expectedResult); - }); - - it('should return existing url, if the value is empty',function(){ - var url = 'www.a.com?'; - var key = 'b'; - var value = ''; - - var output = utils.tryAppendQueryString(url, key, value); - assert.equal(output,url); - }); - }); - - describe('parseQueryStringParameters',function(){ - it('should append query string to existing using the input obj',function(){ - var obj={ - 'a':'1', - 'b':'2' - }; - - var output = utils.parseQueryStringParameters(obj); - var expectedResult = "a=" + encodeURIComponent('1') + "&b=" + encodeURIComponent('2') + "&"; - assert.equal(output,expectedResult); - }); - - it('should return an empty string, if input obj is empty',function(){ - var obj ={}; - var output = utils.parseQueryStringParameters(obj); - assert.equal(output,''); - }); - }); - - describe('transformAdServerTargetingObj',function(){ - it('should append query string to existing using the input obj',function(){ - var obj = { - 'a':'1', - 'b':'2' - }; - - var output = utils.transformAdServerTargetingObj(obj); - var expectedResult = "a=" + encodeURIComponent('1') + "&b=" + encodeURIComponent('2') + "&"; - assert.equal(output,expectedResult); - }); - - it('should return an empty string, if input obj is empty',function(){ - var obj ={}; - var output = utils.transformAdServerTargetingObj(obj); - assert.equal(output,''); - }); - }); - - describe('extend',function(){ - it('should merge two input object',function(){ - var target = { - 'a':'1', - 'b':'2' - }; - - var source = { - 'c':'3' - }; - - var expectedResult = { - 'a':'1', - 'b':'2', - 'c':'3' - }; - - var output = utils.extend(target, source); - assert.deepEqual(output,expectedResult); - }); - - it('should merge two input object even though target object is empty',function(){ - var target = {}; - var source = { - 'c':'3' - }; - - var output = utils.extend(target, source); - assert.deepEqual(output,source); - }); - - it('just return target object, if the source object is empty',function(){ - var target = { - 'a':'1', - 'b':'2' - }; - var source = {}; - - var output = utils.extend(target, source); - assert.deepEqual(output,target); - }); - }); - - describe('parseSizesInput',function(){ - - it('should return query string using multi size array',function(){ - var sizes = [[728, 90], [970, 90]]; - var output = utils.parseSizesInput(sizes); - assert.equal(output,'size=728x90&promo_sizes=970x90'); - }); - - it('should return query string using single size array',function(){ - var sizes = [728, 90]; - var output = utils.parseSizesInput(sizes); - assert.equal(output,'size=728x90'); - }); - - it('should return query string using string input',function(){ - var sizes = '300x250,970x90'; - var output = utils.parseSizesInput(sizes); - assert.equal(output,'size=300x250&promo_sizes=970x90'); - }); - - it('return undefined if input array is empty',function(){ - var sizes =[]; - var output = utils.parseSizesInput(sizes); - assert.equal(output,undefined); - }); - }); - - describe('parseGPTSingleSizeArray',function(){ - - it('should return size string with input single size array',function(){ - var size = [300,250]; - var output = utils.parseGPTSingleSizeArray(size); - assert.equal(output,'300x250'); - }); - - it('should return size string with input single size array',function(){ - var size =['300','250']; - var output = utils.parseGPTSingleSizeArray(size); - assert.equal(output,'300x250'); - }); - - it('return undefined using string input',function(){ - var size ='1'; - var output = utils.parseGPTSingleSizeArray(size); - assert.equal(output,undefined); - }); - - it('return undefined using number input',function(){ - var size =1; - var output = utils.parseGPTSingleSizeArray(size); - assert.equal(output,undefined); - }); - - it('return undefined using one length single array',function(){ - var size =[300]; - var output = utils.parseGPTSingleSizeArray(size); - assert.equal(output,undefined); - }); - - it('return undefined if the input is empty',function(){ - var size =''; - var output = utils.parseGPTSingleSizeArray(size); - assert.equal(output,undefined); - }); - - it('return undefined if the input is not a number',function(){ - var size =['foo','bar']; - var output = utils.parseGPTSingleSizeArray(size); - assert.equal(output,undefined); - }); - - it('return undefined if the input is not a number 2',function(){ - var size =['foo',300]; - var output = utils.parseGPTSingleSizeArray(size); - assert.equal(output,undefined); - }); - }); - - describe('isA',function(){ - it('should return true with string object', function() { - var output = utils.isA(obj_string,type_string); - assert.deepEqual(output,true); - }); - - it('should return false with object', function() { - var output = utils.isA(obj_object,type_string); - assert.deepEqual(output,false); - }); - - it('should return true with object', function() { - var output = utils.isA(obj_object,type_object); - assert.deepEqual(output,true); - }); - - it('should return false with array object', function() { - var output = utils.isA(obj_array,type_object); - assert.deepEqual(output,false); - }); - - it('should return true with array object', function() { - var output = utils.isA(obj_array,type_array); - assert.deepEqual(output,true); - }); - - it('should return false with array object', function() { - var output = utils.isA(obj_array,type_function); - assert.deepEqual(output,false); - }); - - it('should return true with function', function() { - var output = utils.isA(obj_function,type_function); - assert.deepEqual(output,true); - }); - - it('should return false with number', function() { - var output = utils.isA(obj_function,type_number); - assert.deepEqual(output,false); - }); - - it('should return true with number', function() { - var output = utils.isA(obj_number,type_number); - assert.deepEqual(output,true); - }); - }); - - describe('isFn', function() { - it('should return true with input function',function(){ - var output = utils.isFn(obj_function); - assert.deepEqual(output,true); - }); - - it('should return false with input string',function(){ - var output = utils.isFn(obj_string); - assert.deepEqual(output,false); - }); - - it('should return false with input number',function(){ - var output = utils.isFn(obj_number); - assert.deepEqual(output,false); - }); - - it('should return false with input Array',function(){ - var output = utils.isFn(obj_array); - assert.deepEqual(output,false); - }); - - it('should return false with input object',function(){ - var output = utils.isFn(obj_object); - assert.deepEqual(output,false); - }); - }); - - describe('isStr',function(){ - it('should return true with input string',function(){ - var output = utils.isStr(obj_string); - assert.deepEqual(output,true); - }); - - it('should return false with input number',function(){ - var output = utils.isStr(obj_number); - assert.deepEqual(output,false); - }); - - it('should return false with input object',function(){ - var output = utils.isStr(obj_object); - assert.deepEqual(output,false); - }); - - it('should return false with input array',function(){ - var output = utils.isStr(obj_array); - assert.deepEqual(output,false); - }); - - it('should return false with input function',function(){ - var output = utils.isStr(obj_function); - assert.deepEqual(output,false); - }); - - }); - - describe('isArray',function(){ - it('should return false with input string',function(){ - var output = utils.isArray(obj_string); - assert.deepEqual(output,false); - }); - - it('should return false with input number',function(){ - var output = utils.isArray(obj_number); - assert.deepEqual(output,false); - }); - - it('should return false with input object',function(){ - var output = utils.isArray(obj_object); - assert.deepEqual(output,false); - }); - - it('should return true with input array',function(){ - var output = utils.isArray(obj_array); - assert.deepEqual(output,true); - }); - - it('should return false with input function',function(){ - var output = utils.isArray(obj_function); - assert.deepEqual(output,false); - }); - - }); - - - describe('isEmpty',function(){ - it('should return true with empty object',function(){ - var output = utils.isEmpty(obj_object); - assert.deepEqual(output,true); - }); - - it('should return false with non-empty object',function(){ - var obj = {'a':'b'}; - var output = utils.isEmpty(obj); - assert.deepEqual(output,false); - }); - - it('should return false with null',function(){ - var obj = null; - var output = utils.isEmpty(obj); - assert.deepEqual(output,true); - }); - }); - - describe('contains',function(){ - it('should return true if the input string contains in the input obj',function(){ - var output = utils.contains('123','1'); - assert.deepEqual(output,true); +describe('Utils', function () { + + var obj_string = 's', + obj_number = 1, + obj_object = {}, + obj_array = [], + obj_function = function () {}; + + var type_string = 'String', + type_number = 'Number', + type_object = 'Object', + type_array = 'Array', + type_function = 'Function'; + + describe('replaceTokenInString', function () { + + it('should replace all given tokens in a String', function () { + var tokensToReplace = { + foo: 'bar', + zap: 'quux' + }; + + var output = utils.replaceTokenInString('hello %FOO%, I am %ZAP%', tokensToReplace, '%'); + assert.equal(output, 'hello bar, I am quux'); + }); + + it('should ignore tokens it does not see', function () { + var output = utils.replaceTokenInString('hello %FOO%', {}, '%'); + + assert.equal(output, 'hello %FOO%'); + }); + }); + + describe('getBidIdParamater', function () { + it('should return value of the key in input object', function () { + var obj = { + a: 'valueA', + b: 'valueB' + }; + var output = utils.getBidIdParamater('a', obj); + assert.equal(output, 'valueA'); + }); + + it('should return empty string, if the key is not existsed in the object', function () { + var obj = { + a: 'valueA', + b: 'valueB' + }; + var output = utils.getBidIdParamater('c', obj); + assert.equal(output, ''); + }); + }); + + describe('tryAppendQueryString', function () { + it('should append query string to existing url', function () { + var url = 'www.a.com?'; + var key = 'b'; + var value = 'c'; + + var output = utils.tryAppendQueryString(url, key, value); + + var expectedResult = url + key + '=' + encodeURIComponent(value) + '&'; + assert.equal(output, expectedResult); + }); + + it('should return existing url, if the value is empty', function () { + var url = 'www.a.com?'; + var key = 'b'; + var value = ''; + + var output = utils.tryAppendQueryString(url, key, value); + assert.equal(output, url); + }); + }); + + describe('parseQueryStringParameters', function () { + it('should append query string to existing using the input obj', function () { + var obj = { + a:'1', + b:'2' + }; + + var output = utils.parseQueryStringParameters(obj); + var expectedResult = 'a=' + encodeURIComponent('1') + '&b=' + encodeURIComponent('2') + '&'; + assert.equal(output, expectedResult); + }); + + it('should return an empty string, if input obj is empty', function () { + var obj = {}; + var output = utils.parseQueryStringParameters(obj); + assert.equal(output, ''); + }); + }); + + describe('transformAdServerTargetingObj', function () { + it('should append query string to existing using the input obj', function () { + var obj = { + a:'1', + b:'2' + }; + + var output = utils.transformAdServerTargetingObj(obj); + var expectedResult = 'a=' + encodeURIComponent('1') + '&b=' + encodeURIComponent('2') + '&'; + assert.equal(output, expectedResult); + }); + + it('should return an empty string, if input obj is empty', function () { + var obj = {}; + var output = utils.transformAdServerTargetingObj(obj); + assert.equal(output, ''); + }); + }); + + describe('extend', function () { + it('should merge two input object', function () { + var target = { + a:'1', + b:'2' + }; + + var source = { + c:'3' + }; + + var expectedResult = { + a:'1', + b:'2', + c:'3' + }; + + var output = utils.extend(target, source); + assert.deepEqual(output, expectedResult); + }); + + it('should merge two input object even though target object is empty', function () { + var target = {}; + var source = { + c:'3' + }; + + var output = utils.extend(target, source); + assert.deepEqual(output, source); + }); + + it('just return target object, if the source object is empty', function () { + var target = { + a:'1', + b:'2' + }; + var source = {}; + + var output = utils.extend(target, source); + assert.deepEqual(output, target); + }); + }); + + describe('parseSizesInput', function () { + + it('should return query string using multi size array', function () { + var sizes = [[728, 90], [970, 90]]; + var output = utils.parseSizesInput(sizes); + assert.deepEqual(output, ['728x90', '970x90']); + }); + + it('should return query string using single size array', function () { + var sizes = [728, 90]; + var output = utils.parseSizesInput(sizes); + assert.deepEqual(output, ['728x90']); + }); + + it('should return query string using string input', function () { + var sizes = '300x250,970x90'; + var output = utils.parseSizesInput(sizes); + assert.deepEqual(output, ['300x250', '970x90']); + }); + + it('return undefined if input array is empty', function () { + var sizes = []; + var output = utils.parseSizesInput(sizes); + assert.deepEqual(output, []); + }); + }); + + describe('parseGPTSingleSizeArray', function () { + + it('should return size string with input single size array', function () { + var size = [300, 250]; + var output = utils.parseGPTSingleSizeArray(size); + assert.equal(output, '300x250'); + }); + + it('should return size string with input single size array', function () { + var size = ['300', '250']; + var output = utils.parseGPTSingleSizeArray(size); + assert.equal(output, '300x250'); + }); + + it('return undefined using string input', function () { + var size = '1'; + var output = utils.parseGPTSingleSizeArray(size); + assert.equal(output, undefined); + }); + + it('return undefined using number input', function () { + var size = 1; + var output = utils.parseGPTSingleSizeArray(size); + assert.equal(output, undefined); + }); + + it('return undefined using one length single array', function () { + var size = [300]; + var output = utils.parseGPTSingleSizeArray(size); + assert.equal(output, undefined); + }); + + it('return undefined if the input is empty', function () { + var size = ''; + var output = utils.parseGPTSingleSizeArray(size); + assert.equal(output, undefined); + }); + + it('return undefined if the input is not a number', function () { + var size = ['foo', 'bar']; + var output = utils.parseGPTSingleSizeArray(size); + assert.equal(output, undefined); + }); + + it('return undefined if the input is not a number 2', function () { + var size = ['foo', 300]; + var output = utils.parseGPTSingleSizeArray(size); + assert.equal(output, undefined); + }); + }); + + describe('isA', function () { + it('should return true with string object', function () { + var output = utils.isA(obj_string, type_string); + assert.deepEqual(output, true); + }); + + it('should return false with object', function () { + var output = utils.isA(obj_object, type_string); + assert.deepEqual(output, false); + }); + + it('should return true with object', function () { + var output = utils.isA(obj_object, type_object); + assert.deepEqual(output, true); + }); + + it('should return false with array object', function () { + var output = utils.isA(obj_array, type_object); + assert.deepEqual(output, false); + }); + + it('should return true with array object', function () { + var output = utils.isA(obj_array, type_array); + assert.deepEqual(output, true); + }); + + it('should return false with array object', function () { + var output = utils.isA(obj_array, type_function); + assert.deepEqual(output, false); + }); + + it('should return true with function', function () { + var output = utils.isA(obj_function, type_function); + assert.deepEqual(output, true); + }); + + it('should return false with number', function () { + var output = utils.isA(obj_function, type_number); + assert.deepEqual(output, false); + }); + + it('should return true with number', function () { + var output = utils.isA(obj_number, type_number); + assert.deepEqual(output, true); + }); + }); + + describe('isFn', function () { + it('should return true with input function', function () { + var output = utils.isFn(obj_function); + assert.deepEqual(output, true); + }); + + it('should return false with input string', function () { + var output = utils.isFn(obj_string); + assert.deepEqual(output, false); + }); + + it('should return false with input number', function () { + var output = utils.isFn(obj_number); + assert.deepEqual(output, false); + }); + + it('should return false with input Array', function () { + var output = utils.isFn(obj_array); + assert.deepEqual(output, false); + }); + + it('should return false with input object', function () { + var output = utils.isFn(obj_object); + assert.deepEqual(output, false); + }); + }); + + describe('isStr', function () { + it('should return true with input string', function () { + var output = utils.isStr(obj_string); + assert.deepEqual(output, true); + }); + + it('should return false with input number', function () { + var output = utils.isStr(obj_number); + assert.deepEqual(output, false); + }); + + it('should return false with input object', function () { + var output = utils.isStr(obj_object); + assert.deepEqual(output, false); + }); + + it('should return false with input array', function () { + var output = utils.isStr(obj_array); + assert.deepEqual(output, false); + }); + + it('should return false with input function', function () { + var output = utils.isStr(obj_function); + assert.deepEqual(output, false); + }); + + }); + + describe('isArray', function () { + it('should return false with input string', function () { + var output = utils.isArray(obj_string); + assert.deepEqual(output, false); + }); + + it('should return false with input number', function () { + var output = utils.isArray(obj_number); + assert.deepEqual(output, false); + }); + + it('should return false with input object', function () { + var output = utils.isArray(obj_object); + assert.deepEqual(output, false); + }); + + it('should return true with input array', function () { + var output = utils.isArray(obj_array); + assert.deepEqual(output, true); + }); + + it('should return false with input function', function () { + var output = utils.isArray(obj_function); + assert.deepEqual(output, false); + }); + + }); + + describe('isEmpty', function () { + it('should return true with empty object', function () { + var output = utils.isEmpty(obj_object); + assert.deepEqual(output, true); + }); + + it('should return false with non-empty object', function () { + var obj = { a:'b' }; + var output = utils.isEmpty(obj); + assert.deepEqual(output, false); + }); + + it('should return false with null', function () { + var obj = null; + var output = utils.isEmpty(obj); + assert.deepEqual(output, true); + }); + }); + + describe('contains', function () { + it('should return true if the input string contains in the input obj', function () { + var output = utils.contains('123', '1'); + assert.deepEqual(output, true); }); - it('should return false if the input string do not contain in the input obj',function(){ - var output = utils.contains('234','1'); - assert.deepEqual(output,false); + it('should return false if the input string do not contain in the input obj', function () { + var output = utils.contains('234', '1'); + assert.deepEqual(output, false); }); + + it('should return false if the input string is empty', function () { + var output = utils.contains(); + assert.ok(!output, 'an empty string returns false'); }); + }); - describe('_map',function(){ - it('return empty array when input object is empty',function(){ - var input = {}; - var callback = function(){}; - var output = utils._map(input,callback); - assert.deepEqual(output,[]); + describe('_map', function () { + it('return empty array when input object is empty', function () { + var input = {}; + var callback = function () {}; + + var output = utils._map(input, callback); + assert.deepEqual(output, []); }); - it('return value array with vaild input object',function(){ - var input = { 'a':'A','b':'B'}; - var callback = function(v){return v;}; - var output = utils._map(input,callback); - assert.deepEqual(output,['A','B']); + it('return value array with vaild input object', function () { + var input = { a:'A', b:'B' }; + var callback = function (v) {return v;}; + + var output = utils._map(input, callback); + assert.deepEqual(output, ['A', 'B']); }); - it('return value array with vaild input object_callback func changed 1',function(){ - var input = { 'a':'A','b':'B'}; - var callback = function(v,k){return v+k;}; - var output = utils._map(input,callback); - assert.deepEqual(output,['Aa','Bb']); + it('return value array with vaild input object_callback func changed 1', function () { + var input = { a:'A', b:'B' }; + var callback = function (v, k) {return v + k;}; + + var output = utils._map(input, callback); + assert.deepEqual(output, ['Aa', 'Bb']); }); - it('return value array with vaild input object_callback func changed 2',function(){ - var input = { 'a':'A','b':'B'}; - var callback = function(v,k,o){return o;}; - var output = utils._map(input,callback); - assert.deepEqual(output,[input,input]); + it('return value array with vaild input object_callback func changed 2', function () { + var input = { a:'A', b:'B' }; + var callback = function (v, k, o) {return o;}; + + var output = utils._map(input, callback); + assert.deepEqual(output, [input, input]); }); + }); + + describe('createInvisibleIframe', function () { + var output = utils.createInvisibleIframe(); + + it('return iframe - id', function () { + assert.ok(output.id); + }); + + it('return iframe - height', function () { + assert.deepEqual(output.height, 0); }); - describe('createInvisibleIframe',function(){ - var output = utils.createInvisibleIframe(); - - it('return iframe - id',function(){ - assert.ok(output.id); - }); - it('return iframe - height',function(){ - assert.deepEqual(output.height,0); - }); - it('return iframe - width',function(){ - assert.deepEqual(output.width,0); - }); - it('return iframe - border',function(){ - assert.deepEqual(output.border,'0px'); - }); - it('return iframe - hspace',function(){ - assert.deepEqual(output.hspace,'0'); - }); - it('return iframe - vspace',function(){ - assert.deepEqual(output.vspace,'0'); - }); - it('return iframe - marginWidth',function(){ - assert.deepEqual(output.marginWidth,'0'); - }); - it('return iframe - marginHeight',function(){ - assert.deepEqual(output.marginHeight,'0'); - }); - it('return iframe - style.border',function(){ - assert.deepEqual(output.style.border,'0px'); - }); - it('return iframe - scrolling',function(){ - assert.deepEqual(output.scrolling,'no'); - }); - it('return iframe - frameBorder',function(){ - assert.deepEqual(output.frameBorder,'0'); - }); - it('return iframe - src',function(){ - assert.deepEqual(output.src,'about:self'); - }); - it('return iframe - style',function(){ - assert.ok(output.style); - }); - }); + it('return iframe - width', function () { + assert.deepEqual(output.width, 0); + }); + it('return iframe - border', function () { + assert.deepEqual(output.border, '0px'); + }); + + it('return iframe - hspace', function () { + assert.deepEqual(output.hspace, '0'); + }); + + it('return iframe - vspace', function () { + assert.deepEqual(output.vspace, '0'); + }); + + it('return iframe - marginWidth', function () { + assert.deepEqual(output.marginWidth, '0'); + }); + + it('return iframe - marginHeight', function () { + assert.deepEqual(output.marginHeight, '0'); + }); + + //it('return iframe - style.border',function(){ + // assert.deepEqual(output.style.border,'0px'); + //}); + it('return iframe - scrolling', function () { + assert.deepEqual(output.scrolling, 'no'); + }); + + it('return iframe - frameBorder', function () { + assert.deepEqual(output.frameBorder, '0'); + }); + + it('return iframe - src', function () { + assert.deepEqual(output.src, 'about:blank'); + }); + + it('return iframe - style', function () { + assert.ok(output.style); + }); + }); }); diff --git a/test/utilsTest.js b/test/utilsTest.js deleted file mode 100644 index d367ab66278..00000000000 --- a/test/utilsTest.js +++ /dev/null @@ -1,28 +0,0 @@ -var assert = require("assert"); - -describe('Utils', function () { - - window = { console: console }; - - var utils = require('../src/utils.js'); - - describe('#replaceTokenInString', function () { - - it('should replace all given tokens in a String', function () { - var tokensToReplace = { - 'foo': 'bar', - 'zap': 'quux' - }; - - var output = utils.replaceTokenInString("hello %FOO%, I am %ZAP%", tokensToReplace, "%"); - - assert.equal(output, "hello bar, I am quux"); - }); - - it('should ignore tokens it does not see', function() { - var output = utils.replaceTokenInString("hello %FOO%", {}, "%"); - - assert.equal(output, "hello %FOO%"); - }); - }) -}); \ No newline at end of file diff --git a/webpack.conf.js b/webpack.conf.js new file mode 100644 index 00000000000..6dcc1cd9e98 --- /dev/null +++ b/webpack.conf.js @@ -0,0 +1,33 @@ +module.exports = { + output: { + filename: 'prebid.js' + }, + devtool: 'source-map', + resolve: { + modulesDirectories: ['', 'node_modules', 'src'] + }, + resolveLoader: { + modulesDirectories: ['loaders', 'node_modules'] + }, + module: { + loaders: [ + { + test: /\.js$/, + include: /(src|test)/, + loader: 'babel', // 'babel-loader' is also a legal name to reference + query: { + presets: ['es2015'] + } + }, + { + test: /\.json$/, + loader: 'json' + }, + { + test: /adaptermanager.js/, + include: /(src)/, + loader: 'adapterLoader' + } + ] + } +};