Source: components/ads.js

if (typeof window.db == 'undefined') window.db = { libs: {} };

(function() {
    'use strict';

    /**
     * Ads.
     * @namespace
     */
    db.libs.ads = (function(){
        
        var name = 'ads';
        
        var ua = window.navigator.userAgent.toLowerCase();

        /**
         * Cached list of all adunits
         * @memberof db.libs.ads
         * @static
         * @type nodelist
         **/
        var adunits;

        /**
         * Cached list of all sticky adunits
         * @memberof db.libs.ads
         * @static
         * @type array
         **/
        var stickies = [];

        /**
         * Cached list of all fullscreen adunits
         * @memberof db.libs.ads
         * @static
         * @type array
         **/
        var fullscreens = [];

        /**
         * Cached list of all scaleable adunits
         * @memberof db.libs.ads
         * @static
         * @type array
         **/
        var scaleable = [];

        /**
         * Set wallpaper. This function can be given as a callback to <code>postMessage</code> from within an <code>iframe</code> to allow ads to set the a wallpaper ad.
         * @public
         * @example <caption>Example using as a callback and postMessage</caption>
         * window.parent.postMessage({ callback: 'db.libs.ads.setWallpaper', src: 'http://…', url: 'http://…', frame: window.name }, 'http://dagbadet.no');
         * @memberof db.libs.ads
         * @param  {object} message
         * @param  {string} message.frame Name of the frame posting the message
         * @param  {string} message.src Url to image to be used as wallpaper
         * @param  {string} message.url Url to trigger when clicked
         * @returns {element}
         */
        var setWallpaper = function(message) {
            var $frame = document.querySelector('iframe[name="' + message.frame + '"]');

            if ($frame === null) {
                console.warn('db.libs.ads.setWallpaper > Adunit iframe missing name attribute.');
                return;
            }

            $frame.parentNode.style.backgroundImage = 'url(' + message.src + ')';

            if (document.querySelector('#adunit-wallpaper') === null) {
                document.querySelector('main').insertAdjacentHTML('beforebegin', '<a id="adunit-wallpaper" target="_blank" href="#"></a>');
            }

            var $wallpaper = document.querySelector('#adunit-wallpaper');
            $wallpaper.style.backgroundImage = 'url(' + message.src + ')';
            $wallpaper.style.top = ($frame.getBoundingClientRect().top + document.body.scrollTop) + 'px';
            $wallpaper.setAttribute('href', message.url);

            return $frame.parentNode.parentNode;
        };

        /**
         * Sets size for adunit.
         * @public
         * @example
         * window.parent.postMessage({ callback: 'db.libs.ads.size', size: 'large-980x300', frame: window.name }, 'http://dagbadet.no');
         * @memberof db.libs.ads
         * @param  {object} message
         * @param  {string} message.frame Name of the frame posting the message
         * @param  {string} message.size Classname refering to the desired size
         */
        var setSize = function(message){
            var $adunit;
            if(message.id){
                $adunit = document.getElementById(message.id);
            } else {
                $adunit = document.querySelector('iframe[name="' + message.frame + '"]').parentNode.parentNode;
            }

            if ($adunit === null) {
                console.warn('db.libs.ads.setSize > Adunit iframe missing name attribute.');
            }

            var size = message.size.match(/small|medium|large/i);
            if (size !== null) {
                var re = new RegExp("(^|\\s)" + size + "-\\S+", "g");
                $adunit.className = $adunit.className.replace(re, ' ' + message.size);
            } else {
                console.warn('db.libs.ads.setSize > Size passed does not match any possible sizes.');
            }
        };

        /**
         * Scale all adunits to fit within given frame.
         * @private
         * @memberof db.libs.ads
         */
        var scale = function() {
            scaleable.forEach(function($adunit, i) {
                var re = new RegExp("(small|medium|large)-([0-9]{3})x([0-9]{3})-scaled", "i");
                var scale = $adunit.className.match(re);
                var rule = [];
                var styleId = $adunit.getAttribute('id') + '-scale';
                if (scale !== null) {
                    if (document.querySelector('#' + styleId) === null) {
                        var s = document.createElement('style');
                        s.id = styleId;
                        s.type = 'text/css';
                        document.getElementsByTagName('head')[0].appendChild(s);
                    }
                    var $style = document.querySelector('#' + styleId);
                    if (scale[1] == 'small') rule.push('@media only screen and (max-width:640px){');
                    if (scale[1] == 'medium') rule.push('@media only screen and (min-width:641px) and (max-width:1024px){');
                    if (scale[1] == 'large') rule.push('@media only screen and (min-width:1024px){');
                    rule.push('#' + $adunit.getAttribute('id') + ' iframe{ transform: scale(' + ((1 / scale[2]) * $adunit.offsetWidth) + '); -webkit-transform: scale(' + ((1 / scale[2]) * $adunit.offsetWidth) + ');}');
                    rule.push('}');
                    $style.textContent = rule.join('');
                }
            });
        };

        /**
         * Updates poisition and state of sticky adunits. This function is bound to window scroll event and throttled.
         * @private
         * @memberof db.libs.ads
         */
        var updateSticky = function() {
            var top = 0;
            if(window.pageYOffset !== undefined){
                top = window.pageYOffset;
            } else {
                top = document.body.scrollTop;
            }
            for (var i = 0; i < stickies.length; i++) {
                if (top > parseInt(stickies[i].getAttribute('data-top')) && !stickies[i].classList.contains('active')) {
                    stickies[i].classList.add('active');
                } else if (top < parseInt(stickies[i].getAttribute('data-top')) && stickies[i].classList.contains('active')) {
                    stickies[i].classList.remove('active');
                }
            }
        };

        /**
         * Updates visibility of fullscreen ads. This function is bound to window scroll event and throttled.
         * @private
         * @memberof db.libs.ads
         */
        var updateFullscreen = function(){
            var top = 0;
            if(window.pageYOffset !== undefined){
                top = window.pageYOffset;
            } else {
                top = document.body.scrollTop;
            }
            for (var i = 0; i < fullscreens.length; i++) {
                if (top > parseInt(fullscreens[i].getAttribute('data-top')) && top < parseInt(fullscreens[i].getAttribute('data-bottom'))) {
                    fullscreens[i].classList.add('active');
                    if(ua.indexOf('edge/') > -1 || ua.indexOf('trident/') > -1){
                        fullscreens[i].querySelector('div').style.perspective = '1px';
                    }
                    if(ua.indexOf('safari') > -1 && ua.indexOf('edge/') == -1 && ua.indexOf('chrome') == -1){
                        fullscreens[i].querySelector('iframe').style.position = 'absolute';
                    }
                    if(ua.indexOf('_app_') > -1 && (ua.indexOf('iphone') > -1 || ua.indexOf('ipad') > -1)){
                        fullscreens[i].querySelector('iframe').style.position = 'absolute';
                    }
                } else {
                    fullscreens[i].classList.remove('active');
                }
            }
        };

        var reflowFullscreen = function(){
            var height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
            for (var f = 0; f < fullscreens.length; f++) {
                fullscreens[f].setAttribute('data-top', parseInt(fullscreens[f].getBoundingClientRect().top + document.body.scrollTop - height));
                fullscreens[f].setAttribute('data-bottom', parseInt(fullscreens[f].getBoundingClientRect().top + document.body.scrollTop + height));
            }
            updateFullscreen();
        };

        /**
         * Initialize the component
         * @public
         * @memberof db.libs.ads
         */
        var init = function(){
            //Check of the ads already have been initialized
            if( !document.querySelector('html').getAttribute('data-ads') ){
                //Set a flag signaling that the ads have been initialized
                document.querySelector('html').setAttribute('data-ads', 'true');

                //Listen for incomming messages from ads.
                window.addEventListener('message', function(event) {
                    //Check if the callback is within the 'db.libs.ads' namespace
                    try {
                        if (event.data.callback.lastIndexOf('db.libs.ads', 0) === -1) return;
                        var callback = event.data.callback.split('.').pop();
                        if (typeof db.libs.ads[callback] === typeof Function) {
                            db.libs.ads[callback](event.data);
                        }
                    } catch (e) {
                        return;
                    }
                }, false);
                
                //Create a list of sticky adunits
                stickies = Array.prototype.filter.call(document.querySelectorAll('.adunit'), function(el){
                    return el.className.match(/sticky-/);
                });

                var top = 0;
                if(window.pageYOffset !== undefined){
                    top = window.pageYOffset;
                } else {
                    top = document.body.scrollTop;
                }
                
                //Set the initial position for all sticky adunits
                for (var i = 0; i < stickies.length; i++) {
                    stickies[i].setAttribute('data-top', parseInt(stickies[i].getBoundingClientRect().top + top));
                }

                //Create a list of sticky fullscreen
                fullscreens = Array.prototype.filter.call(document.querySelectorAll('.adunit'), function(el){
                    if(window.innerWidth <= 640){
                        return el.className.match(/small-fullscreen/);
                    } else if(window.innerWidth > 1024){
                        return el.className.match(/medium-fullscreen/);
                    } else {
                        return el.className.match(/large-fullscreen/);
                    }
                });
                
                //Set the initial position for all fullscreen adunits
                var height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
                for (var f = 0; f < fullscreens.length; f++) {
                    fullscreens[f].setAttribute('data-top', parseInt(fullscreens[f].getBoundingClientRect().top + document.body.scrollTop - height));
                    fullscreens[f].setAttribute('data-bottom', parseInt(fullscreens[f].getBoundingClientRect().top + document.body.scrollTop + height));
                    
                    //If the iOS version is lower than 7 we need to disable the ad.
                    if (/iP(hone|od|ad)/.test(navigator.userAgent)) {
                        var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);
                        var version = [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)];
                        if(version[0] <= 7){
                            fullscreens[f].classList.add('hide');
                            fullscreens[f].setAttribute('data-is-configured', 'true');
                        }
                    }
                    
                    if(ua.indexOf('MSIE ') > -1){
                        fullscreens[f].className += ' hide';
                        fullscreens[f].setAttribute('data-is-configured', 'true');
                    }
                } 
                
                if(stickies.length || fullscreens.length){
                    window.addEventListener('scroll', function(){
                        window.requestAnimationFrame(updateSticky);
                        window.requestAnimationFrame(updateFullscreen);
                    });
                    updateSticky();
                    updateFullscreen();
                }

                if(fullscreens.length){
                    window.addEventListener('resize', function(){
                        window.requestAnimationFrame(reflowFullscreen);
                    });
                }

                //Create a list of all scaleable adunits
                scaleable = Array.prototype.filter.call(document.querySelectorAll('.adunit'), function(el) {
                    return el.className.match(/-scaled/);
                });

                if(scaleable.length){
                    scale();
                }
            }
        };

        return {
            init: init,
            reflow: function() {},
            setWallpaper: setWallpaper,
            setSize: setSize
        };

    })();
})();