angular-dfp
Version:
Semantic DoubleClick integration with AngularJS
1,627 lines (1,194 loc) • 42.5 kB
JavaScript
/**
* @license Apache
* Copyright 2016 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
;
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var googletag = googletag || {};
googletag.cmd = googletag.cmd || [];
var angularDfp = angular.module('angularDfp', []);
( function (module) {
'use strict';
function httpErrorFactory($log) {
function httpError(response, message) {
$log.error('Error (' + response.status + ')');
}
httpError.isErrorCode = function (code) {
if (typeof code === 'number') {
return !(code >= 200 && code < 300);
}
console.assert(typeof code === 'string');
return code[0] !== '2';
};
return httpError;
}
module.factory('httpError', ['$log', httpErrorFactory]);
})(angularDfp);
( function (module) {
'use strict';
var DFPDurationError = function (_Error) {
_inherits(DFPDurationError, _Error);
function DFPDurationError(interval) {
_classCallCheck(this, DFPDurationError);
return _possibleConstructorReturn(this, (DFPDurationError.__proto__ || Object.getPrototypeOf(DFPDurationError)).call(this, 'Invalid interval: \'' + interval + '\'ls'));
}
return DFPDurationError;
}(Error);
function parseDurationFactory() {
function convertToMilliseconds(time, unit) {
console.assert(/^(m?s|min|h)$/g.test(unit));
if (unit === 'ms') return time;
if (unit === 's') return time * 1000;
if (unit === 'min') return time * 60 * 1000;
return time * 60 * 60 * 1000;
}
function convert(match) {
var time = parseFloat(match[1]);
if (match.length === 2) return time;
return convertToMilliseconds(time, match[2]);
}
function parseDuration(interval) {
if (interval === undefined || interval === null) {
throw new DFPDurationError(interval);
}
if (typeof interval === 'number') {
return interval;
}
if (typeof interval !== 'string') {
throw new TypeError('\'' + interval + '\' must be of number or string type');
}
var match = interval.match(/((?:\d+)?.?\d+)(m?s|min|h)?/);
if (!match) {
throw new DFPDurationError(interval);
}
return convert(match);
}
return parseDuration;
}
module.factory('parseDuration', parseDurationFactory);
})(angularDfp);
( function (module) {
'use strict';
function scriptInjectorFactory($q, httpError) {
function createScript(url) {
var script = document.createElement('script');
var ssl = document.location.protocol === 'https:';
script.async = 'async';
script.type = 'text/javascript';
script.src = (ssl ? 'https:' : 'http:') + url;
return script;
}
function promiseScript(script, url) {
var deferred = $q.defer();
function resolve() {
deferred.resolve();
}
function reject(response) {
response = response || { status: 400 };
httpError(response, 'loading script "{0}".', url);
deferred.reject(response);
}
script.onreadystatechange = function () {
if (this.readyState === 4) {
if (httpError.isErrorCode(this.status)) {
reject(this);
} else {
resolve();
}
}
};
script.onload = resolve;
script.onerror = reject;
return deferred.promise;
}
function injectScript(script) {
var head = document.head || document.querySelector('head');
head.appendChild(script);
}
function scriptInjector(url) {
var script = createScript(url);
injectScript(script);
return promiseScript(script, url);
}
return scriptInjector;
}
module.factory('scriptInjector', ['$q', 'httpError', scriptInjectorFactory]);
})(angularDfp);
var googletag = googletag || {};
googletag.cmd = googletag.cmd || [];
( function (module) {
'use strict';
function dfpAdController(DFPIncompleteError) {
var sizes = [];
var responsiveMapping = [];
var targetings = [];
var exclusions = [];
var scripts = [];
this.booleanProperty = function (name) {
return this[name] !== undefined;
};
this.checkValid = function () {
if (sizes.length === 0) {
throw new DFPIncompleteError('dfp-ad', 'dfp-size');
}
if (!this['adUnit']) {
throw new DFPIncompleteError('dfp-ad', 'ad-unit', true);
}
};
this.getState = function () {
this.checkValid();
return Object.freeze({
sizes: sizes,
responsiveMapping: responsiveMapping,
targetings: targetings,
exclusions: exclusions,
adUnit: this['adUnit'],
forceSafeFrame: this.booleanProperty('forceSafeFrame'),
safeFrameConfig: this['safeFrameConfig'],
clickUrl: this['clickUrl'],
refresh: this['refresh'],
scripts: scripts,
collapseIfEmpty: this.booleanProperty('collapseIfEmpty')
});
};
this.addSize = function (size) {
sizes.push(size);
};
this.addResponsiveMapping = function (mapping) {
responsiveMapping.push(mapping);
};
this.addTargeting = function (targeting) {
targetings.push(targeting);
};
this.addExclusion = function (exclusion) {
exclusions.push(exclusion);
};
this.addScript = function (script) {
scripts.push(script);
};
}
function dfpAdDirective(scope, element, attributes, controller, $injector) {
var dfp = $injector.get('dfp');
var dfpIDGenerator = $injector.get('dfpIDGenerator');
var dfpRefresh = $injector.get('dfpRefresh');
var dfpResponsiveResize = $injector.get('dfpResponsiveResize');
var ad = controller.getState();
var jQueryElement = element;
element = element[0];
dfpIDGenerator(element);
function addResponsiveMapping(slot) {
if (ad.responsiveMapping.length === 0) return;
var sizeMapping = googletag.sizeMapping();
ad.responsiveMapping.forEach(function (mapping) {
sizeMapping.addSize(mapping.viewportSize, mapping.adSizes);
});
slot.defineSizeMapping(sizeMapping.build());
}
function extractViewportDimensions(responsiveMappings) {
return responsiveMappings.map(function (mapping) {
return {
width: mapping.viewportSize[0],
height: mapping.viewportSize[1]
};
});
}
function defineSlot() {
var slot = googletag.defineSlot(ad.adUnit, ad.sizes, element.id);
if (ad.forceSafeFrame !== undefined) {
slot.setForceSafeFrame(true);
}
if (ad.clickUrl) {
slot.setClickUrl(ad.clickUrl);
}
if (ad.collapseIfEmpty) {
slot.setCollapseEmptyDiv(true, true);
}
if (ad.safeFrameConfig) {
slot.setSafeFrameConfig(
JSON.parse(ad.safeFrameConfig));
}
addResponsiveMapping(slot);
ad.targetings.forEach(function (targeting) {
slot.setTargeting(targeting.key, targeting.values);
});
ad.exclusions.forEach(function (exclusion) {
slot.setCategoryExclusion(exclusion);
});
ad.scripts.forEach(function (script) {
script(slot);
});
slot.addService(googletag.pubads());
googletag.display(element.id);
dfpRefresh(slot, ad.refresh).then(function () {
if (ad.responsiveMapping.length > 0) {
var dimensions = extractViewportDimensions(ad.responsiveMapping);
dfpResponsiveResize(jQueryElement, slot, dimensions);
}
});
scope.$on('$destroy', function () {
console.assert(googletag.destroySlots([slot]));
});
}
dfp.then(defineSlot);
}
module.directive('dfpAd', ['$injector', function ($injector) {
return {
restrict: 'AE',
controller: ['DFPIncompleteError', dfpAdController],
controllerAs: 'controller',
bindToController: true,
link: function link() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
dfpAdDirective.apply(null, args.slice(0, 4).concat($injector));
},
scope: {
'adUnit': '@',
'clickUrl': '@',
'forceSafeFrame': '@',
'safeFrameConfig': '@',
'refresh': '@',
'collapseIfEmpty': '@'
}
};
}]);
})(angularDfp);
( function (module) {
'use strict';
function dfpAudiencePixelDirective(scope, element, attributes) {
var axel = String(Math.random());
var random = axel * 10000000000000;
var adUnit = '';
if (scope.adUnit) {
adUnit = 'dc_iu=' + scope['adUnit'];
}
var ppid = '';
if (scope.ppid) {
ppid = 'ppid=' + scope['ppid'];
}
var pixel = document.createElement('img');
pixel.src = 'https://pubads.g.doubleclick.net/activity;ord=';
pixel.src += random + ';dc_seg=' + scope['segmentId'] + ';' + adUnit + ppid;
pixel.width = 1;
pixel.height = 1;
pixel.border = 0;
pixel.style.visibility = 'hidden';
element.append(pixel);
}
module.directive('dfpAudiencePixel', [function () {
return {
restrict: 'E',
link: dfpAudiencePixelDirective,
scope: { 'adUnit': '@', 'segmentId': '@', 'ppid': '@' }
};
}]);
})(angularDfp);
( function (module) {
'use strict';
function dfpIncompleteErrorFactory() {
var DFPIncompleteError = function (_Error2) {
_inherits(DFPIncompleteError, _Error2);
function DFPIncompleteError(directiveName, missingName, isAttribute) {
_classCallCheck(this, DFPIncompleteError);
return _possibleConstructorReturn(this, (DFPIncompleteError.__proto__ || Object.getPrototypeOf(DFPIncompleteError)).call(this, 'Incomplete definition of \'' + directiveName + '\': ' + ('Missing ' + (isAttribute ? 'attribute' : 'child directive') + ' ') + ('\'' + missingName + '\'.')));
}
return DFPIncompleteError;
}(Error);
return DFPIncompleteError;
}
function dfpTypeErrorFactory() {
var DFPTypeError = function (_Error3) {
_inherits(DFPTypeError, _Error3);
function DFPTypeError(directiveName, attributeName, wrongValue, expectedType) {
_classCallCheck(this, DFPTypeError);
return _possibleConstructorReturn(this, (DFPTypeError.__proto__ || Object.getPrototypeOf(DFPTypeError)).call(this, 'Wrong type for attribute \'' + attributeName + '\' on ' + ('directive \'' + directiveName + '\': Expected ' + expectedType) + (', got ' + (typeof wrongValue === 'undefined' ? 'undefined' : _typeof(wrongValue)))));
}
return DFPTypeError;
}(Error);
return DFPTypeError;
}
function dfpMissingParentErrorFactory() {
var DFPMissingParentError = function (_Error4) {
_inherits(DFPMissingParentError, _Error4);
function DFPMissingParentError(directiveName) {
for (var _len2 = arguments.length, parents = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
parents[_key2 - 1] = arguments[_key2];
}
_classCallCheck(this, DFPMissingParentError);
console.assert(parents && parents.length > 0);
if (Array.isArray(parents[0])) {
parents = parents[0];
}
var parentMessage = void 0;
if (parents.length > 1) {
parents = parents.map(function (p) {
return '\'' + p + '\'';
});
parentMessage = ', which must be ';
parentMessage += parents.slice(0, -1).join(', ');
parentMessage += ' or ' + parents[parents.length - 1];
} else {
parentMessage = ' \'' + parents[0] + '\'';
}
return _possibleConstructorReturn(this, (DFPMissingParentError.__proto__ || Object.getPrototypeOf(DFPMissingParentError)).call(this, 'Invalid use of \'' + directiveName + '\' directive. ' + ('Missing parent directive' + parentMessage + '.')));
}
return DFPMissingParentError;
}(Error);
return DFPMissingParentError;
}
module.factory('DFPIncompleteError', dfpIncompleteErrorFactory);
module.factory('DFPTypeError', dfpTypeErrorFactory);
module.factory('DFPMissingParentError', dfpMissingParentErrorFactory);
})(angularDfp);
( function (module) {
'use strict';
function dfpExclusionDirective(scope, element, attributes, ad) {
ad.addExclusion(element.html());
}
module.directive('dfpExclusion', [function () {
return {
restrict: 'E',
require: '^^dfpAd',
link: dfpExclusionDirective
};
}]);
})(angularDfp);
( function (module) {
'use strict';
function dfpIDGeneratorFactory() {
var generatedIDs = {};
function generateID() {
var id = null;
do {
var number = Math.random().toString().slice(2);
id = 'gpt-ad-' + number;
} while (id in generatedIDs);
generatedIDs[id] = true;
return id;
}
function dfpIDGenerator(element) {
if (element && element.id && !(element.id in generatedIDs)) {
return element.id;
}
var id = generateID();
if (element) element.id = id;
return id;
}
dfpIDGenerator.isTaken = function (id) {
return id in generatedIDs;
};
dfpIDGenerator.isUnique = function (id) {
return !dfpIDGenerator.isTaken(id);
};
return dfpIDGenerator;
}
module.factory('dfpIDGenerator', [dfpIDGeneratorFactory]);
})(angularDfp);
var googletag = googletag || {};
googletag.cmd = googletag.cmd || [];
( function (module) {
'use strict';
var DFPRefreshError = function (_Error5) {
_inherits(DFPRefreshError, _Error5);
function DFPRefreshError() {
_classCallCheck(this, DFPRefreshError);
return _possibleConstructorReturn(this, (DFPRefreshError.__proto__ || Object.getPrototypeOf(DFPRefreshError)).apply(this, arguments));
}
return DFPRefreshError;
}(Error);
function dfpRefreshProvider() {
var self = this;
self.bufferInterval = null;
self.bufferBarrier = null;
self.oneShotBarrier = true;
self.refreshInterval = null;
self.priority = {
'refresh': 1,
'interval': 1,
'barrier': 1
};
self.$get = ['$rootScope', '$interval', '$q', '$log', 'parseDuration', function ($rootScope, $interval, $q, $log, parseDuration) {
var Options = Object.freeze({
REFRESH: 'refresh',
INTERVAL: 'interval',
BARRIER: 'barrier'
});
dfpRefresh.Options = Object.freeze({
'REFRESH': Options.REFRESH,
'INTERVAL': Options.INTERVAL,
'BARRIER': Options.BARRIER
});
var buffer = [];
var intervals = { refresh: null, buffer: null };
var isEnabled = Object.seal({
refresh: self.refreshInterval !== null,
interval: self.bufferInterval !== null,
barrier: self.bufferBarrier !== null
});
function dfpRefresh(slot, interval, defer) {
var deferred = $q.defer();
var task = { slot: slot, deferred: deferred };
if (interval) {
addSlotInterval(task, interval);
}
if (!interval || !defer) {
scheduleRefresh(task);
}
return deferred.promise;
}
dfpRefresh.cancelInterval = function (slot) {
if (!dfpRefresh.hasSlotInterval(slot)) {
throw new DFPRefreshError("No interval for given slot");
}
$interval.cancel(intervals[slot]);
delete intervals[slot];
return dfpRefresh;
};
dfpRefresh.hasSlotInterval = function (slot) {
return slot in intervals;
};
dfpRefresh.setBufferInterval = function (interval) {
self.bufferInterval = parseDuration(interval);
prioritize();
return dfpRefresh;
};
dfpRefresh.clearBufferInterval = function () {
if (!dfpRefresh.hasBufferInterval()) {
console.warn("clearBufferInterval had no " + "effect because no interval was set.");
return dfpRefresh;
}
disableBufferInterval();
self.bufferInterval = null;
prioritize();
return dfpRefresh;
};
dfpRefresh.hasBufferInterval = function () {
return self.bufferInterval !== null;
};
dfpRefresh.bufferIntervalIsEnabled = function () {
return isEnabled.interval;
};
dfpRefresh.getBufferInterval = function () {
return self.bufferInterval;
};
dfpRefresh.setBufferBarrier = function (numberOfAds, oneShot) {
self.bufferBarrier = numberOfAds;
self.oneShotBarrier = oneShot === undefined ? true : oneShot;
prioritize();
return dfpRefresh;
};
dfpRefresh.clearBufferBarrier = function () {
if (!dfpRefresh.hasBufferBarrier()) {
console.warn("clearBufferBarrier had not effect because " + "no barrier was set.");
return dfpRefresh;
}
self.bufferBarrier = null;
prioritize();
return dfpRefresh;
};
dfpRefresh.getBufferBarrier = function () {
return self.bufferBarrier;
};
dfpRefresh.hasBufferBarrier = function () {
return self.bufferBarrier !== null;
};
dfpRefresh.bufferBarrierIsEnabled = function () {
return isEnabled.barrier;
};
dfpRefresh.bufferBarrierIsOneShot = function () {
return self.oneShotBarrier;
};
dfpRefresh.setRefreshInterval = function (interval) {
self.refreshInterval = parseDuration(interval);
validateInterval(self.refreshInterval, interval);
enableRefreshInterval();
prioritize();
return dfpRefresh;
};
dfpRefresh.hasRefreshInterval = function () {
return self.refreshInterval !== null;
};
dfpRefresh.refreshIntervalIsEnabled = function () {
return isEnabled.refresh;
};
dfpRefresh.clearRefreshInterval = function () {
if (!dfpRefresh.hasRefreshInterval()) {
console.warn("clearRefreshInterval had no effect because " + "no refresh interval was set.");
}
disableRefreshInterval();
prioritize();
return dfpRefresh;
};
dfpRefresh.getRefreshInterval = function () {
return self.refreshInterval;
};
dfpRefresh.isBuffering = function () {
return isEnabled.barrier || isEnabled.interval;
};
dfpRefresh.has = function (option) {
switch (option) {
case Options.REFRESH:
return dfpRefresh.hasRefreshInterval();
case Options.INTERVAL:
return dfpRefresh.hasBufferInterval();
case Options.BARRIER:
return dfpRefresh.hasBufferBarrier();
default:
throw new DFPRefreshError('Invalid option \'' + option + '\'');
}
};
dfpRefresh.setPriority = function (option, priority) {
ensureValidOption(option);
ensureValidPriority(priority);
self.priority[option] = priority;
return dfpRefresh;
};
dfpRefresh.getPriority = function (option) {
ensureValidOption(option);
return self.priority[option];
};
dfpRefresh.setRefreshPriority = function (priority) {
ensureValidPriority(priority);
dfpRefresh.setPriority('refresh', priority);
};
dfpRefresh.getRefreshPriority = function () {
return dfpRefresh.getPriority('refresh');
};
dfpRefresh.setBarrierPriority = function (priority) {
ensureValidPriority(priority);
dfpRefresh.setPriority('barrier', priority);
};
dfpRefresh.getBarrierPriority = function () {
return dfpRefresh.getPriority('barrier');
};
dfpRefresh.setIntervalPriority = function (priority) {
ensureValidPriority(priority);
dfpRefresh.setPriority('interval', priority);
};
dfpRefresh.getIntervalPriority = function () {
return dfpRefresh.getPriority('interval');
};
function ensureValidOption(option) {
if (!(option in Options)) {
throw new DFPRefreshError('Invalid option \'' + option + '\'');
}
}
function ensureValidPriority(priority) {
if (typeof priority !== 'number') {
throw new DFPRefreshError('Priority \'' + priority + '\' is not a number');
}
}
function enable(option, yes) {
if (yes === false) {
disable(option);
return;
}
switch (option) {
case Options.REFRESH:
enableRefreshInterval();break;
case Options.INTERVAL:
enableBufferInterval();break;
case Options.BARRIER:
enableBufferBarrier();break;
default:
console.assert(false);
}
}
function disable(option) {
switch (option) {
case Options.REFRESH:
disableRefreshInterval();break;
case Options.INTERVAL:
disableBufferInterval();break;
case Options.BARRIER:
disableBufferBarrier();break;
default:
console.assert(false);
}
}
function prioritize() {
var options = Object.keys(Options).map(function (key) {
return Options[key];
});
var available = options.filter(dfpRefresh.has);
var priorities = available.map(function (option) {
return self.priority[option];
});
var maximum = null;
if (priorities.length > 0) {
maximum = priorities.reduce(function (a, b) {
return Math.max(a, b);
});
}
for (var index = 0; index < available.length; ++index) {
if (priorities[index] === maximum) {
enable(available[index]);
} else {
disable(available[index]);
}
}
}
function refresh(tasks) {
console.assert(tasks === undefined || tasks !== null);
if (tasks === undefined) {
googletag.cmd.push(function () {
googletag.pubads().refresh();
});
return;
}
if (tasks.length === 0) return;
tasks = tasks.filter(function (pair) {
return pair !== null;
});
googletag.cmd.push(function () {
googletag.pubads().refresh(tasks.map(function (task) {
return task.slot;
}));
tasks.forEach(function (task) {
return task.deferred.resolve();
});
});
}
function flushBuffer() {
refresh(buffer);
buffer = [];
}
function enableRefreshInterval() {
console.assert(dfpRefresh.hasRefreshInterval());
var task = function task() {
clearBufferRespectingBarrier();
refresh();
};
var promise = $interval(task, self.refreshInterval);
intervals.refresh = promise;
isEnabled.refresh = true;
}
function disableRefreshInterval() {
if (isEnabled.refresh) {
$interval.cancel(intervals.refresh);
intervals.refresh = null;
isEnabled.refresh = false;
}
}
function enableBufferInterval() {
console.assert(dfpRefresh.hasBufferInterval());
var task = function task() {
refresh(buffer);
clearBufferRespectingBarrier();
};
var promise = $interval(task, self.bufferInterval);
intervals.buffer = promise;
isEnabled.interval = true;
}
function disableBufferInterval() {
if (isEnabled.interval) {
$interval.cancel(intervals.buffer);
intervals.buffer = null;
isEnabled.interval = false;
}
}
function enableBufferBarrier() {
console.assert(dfpRefresh.hasBufferBarrier());
isEnabled.barrier = true;
}
function disableBufferBarrier() {
isEnabled.barrier = false;
}
function clearBufferRespectingBarrier() {
if (isEnabled.barrier) {
for (var i = 0; i < buffer.length; ++i) {
buffer[i] = null;
}
} else {
buffer = [];
}
}
function addSlotInterval(task, interval) {
var parsedInterval = parseDuration(interval);
validateInterval(parsedInterval, interval);
var promise = $interval(function () {
scheduleRefresh(task);
}, parsedInterval);
intervals[task.slot] = promise;
}
function scheduleRefresh(task) {
if (dfpRefresh.isBuffering()) {
bufferRefresh(task);
} else {
refresh([task]);
}
}
function bufferRefresh(task) {
buffer.push(task);
if (!isEnabled[Options.BARRIER]) return;
if (buffer.length === self.bufferBarrier) {
flushBuffer();
if (self.oneShotBarrier) {
dfpRefresh.clearBufferBarrier();
}
}
}
function validateInterval(milliseconds, beforeParsing) {
console.assert(milliseconds);
if (milliseconds < 500) {
$log.warn('Careful: ${beforeParsing} is quite a low interval!');
}
}
$rootScope.$on('$destroy', function () {
intervals.forEach(function (promise) {
$interval.cancel(promise);
});
});
if (self.refreshInterval) {
self.refreshInterval = parseDuration(self.refreshInterval);
}
if (self.bufferInterval) {
self.bufferInterval = parseDuration(self.bufferInterval);
}
prioritize();
return dfpRefresh;
}];
}
module.provider('dfpRefresh', [dfpRefreshProvider]);
})(angularDfp);
( function (module) {
'use strict';
function dfpResponsiveResizeProvider() {
var self = this;
self.refreshDelay = 200;
this.$get = ['$interval', '$timeout', '$window', 'dfpRefresh', 'parseDuration', function ($interval, $timeout, $window, dfpRefresh, parseDuration) {
$window = angular.element($window);
self.refreshDelay = parseDuration(self.refreshDelay);
function responsiveResize(element, slot, dimensions) {
dimensions.sort(function (first, second) {
if (first.width < second.width) return -1;
if (first.width > second.width) return +1;
if (first.height < second.height) return -1;
if (first.height > second.height) return +1;
return 0;
});
var POLL_INTERVAL = 100;
var POLL_DURATION = 2500;
function queryIFrame() {
return element.find('div iframe');
}
function normalizeIFrame(iframe) {
iframe = iframe || queryIFrame();
iframe.css('width', iframe.attr('width') + 'px');
iframe.css('height', iframe.attr('height') + 'px');
}
function pollForChange(initial) {
var iframe = queryIFrame();
var change = ['width', 'height'].some(function (dimension) {
return iframe.attr(dimension) !== initial[dimension];
});
if (change) {
normalizeIFrame(iframe);
element.parent().removeClass('refreshing');
}
}
function startPolling(initial) {
var poll = $interval(function () {
return pollForChange(initial);
}, POLL_INTERVAL);
$timeout(function () {
return $interval.cancel(poll);
}, POLL_DURATION);
}
function getIframeDimensions() {
var iframe = queryIFrame();
var dimensions = [iframe.css('width'), iframe.css('height')];
var plain = dimensions.map(function (dimension) {
return dimension ? dimension.slice(0, -2) : null;
});
return { width: plain[0], height: plain[1] };
}
function watchResize() {
startPolling(getIframeDimensions());
$window.on('resize', function () {
normalizeIFrame();
});
}
function makeResponsive() {
function determineIndex() {
var width = window.innerWidth;
var height = window.innerHeight;
var numberOfDimensions = dimensions.length;
var index = 1;
for (; index < numberOfDimensions; ++index) {
if (width < dimensions[index].width) break;
if (height < dimensions[index].height) break;
}
return index - 1;
}
var index = determineIndex();
function couldGrow() {
if (index + 1 >= dimensions.length) return false;
if (window.innerWidth < dimensions[index + 1].width) {
return false;
}
if (window.innerHeight < dimensions[index + 1].height) {
return false;
}
return true;
}
function couldShrink() {
if (index === 0) return false;
if (window.innerWidth < dimensions[index].width) return true;
if (window.innerHeight < dimensions[index].height) return true;
return false;
}
function refresh() {
dfpRefresh(slot).then(function () {
watchResize();
});
}
function transition(delta) {
console.assert(index >= 0 && index < dimensions.length);
console.assert(delta === -1 || delta === +1);
index += delta;
element.parent().addClass('refreshing');
$timeout(refresh, self.refreshDelay);
console.assert(index >= 0 && index < dimensions.length);
}
watchResize();
return function watchListener() {
if (couldGrow()) {
transition(+1);
} else if (couldShrink()) {
transition(-1);
}
};
}
$window.on('resize', makeResponsive());
}
return responsiveResize;
}];
}
module.provider('dfpResponsiveResize', dfpResponsiveResizeProvider);
})(angularDfp);
( function (module) {
'use strict';
function DFPResponsiveController(DFPIncompleteError, DFPTypeError) {
var viewportSize = Object.seal([this['viewportWidth'], this['viewportHeight'] || 0]);
var adSizes = [];
this.checkValid = function () {
var _this6 = this;
['viewportWidth', 'viewportHeight'].forEach(function (dimension) {
var value = _this6[dimension];
if (typeof value !== 'number') {
dimension = dimension.replace(/[A-Z]/g, function (m) {
return '-' + m.toLowerCase();
});
throw new DFPTypeError('dfp-responsive', dimension, value, 'number');
}
});
if (adSizes.length === 0) {
throw new DFPIncompleteError('dfp-responsive', 'dfp-size', false);
}
};
this.addSize = function (size) {
adSizes.push(size);
};
this.getState = function () {
this.checkValid();
return Object.freeze({
viewportSize: viewportSize,
adSizes: adSizes
});
};
}
DFPResponsiveController.$inject = ['$scope'];
function dfpResponsiveDirective(scope, element, attributes, ad) {
var mapping = scope.controller.getState();
ad.addResponsiveMapping(mapping);
}
module.directive('dfpResponsive', [function () {
return {
restrict: 'E',
require: '^^dfpAd',
controller: ['DFPIncompleteError', 'DFPTypeError', DFPResponsiveController],
controllerAs: 'controller',
bindToController: true,
link: dfpResponsiveDirective,
scope: { 'viewportWidth': '=', 'viewportHeight': '=' }
};
}]);
})(angularDfp);
( function (module) {
'use strict';
var DFPScriptError = function (_Error6) {
_inherits(DFPScriptError, _Error6);
function DFPScriptError(response) {
_classCallCheck(this, DFPScriptError);
return _possibleConstructorReturn(this, (DFPScriptError.__proto__ || Object.getPrototypeOf(DFPScriptError)).call(this, 'Error (' + response.status + '): could not fetch ' + ('\'dfp-script\' source from \'' + response.config.url + '\'.')));
}
return DFPScriptError;
}(Error);
function dfpScriptDirective(scope, element, attributes, ad, $injector) {
var $http = $injector.get('$http');
var $log = $injector.get('$log');
function addScript(contents) {
var script = '(function(scope, ' + scope['slotAs'] + '){' + contents + '})';
ad.addScript(eval(script).bind(null, scope['scope']));
}
if (scope['src']) {
if (element.html()) {
$log.warn('Ignoring inner HTML of dfp-script ' + 'in favor of src contents.');
}
$http.get(scope['src']).then(function (response) {
addScript(response.data);
}, function (response) {
throw new DFPScriptError(response);
});
} else {
addScript(element.html().trim());
}
}
module.directive('dfpScript', ['$injector', function ($injector) {
return {
restrict: 'E',
require: '^^dfpAd',
scope: { 'slotAs': '@', 'scope': '=', 'src': '@' },
link: function link() {
for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
args[_key3] = arguments[_key3];
}
dfpScriptDirective.apply(null, args.slice(0, 4).concat($injector));
}
};
}]);
})(angularDfp);
( function (module) {
'use strict';
function DFPSizeDirective(scope, element, attributes, parent, DFPMissingParentError) {
parent = parent[1] || parent[0];
if (!parent) {
throw new DFPMissingParentError('dfp-size', 'dfp-ad', 'dfp-responsive');
}
if (scope.width && scope.height) {
parent.addSize([scope.width, scope.height]);
} else {
parent.addSize(element[0].innerHTML);
}
}
module.directive('dfpSize', ['DFPMissingParentError', function (DFPMissingParentError) {
return {
restrict: 'E',
require: ['?^^dfpAd', '?^^dfpResponsive'],
scope: { width: '=', height: '=' },
link: function link() {
for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
args[_key4] = arguments[_key4];
}
args = args.slice(0, 4).concat(DFPMissingParentError);
DFPSizeDirective.apply(null, args);
}
};
}]);
})(angularDfp);
( function (module) {
'use strict';
function dfpTargetingController(DFPIncompleteError) {
var values = this.value ? [this.value] : [];
this.checkValid = function () {
if (this.key === undefined) {
throw new DFPIncompleteError('dfp-targeting', 'key', true);
}
if (values.length === 0) {
throw new DFPIncompleteError('dfp-targeting', 'value', true);
}
};
this.getState = function () {
this.checkValid();
return Object.freeze({
key: this.key,
values: values
});
};
this.addValue = function (value) {
values.push(value);
};
}
function dfpTargetingDirective(scope, element, attributes, ad) {
console.assert(ad !== undefined);
var targeting = scope.controller.getState();
ad.addTargeting(targeting);
}
module.directive('dfpTargeting', [function () {
return {
restrict: 'E',
require: '^^dfpAd',
controller: ['DFPIncompleteError', dfpTargetingController],
controllerAs: 'controller',
bindToController: true,
scope: { key: '@', value: '@' },
link: dfpTargetingDirective
};
}]);
})(angularDfp);
( function (module) {
'use strict';
function dfpValueDirective(scope, element, attributes, parent) {
parent.addValue(element.html());
}
module.directive('dfpValue', [function () {
return {
restrict: 'E',
require: '^^dfpTargeting',
link: dfpValueDirective
};
}]);
})(angularDfp);
var angularDfpVideo = angular.module('angularDfp');
( function (module) {
'use strict';
var DFPVideoError = function (_Error7) {
_inherits(DFPVideoError, _Error7);
function DFPVideoError() {
_classCallCheck(this, DFPVideoError);
return _possibleConstructorReturn(this, (DFPVideoError.__proto__ || Object.getPrototypeOf(DFPVideoError)).apply(this, arguments));
}
return DFPVideoError;
}(Error);
function dfpVideoDirective(scope, element, attributes, $injector) {
var dfpIDGenerator = $injector.get('dfpIDGenerator');
element = element[0];
if (element.tagName !== 'VIDEO') {
throw new DFPVideoError("'dfp-video' directive must be attached to a <video> tag.");
}
dfpIDGenerator(element);
var player = videojs(element.id);
player.ima({ id: element.id, adTagUrl: scope['adTag'] });
player.ima.requestAds();
player.ima.initializeAdDisplayContainer();
}
module.directive('dfpVideo', ['$injector', function ($injector) {
return {
restrict: 'A',
scope: { 'adTag': '@' },
link: function link() {
for (var _len5 = arguments.length, args = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
args[_key5] = arguments[_key5];
}
dfpVideoDirective.apply(null, args.slice(0, 3).concat($injector));
}
};
}]);
return module;
})(angularDfpVideo);
var googletag = googletag || {};
googletag.cmd = googletag.cmd || [];
( function (module) {
'use strict';
var DFPConfigurationError = function (_Error8) {
_inherits(DFPConfigurationError, _Error8);
function DFPConfigurationError() {
_classCallCheck(this, DFPConfigurationError);
return _possibleConstructorReturn(this, (DFPConfigurationError.__proto__ || Object.getPrototypeOf(DFPConfigurationError)).apply(this, arguments));
}
return DFPConfigurationError;
}(Error);
module.constant('GPT_LIBRARY_URL', '//www.googletagservices.com/tag/js/gpt.js');
function dfpProvider(GPT_LIBRARY_URL) {
var self = this;
self.enableVideoAds = true;
self.collapseIfEmpty = true;
self.centering = false;
self.location = null;
self.ppid = null;
self.globalTargeting = null;
self.forceSafeFrame = false;
self.safeFrameConfig = null;
self.loadGPT = true;
var loaded = false;
function addSafeFrameConfig(pubads) {
if (!self.safeFrameConfig) return;
if (_typeof(self.globalTargeting) !== 'object') {
throw new DFPConfigurationError('Targeting must be an object');
}
pubads.setSafeFrameConfig(self.safeFrameConfig);
}
function addTargeting(pubads) {
if (!self.globalTargeting) return;
if (_typeof(self.globalTargeting) !== 'object') {
throw new DFPConfigurationError('Targeting must be an object');
}
for (var key in self.globalTargeting) {
if (self.globalTargeting.hasOwnProperty(key)) {
pubads.setTargeting(key, self.globalTargeting[key]);
}
}
}
function addLocation(pubads) {
if (!self.location) return;
if (typeof self.location === 'string') {
pubads.setLocation(self.location);
return;
}
if (!Array.isArray(self.location)) {
throw new DFPConfigurationError('Location must be an ' + 'array or string');
}
pubads.setLocation.apply(pubads, self.location);
}
function addPPID(pubads) {
if (!self.ppid) return;
if (typeof self.ppid !== 'string') {
throw new DFPConfigurationError('PPID must be a string');
}
pubads.setPublisherProvidedId(self.ppid);
}
this.$get = ['scriptInjector', function (scriptInjector) {
function setup() {
var pubads = googletag.pubads();
if (self.enableVideoAds) {
pubads.enableVideoAds();
}
if (self.collapseIfEmpty) {
pubads.collapseEmptyDivs();
}
pubads.disableInitialLoad();
pubads.setForceSafeFrame(self.forceSafeFrame);
pubads.setCentering(self.centering);
addLocation(pubads);
addPPID(pubads);
addTargeting(pubads);
addSafeFrameConfig(pubads);
googletag.enableServices();
}
function dfp() {
googletag.cmd.push(setup);
if (self.loadGPT) {
scriptInjector(GPT_LIBRARY_URL).then(function () {
loaded = true;
});
}
}
dfp.hasLoaded = function () {
return loaded;
};
dfp.then = function (task) {
googletag.cmd.push(task);
};
return dfp;
}];
}
window.googletag = googletag;
module.provider('dfp', ['GPT_LIBRARY_URL', dfpProvider]);
})(angularDfp);